BsFMODAudioClip.cpp 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
  2. //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
  3. #include "BsFMODAudioClip.h"
  4. #include "BsFMODAudio.h"
  5. #include "FileSystem/BsDataStream.h"
  6. namespace bs
  7. {
  8. FMOD_RESULT F_CALLBACK pcmReadCallback(FMOD_SOUND* sound, void *data, unsigned int dataLen)
  9. {
  10. FMODOggDecompressorData* decompressor = nullptr;
  11. ((FMOD::Sound*)sound)->getUserData((void**)&decompressor);
  12. const FMODAudioClip* clip = decompressor->clip;
  13. UINT32 bytesPerSample = (clip->getBitDepth() / 8);
  14. assert(dataLen % bytesPerSample == 0);
  15. UINT32 numSamples = dataLen / bytesPerSample;
  16. decompressor->vorbisReader.seek(decompressor->readPos);
  17. UINT32 readSamples = decompressor->vorbisReader.read((UINT8*)data, numSamples);
  18. while(readSamples < numSamples) // Looping
  19. {
  20. decompressor->vorbisReader.seek(0);
  21. UINT8* writePtr = (UINT8*)data;
  22. writePtr += readSamples * bytesPerSample;
  23. readSamples += decompressor->vorbisReader.read(writePtr, numSamples - readSamples);
  24. }
  25. assert(readSamples == numSamples);
  26. decompressor->readPos += readSamples;
  27. decompressor->readPos %= clip->getNumSamples();
  28. return FMOD_OK;
  29. }
  30. FMOD_RESULT F_CALLBACK pcmSetPosCallback(FMOD_SOUND* sound, int subsound, unsigned int position, FMOD_TIMEUNIT posType)
  31. {
  32. FMODOggDecompressorData* decompressor = nullptr;
  33. ((FMOD::Sound*)sound)->getUserData((void**)&decompressor);
  34. const FMODAudioClip* clip = decompressor->clip;
  35. UINT32 bytesPerSample = (clip->getBitDepth() / 8);
  36. switch(posType)
  37. {
  38. case FMOD_TIMEUNIT_MS:
  39. decompressor->readPos = (UINT32)((clip->getFrequency() * clip->getNumChannels()) * (position / 1000.0f));
  40. break;
  41. case FMOD_TIMEUNIT_PCM:
  42. decompressor->readPos = clip->getNumChannels() * position;
  43. break;
  44. case FMOD_TIMEUNIT_PCMBYTES:
  45. assert(position % bytesPerSample == 0);
  46. decompressor->readPos = position / bytesPerSample;
  47. break;
  48. default:
  49. LOGERR("Invalid time unit.");
  50. break;
  51. }
  52. decompressor->readPos %= clip->getNumSamples();
  53. decompressor->vorbisReader.seek(decompressor->readPos);
  54. return FMOD_OK;
  55. }
  56. FMODAudioClip::FMODAudioClip(const SPtr<DataStream>& samples, UINT32 streamSize, UINT32 numSamples, const AUDIO_CLIP_DESC& desc)
  57. :AudioClip(samples, streamSize, numSamples, desc), mSound(nullptr), mSourceStreamSize(0)
  58. { }
  59. FMODAudioClip::~FMODAudioClip()
  60. {
  61. if(mSound != nullptr)
  62. mSound->release();
  63. }
  64. void FMODAudioClip::initialize()
  65. {
  66. AudioDataInfo info;
  67. info.bitDepth = mDesc.bitDepth;
  68. info.numChannels = mDesc.numChannels;
  69. info.numSamples = mNumSamples;
  70. info.sampleRate = mDesc.frequency;
  71. // If we need to keep source data, read everything into memory and keep a copy
  72. if (mKeepSourceData)
  73. {
  74. mStreamData->seek(mStreamOffset);
  75. UINT8* sampleBuffer = (UINT8*)bs_alloc(mStreamSize);
  76. mStreamData->read(sampleBuffer, mStreamSize);
  77. mSourceStreamData = bs_shared_ptr_new<MemoryDataStream>(sampleBuffer, mStreamSize);
  78. mSourceStreamSize = mStreamSize;
  79. }
  80. // If streaming is not required, create the sound right away
  81. if(!requiresStreaming())
  82. {
  83. SPtr<DataStream> stream;
  84. UINT32 offset = 0;
  85. if (mSourceStreamData != nullptr) // If it's already loaded in memory, use it directly
  86. stream = mSourceStreamData;
  87. else
  88. {
  89. stream = mStreamData;
  90. offset = mStreamOffset;
  91. }
  92. UINT32 bufferSize = info.numSamples * (info.bitDepth / 8);
  93. UINT8* sampleBuffer = (UINT8*)bs_stack_alloc(bufferSize);
  94. FMOD_CREATESOUNDEXINFO exInfo;
  95. memset(&exInfo, 0, sizeof(exInfo));
  96. exInfo.cbsize = sizeof(exInfo);
  97. exInfo.length = bufferSize;
  98. FMOD_MODE flags = FMOD_CREATESAMPLE | FMOD_OPENMEMORY;
  99. if (is3D())
  100. flags |= FMOD_3D;
  101. else
  102. flags |= FMOD_2D;
  103. if (mDesc.format == AudioFormat::PCM)
  104. {
  105. flags |= FMOD_OPENRAW;
  106. switch (mDesc.bitDepth)
  107. {
  108. case 8:
  109. exInfo.format = FMOD_SOUND_FORMAT_PCM8;
  110. break;
  111. case 16:
  112. exInfo.format = FMOD_SOUND_FORMAT_PCM16;
  113. break;
  114. case 24:
  115. exInfo.format = FMOD_SOUND_FORMAT_PCM24;
  116. break;
  117. case 32:
  118. exInfo.format = FMOD_SOUND_FORMAT_PCM32;
  119. break;
  120. default:
  121. assert(false);
  122. break;
  123. }
  124. exInfo.numchannels = mDesc.numChannels;
  125. exInfo.defaultfrequency = mDesc.frequency;
  126. }
  127. sampleBuffer = (UINT8*)bs_stack_alloc(bufferSize);
  128. stream->seek(offset);
  129. stream->read(sampleBuffer, bufferSize);
  130. FMOD::System* fmod = gFMODAudio()._getFMOD();
  131. if (fmod->createSound((const char*)sampleBuffer, flags, &exInfo, &mSound) != FMOD_OK)
  132. {
  133. LOGERR("Failed creating sound.");
  134. }
  135. else
  136. {
  137. mSound->setMode(FMOD_LOOP_OFF);
  138. }
  139. mStreamData = nullptr;
  140. mStreamOffset = 0;
  141. mStreamSize = 0;
  142. bs_stack_free(sampleBuffer);
  143. }
  144. else // Streaming
  145. {
  146. // If reading from file, make a copy of data in memory, otherwise just take ownership of the existing buffer
  147. if(mDesc.readMode == AudioReadMode::LoadCompressed && mStreamData->isFile())
  148. {
  149. if (mSourceStreamData != nullptr) // If it's already loaded in memory, use it directly
  150. mStreamData = mSourceStreamData;
  151. else
  152. {
  153. UINT8* data = (UINT8*)bs_alloc(mStreamSize);
  154. mStreamData->seek(mStreamOffset);
  155. mStreamData->read(data, mStreamSize);
  156. mStreamData = bs_shared_ptr_new<MemoryDataStream>(data, mStreamSize);
  157. }
  158. mStreamOffset = 0;
  159. }
  160. }
  161. AudioClip::initialize();
  162. }
  163. FMOD::Sound* FMODAudioClip::createStreamingSound() const
  164. {
  165. if(!requiresStreaming() || mStreamData == nullptr)
  166. {
  167. LOGERR("Invalid audio stream data.");
  168. return nullptr;
  169. }
  170. FMOD_MODE flags = FMOD_CREATESTREAM;
  171. const char* streamData;
  172. FMOD_CREATESOUNDEXINFO exInfo;
  173. memset(&exInfo, 0, sizeof(exInfo));
  174. exInfo.cbsize = sizeof(exInfo);
  175. String pathStr;
  176. if (mStreamData->isFile())
  177. {
  178. // initialize() guarantees the data was loaded in memory if it's not streaming
  179. assert(mDesc.readMode == AudioReadMode::Stream);
  180. exInfo.length = mStreamSize;
  181. exInfo.fileoffset = mStreamOffset;
  182. SPtr<FileDataStream> fileStream = std::static_pointer_cast<FileDataStream>(mStreamData);
  183. pathStr = fileStream->getPath().toString();
  184. streamData = pathStr.c_str();
  185. }
  186. else
  187. {
  188. SPtr<MemoryDataStream> memStream = std::static_pointer_cast<MemoryDataStream>(mStreamData);
  189. if (mDesc.readMode == AudioReadMode::Stream)
  190. {
  191. // Note: I could use FMOD_OPENMEMORY_POINT here to save on memory, but then the caller would need to make
  192. // sure the memory is not deallocated. I'm ignoring this for now as streaming from memory should be a rare
  193. // occurence (normally only in editor)
  194. flags |= FMOD_OPENMEMORY;
  195. memStream->seek(mStreamOffset);
  196. streamData = (const char*)memStream->getCurrentPtr();
  197. exInfo.length = mStreamSize;
  198. }
  199. else // Load compressed
  200. {
  201. flags |= FMOD_OPENUSER;
  202. exInfo.decodebuffersize = mDesc.frequency;
  203. exInfo.pcmreadcallback = pcmReadCallback;
  204. exInfo.pcmsetposcallback = pcmSetPosCallback;
  205. AudioDataInfo info;
  206. info.bitDepth = mDesc.bitDepth;
  207. info.numChannels = mDesc.numChannels;
  208. info.numSamples = mNumSamples;
  209. info.sampleRate = mDesc.frequency;
  210. FMODOggDecompressorData* decompressorData = bs_new<FMODOggDecompressorData>();
  211. decompressorData->clip = this;
  212. if (!decompressorData->vorbisReader.open(memStream, info, mStreamOffset))
  213. {
  214. LOGERR("Failed decompressing AudioClip stream.");
  215. return nullptr;
  216. }
  217. exInfo.userdata = decompressorData;
  218. exInfo.length = mNumSamples * (mDesc.bitDepth / 8);
  219. streamData = nullptr;
  220. }
  221. }
  222. if (is3D())
  223. flags |= FMOD_3D;
  224. else
  225. flags |= FMOD_2D;
  226. if (mDesc.format == AudioFormat::PCM || mDesc.readMode == AudioReadMode::LoadCompressed)
  227. {
  228. switch (mDesc.bitDepth)
  229. {
  230. case 8:
  231. exInfo.format = FMOD_SOUND_FORMAT_PCM8;
  232. break;
  233. case 16:
  234. exInfo.format = FMOD_SOUND_FORMAT_PCM16;
  235. break;
  236. case 24:
  237. exInfo.format = FMOD_SOUND_FORMAT_PCM24;
  238. break;
  239. case 32:
  240. exInfo.format = FMOD_SOUND_FORMAT_PCM32;
  241. break;
  242. default:
  243. assert(false);
  244. break;
  245. }
  246. exInfo.numchannels = mDesc.numChannels;
  247. exInfo.defaultfrequency = mDesc.frequency;
  248. if(mDesc.readMode != AudioReadMode::LoadCompressed)
  249. flags |= FMOD_OPENRAW;
  250. }
  251. FMOD::Sound* sound = nullptr;
  252. FMOD::System* fmod = gFMODAudio()._getFMOD();
  253. if (fmod->createSound(streamData, flags, &exInfo, &sound) != FMOD_OK)
  254. {
  255. LOGERR("Failed creating a streaming sound.");
  256. return nullptr;
  257. }
  258. sound->setMode(FMOD_LOOP_OFF);
  259. return sound;
  260. }
  261. void FMODAudioClip::releaseStreamingSound(FMOD::Sound* sound)
  262. {
  263. FMODOggDecompressorData* decompressorData = nullptr;
  264. ((FMOD::Sound*)sound)->getUserData((void**)&decompressorData);
  265. if (decompressorData != nullptr)
  266. bs_delete(decompressorData);
  267. sound->release();
  268. }
  269. SPtr<DataStream> FMODAudioClip::getSourceStream(UINT32& size)
  270. {
  271. size = mSourceStreamSize;
  272. mSourceStreamData->seek(0);
  273. return mSourceStreamData;
  274. }
  275. bool FMODAudioClip::requiresStreaming() const
  276. {
  277. return mDesc.readMode == AudioReadMode::Stream ||
  278. (mDesc.readMode == AudioReadMode::LoadCompressed && mDesc.format == AudioFormat::VORBIS);
  279. }
  280. }