sfxFMODBuffer.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) 2012 GarageGames, LLC
  3. //
  4. // Permission is hereby granted, free of charge, to any person obtaining a copy
  5. // of this software and associated documentation files (the "Software"), to
  6. // deal in the Software without restriction, including without limitation the
  7. // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  8. // sell copies of the Software, and to permit persons to whom the Software is
  9. // furnished to do so, subject to the following conditions:
  10. //
  11. // The above copyright notice and this permission notice shall be included in
  12. // all copies or substantial portions of the Software.
  13. //
  14. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  20. // IN THE SOFTWARE.
  21. //-----------------------------------------------------------------------------
  22. #include "sfx/fmod/sfxFMODBuffer.h"
  23. #include "sfx/fmod/sfxFMODDevice.h"
  24. #include "sfx/sfxDescription.h"
  25. #include "core/util/safeDelete.h"
  26. #include "core/volume.h"
  27. //-----------------------------------------------------------------------------
  28. static const char* sExtensions[] =
  29. {
  30. "", // First try without doing anything with the given path.
  31. "", // Then try it without an extension but by expanding it through Torque::FS.
  32. "aiff",
  33. "asf",
  34. "asx",
  35. "dls",
  36. "flac",
  37. "fsb",
  38. "it",
  39. "m3u",
  40. "mid",
  41. "mod",
  42. "mp2",
  43. "mp3",
  44. "ogg",
  45. "pls",
  46. "s3m",
  47. "vag",
  48. "wav",
  49. "wax",
  50. "wma",
  51. "xm",
  52. #ifdef TORQUE_OS_XENON
  53. ".xma",
  54. #endif
  55. NULL
  56. };
  57. //-----------------------------------------------------------------------------
  58. SFXFMODBuffer* SFXFMODBuffer::create( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description )
  59. {
  60. SFXFMODBuffer *buffer = new SFXFMODBuffer( stream, description );
  61. if( !buffer->mSound )
  62. SAFE_DELETE( buffer );
  63. return buffer;
  64. }
  65. //-----------------------------------------------------------------------------
  66. SFXFMODBuffer* SFXFMODBuffer::create( const String& filename, SFXDescription* description )
  67. {
  68. if( Con::getBoolVariable( "$pref::SFX::FMOD::noCustomFileLoading", false ) )
  69. return NULL;
  70. SFXFMODBuffer *buffer = new SFXFMODBuffer( filename, description );
  71. if( !buffer->mSound )
  72. SAFE_DELETE( buffer );
  73. return buffer;
  74. }
  75. //-----------------------------------------------------------------------------
  76. SFXFMODBuffer::SFXFMODBuffer( const String& filename, SFXDescription* description )
  77. : Parent( description ),
  78. mSound( NULL )
  79. {
  80. FMOD_MODE fMode = ( description->mUseHardware ? FMOD_HARDWARE : FMOD_SOFTWARE )
  81. | ( description->mIs3D ? FMOD_3D : FMOD_2D );
  82. if( description->mIsStreaming )
  83. {
  84. fMode |= FMOD_CREATESTREAM;
  85. mIsUnique = true;
  86. }
  87. // Go through the extensions and try each with the given path. The
  88. // first two are special. First we try without touching the filename at all
  89. // so FMOD gets a chance to handle URLs and whatever, and then second we
  90. // try by expanding the path but without adding an extension.
  91. Torque::Path path = filename;
  92. for( U32 i = 0; sExtensions[ i ]; ++ i )
  93. {
  94. path.setExtension( sExtensions[ i ] );
  95. if( !i || Torque::FS::IsFile( path ) )
  96. {
  97. // Translate to full path.
  98. //TODO: Remove this when hooking up the file system functions in sfxFMODDevice.cpp
  99. String fullPath;
  100. if( !i )
  101. fullPath = filename;
  102. else
  103. {
  104. Torque::Path realPath;
  105. if( !Torque::FS::GetFSPath( path, realPath ) )
  106. continue;
  107. fullPath = realPath.getFullPath().c_str();
  108. }
  109. mSound = NULL;
  110. FMOD_RESULT result = SFXFMODDevice::smFunc->FMOD_System_CreateSound(
  111. SFXFMODDevice::smSystem,
  112. fullPath.c_str(),
  113. fMode,
  114. ( FMOD_CREATESOUNDEXINFO* ) NULL,
  115. &mSound );
  116. if( result == FMOD_OK )
  117. {
  118. SFXFMODDevice::smFunc->FMOD_Sound_GetMode( mSound, &mMode );
  119. // Read out format.
  120. int numChannels;
  121. int bitsPerSample;
  122. unsigned int length;
  123. float frequency;
  124. SFXFMODDevice::smFunc->FMOD_Sound_GetFormat( mSound, ( FMOD_SOUND_TYPE* ) NULL, ( FMOD_SOUND_FORMAT* ) NULL, &numChannels, &bitsPerSample );
  125. SFXFMODDevice::smFunc->FMOD_Sound_GetLength( mSound, &length, FMOD_TIMEUNIT_MS );
  126. SFXFMODDevice::smFunc->FMOD_Sound_GetDefaults( mSound, &frequency, ( float* ) NULL, ( float* ) NULL, ( int* ) NULL );
  127. mDuration = length;
  128. mFormat = SFXFormat( numChannels, numChannels * bitsPerSample, frequency );
  129. break;
  130. }
  131. }
  132. }
  133. if( !mSound )
  134. Con::errorf( "SFXFMODBuffer::SFXFMODBuffer - failed to load '%s' through FMOD", filename.c_str() );
  135. }
  136. //-----------------------------------------------------------------------------
  137. SFXFMODBuffer::SFXFMODBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description )
  138. : Parent( stream, description ),
  139. mSound( NULL )
  140. {
  141. FMOD_MODE fMode = ( description->mUseHardware ? FMOD_HARDWARE : FMOD_SOFTWARE )
  142. | ( description->mIs3D ? FMOD_3D : FMOD_2D );
  143. FMOD_CREATESOUNDEXINFO* pCreatesoundexinfo = NULL;
  144. FMOD_CREATESOUNDEXINFO createsoundexinfo;
  145. fMode |= FMOD_OPENUSER; // this tells fmod we are supplying the data directly
  146. if( isStreaming() )
  147. fMode |= FMOD_LOOP_NORMAL | FMOD_UNIQUE;
  148. const SFXFormat& format = getFormat();
  149. U32 channels = format.getChannels();
  150. U32 frequency = format.getSamplesPerSecond();
  151. U32 bitsPerChannel = format.getBitsPerSample() / channels;
  152. U32 dataSize = mBufferSize;
  153. FMOD_SOUND_FORMAT sfxFmt = FMOD_SOUND_FORMAT_NONE;
  154. switch(bitsPerChannel)
  155. {
  156. case 8:
  157. sfxFmt = FMOD_SOUND_FORMAT_PCM8;
  158. break;
  159. case 16:
  160. sfxFmt = FMOD_SOUND_FORMAT_PCM16;
  161. break;
  162. case 24:
  163. sfxFmt = FMOD_SOUND_FORMAT_PCM24;
  164. break;
  165. case 32:
  166. sfxFmt = FMOD_SOUND_FORMAT_PCM32;
  167. break;
  168. default:
  169. AssertISV(false, "SFXFMODBuffer::SFXFMODBuffer() - unsupported bits-per-sample (what format is it in, 15bit PCM?)");
  170. break;
  171. }
  172. dMemset(&createsoundexinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
  173. createsoundexinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); /* required. */
  174. createsoundexinfo.decodebuffersize = frequency; /* Chunk size of stream update in samples. This will be the amount of data passed to the user callback. */
  175. createsoundexinfo.length = dataSize; /* Length of PCM data in bytes of whole sound (for Sound::getLength) */
  176. createsoundexinfo.numchannels = channels; /* Number of channels in the sound. */
  177. createsoundexinfo.defaultfrequency = frequency; /* Default playback rate of sound. */
  178. createsoundexinfo.format = sfxFmt; /* Data format of sound. */
  179. createsoundexinfo.pcmreadcallback = NULL; /* User callback for reading. */
  180. createsoundexinfo.pcmsetposcallback = NULL; /* User callback for seeking. */
  181. pCreatesoundexinfo = &createsoundexinfo;
  182. FMOD_RESULT result = SFXFMODDevice::smFunc->FMOD_System_CreateSound(
  183. SFXFMODDevice::smSystem,
  184. ( const char* ) NULL,
  185. fMode,
  186. pCreatesoundexinfo,
  187. &mSound );
  188. if( result != FMOD_OK )
  189. {
  190. mSound = NULL;
  191. Con::errorf( "SFXFMODBuffer::SFXFMODBuffer - failed to create buffer (%i)", result );
  192. }
  193. else
  194. SFXFMODDevice::smFunc->FMOD_Sound_GetMode( mSound, &mMode );
  195. }
  196. //-----------------------------------------------------------------------------
  197. SFXFMODBuffer::~SFXFMODBuffer()
  198. {
  199. if( mSound )
  200. FModAssert( SFXFMODDevice::smFunc->FMOD_Sound_Release( mSound ),
  201. "SFXFMODBuffer::~SFXFMODBuffer - Failed to release a sound!" );
  202. mSound = NULL;
  203. }
  204. //-----------------------------------------------------------------------------
  205. void SFXFMODBuffer::_flush()
  206. {
  207. AssertFatal( isStreaming(), "SFXFMODBuffer::_flush() - not a streaming buffer" );
  208. AssertFatal( SFXInternal::isSFXThread(), "SFXFMODBuffer::_flush() - not on SFX thread" );
  209. Parent::_flush();
  210. SFXFMODDevice::smFunc->FMOD_Channel_SetPosition
  211. ( ( ( SFXFMODVoice* ) mUniqueVoice.getPointer() )->mChannel, 0, FMOD_TIMEUNIT_PCM );
  212. }
  213. //-----------------------------------------------------------------------------
  214. bool SFXFMODBuffer::_copyData( U32 offset, const U8* data, U32 length )
  215. {
  216. AssertFatal( data != NULL && length > 0, "Must have data!" );
  217. // Fill the buffer with the resource data.
  218. void* lpvWrite;
  219. U32 dwLength;
  220. void* lpvWrite2;
  221. U32 dwLength2;
  222. int res = SFXFMODDevice::smFunc->FMOD_Sound_Lock(
  223. mSound,
  224. offset, // Offset at which to start lock.
  225. length, // Size of lock.
  226. &lpvWrite, // Gets address of first part of lock.
  227. &lpvWrite2, // Address of wraparound not needed.
  228. &dwLength, // Gets size of first part of lock.
  229. &dwLength2 // Size of wraparound not needed.
  230. );
  231. if ( res != FMOD_OK )
  232. {
  233. // You can remove this if it gets spammy. However since we can
  234. // safely fail in this case it doesn't seem right to assert...
  235. // at the same time it can be very annoying not to know why
  236. // an upload fails!
  237. Con::errorf("SFXFMODBuffer::_copyData - failed to lock a sound buffer! (%d)", this);
  238. return false;
  239. }
  240. // Copy the first part.
  241. dMemcpy( lpvWrite, data, dwLength );
  242. // Do we have a wrap?
  243. if ( lpvWrite2 )
  244. dMemcpy( lpvWrite2, data + dwLength, dwLength2 );
  245. // And finally, unlock.
  246. FModAssert( SFXFMODDevice::smFunc->FMOD_Sound_Unlock(
  247. mSound,
  248. lpvWrite, // Address of lock start.
  249. lpvWrite2, // No wraparound portion.
  250. dwLength, // Size of lock.
  251. dwLength2 ), // No wraparound size.
  252. "Failed to unlock sound buffer!" );
  253. return true;
  254. }
  255. //-----------------------------------------------------------------------------
  256. U32 SFXFMODBuffer::getMemoryUsed() const
  257. {
  258. unsigned int memoryUsed;
  259. SFXFMODDevice::smFunc->FMOD_Sound_GetMemoryInfo(
  260. mSound,
  261. FMOD_MEMBITS_ALL,
  262. FMOD_EVENT_MEMBITS_ALL,
  263. &memoryUsed,
  264. ( unsigned int* ) NULL );
  265. return memoryUsed;
  266. }