123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- //-----------------------------------------------------------------------------
- // Copyright (c) 2012 GarageGames, LLC
- //
- // Permission is hereby granted, free of charge, to any person obtaining a copy
- // of this software and associated documentation files (the "Software"), to
- // deal in the Software without restriction, including without limitation the
- // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
- // sell copies of the Software, and to permit persons to whom the Software is
- // furnished to do so, subject to the following conditions:
- //
- // The above copyright notice and this permission notice shall be included in
- // all copies or substantial portions of the Software.
- //
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
- // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
- // IN THE SOFTWARE.
- //-----------------------------------------------------------------------------
- #include "platform/platform.h"
- #include "platform/threads/mutex.h"
- #include "sfx/fmod/sfxFMODDevice.h"
- #include "sfx/fmod/sfxFMODBuffer.h"
- #include "sfx/sfxSystem.h"
- #include "platform/async/asyncUpdate.h"
- #include "console/consoleTypes.h"
- #include "core/volume.h"
- bool SFXFMODDevice::smPrefDisableSoftware = false;
- bool SFXFMODDevice::smPrefUseSoftwareOcclusion = true;
- bool SFXFMODDevice::smPrefUseSoftwareHRTF = true;
- bool SFXFMODDevice::smPrefEnableProfile = false;
- bool SFXFMODDevice::smPrefGeometryUseClosest = false;
- const char* SFXFMODDevice::smPrefDSoundHRTF = "full";
- const char* SFXFMODDevice::smPrefPluginPath = "";
- U32 SFXFMODDevice::smStatMemUsageCore;
- U32 SFXFMODDevice::smStatMemUsageEvents;
- U32 SFXFMODDevice::smStatNumEventSources;
- SFXFMODDevice* SFXFMODDevice::smInstance;
- FMOD_SYSTEM* SFXFMODDevice::smSystem;
- FMOD_EVENTSYSTEM* SFXFMODDevice::smEventSystem;
- FModFNTable* SFXFMODDevice::smFunc;
- Mutex* FModFNTable::mutex;
- //-----------------------------------------------------------------------------
- String FMODResultToString( FMOD_RESULT result )
- {
- switch( result )
- {
- #define FMOD_ERROR( n ) case n: return #n;
- #include "fmodErrors.h"
- #undef FMOD_ERROR
-
- default:
- break;
- }
-
- return String();
- }
- //------------------------------------------------------------------------------
- // FMOD filesystem wrappers.
- //FIXME: these are not thread-safe and cannot be used as such
- FMOD_RESULT F_CALLBACK fmodFileOpenCallback( const char* name, int unicode, unsigned int* filesize, void** handle, void** userdata )
- {
- String fileName;
- if( unicode )
- fileName = String( ( UTF16* ) name );
- else
- fileName = String( name );
-
- Torque::FS::FileRef file = Torque::FS::OpenFile( fileName, Torque::FS::File::Read );
- if( !file )
- return FMOD_ERR_FILE_NOTFOUND;
- else if( file->getStatus() != Torque::FS::File::Open )
- return FMOD_ERR_FILE_BAD;
-
- // Add a reference so we can pass it into FMOD.
- file->incRefCount();
-
- *filesize = U32( file->getSize() );
- *handle = file.getPointer();
- return FMOD_OK;
- }
- FMOD_RESULT F_CALLBACK fmodFileCloseCallback( void* handle, void* userdata )
- {
- Torque::FS::File* file = reinterpret_cast< Torque::FS::File* >( handle );
- file->decRefCount();
- return FMOD_OK;
- }
- FMOD_RESULT F_CALLBACK fmodFileReadCallback( void* handle, void* buffer, unsigned int sizebytes, unsigned int* bytesread, void* userdata )
- {
- Torque::FS::File* file = reinterpret_cast< Torque::FS::File* >( handle );
- U32 numRead = file->read( buffer, sizebytes );
- *bytesread = numRead;
-
- if( file->getStatus() == Torque::FS::File::EndOfFile )
- return FMOD_ERR_FILE_EOF;
- else if( file->getStatus() != Torque::FS::File::Open )
- return FMOD_ERR_FILE_BAD;
-
- return FMOD_OK;
- }
- FMOD_RESULT F_CALLBACK fmodFileSeekCallback( void* handle, unsigned int pos, void* userdata )
- {
- Torque::FS::File* file = reinterpret_cast< Torque::FS::File* >( handle );
-
- if( file->setPosition( pos, Torque::FS::File::Begin ) != pos )
- return FMOD_ERR_FILE_COULDNOTSEEK;
-
- return FMOD_OK;
- }
- //-----------------------------------------------------------------------------
- SFXFMODDevice::SFXFMODDevice( SFXProvider* provider,
- FModFNTable *fmodFnTbl,
- int deviceIdx,
- String name )
- : SFXDevice( name, provider, false, 32 ),
- m3drolloffmode( FMOD_3D_INVERSEROLLOFF ),
- mDeviceIndex( deviceIdx )
- {
- // Store off the function pointers for later use.
- smFunc = fmodFnTbl;
- smStatMemUsageCore = 0;
- smStatMemUsageEvents = 0;
- smStatNumEventSources = 0;
-
- // Register our SFXSystem plugin.
-
- SFX->addPlugin( &mPlugin );
-
- smInstance = this;
- }
- //-----------------------------------------------------------------------------
- SFXFMODDevice::~SFXFMODDevice()
- {
- _releaseAllResources();
-
- SFX->removePlugin( &mPlugin );
-
- if( smEventSystem )
- {
- smFunc->FMOD_EventSystem_Release( smEventSystem );
- smEventSystem = NULL;
- smSystem = NULL;
- }
- else
- smFunc->FMOD_System_Close( smSystem );
-
- smInstance = NULL;
- }
- //-----------------------------------------------------------------------------
- bool SFXFMODDevice::_init()
- {
- #define FMOD_CHECK( message ) \
- if( result != FMOD_OK ) \
- { \
- Con::errorf( "SFXFMODDevice::_init() - %s (%s)", \
- message, \
- FMOD_ErrorString( result ) ); \
- return false; \
- }
- AssertISV(smSystem,
- "SFXFMODDevice::_init() - can't init w/o an existing FMOD system handle!");
- FMOD_RESULT result;
-
- // Get some prefs.
-
- if( smPrefPluginPath && smPrefPluginPath[ 0 ] )
- {
- char fullPath[ 4096 ];
- Platform::makeFullPathName( smPrefPluginPath, fullPath, sizeof( fullPath ) );
-
- smFunc->FMOD_System_SetPluginPath( smSystem, fullPath );
- }
- else
- {
- smFunc->FMOD_System_SetPluginPath( smSystem, Platform::getExecutablePath() );
- }
-
- // Initialize everything from fmod.
- FMOD_SPEAKERMODE speakermode;
- FMOD_CAPS caps;
- result = smFunc->FMOD_System_GetDriverCaps(smSystem, 0, &caps, ( int* ) 0, &speakermode);
- FMOD_CHECK( "SFXFMODDevice::init - Failed to get driver caps" );
- result = smFunc->FMOD_System_SetDriver(smSystem, mDeviceIndex);
- FMOD_CHECK( "SFXFMODDevice::init - Failed to set driver" );
- result = smFunc->FMOD_System_SetSpeakerMode(smSystem, speakermode);
- FMOD_CHECK( "SFXFMODDevice::init - Failed to set the user selected speaker mode" );
- if (caps & FMOD_CAPS_HARDWARE_EMULATED) /* The user has the 'Acceleration' slider set to off! This is really bad for latency!. */
- { /* You might want to warn the user about this. */
- result = smFunc->FMOD_System_SetDSPBufferSize(smSystem, 1024, 10);
- FMOD_CHECK( "SFXFMODDevice::init - Failed to set DSP buffer size" );
- }
-
- Con::printf( "\nFMOD Device caps:" );
- #define PRINT_CAP( name ) \
- if( caps & FMOD_CAPS_ ## name ) \
- Con::printf( #name );
-
- PRINT_CAP( HARDWARE );
- PRINT_CAP( HARDWARE_EMULATED );
- PRINT_CAP( OUTPUT_MULTICHANNEL );
- PRINT_CAP( OUTPUT_FORMAT_PCM8 );
- PRINT_CAP( OUTPUT_FORMAT_PCM16 );
- PRINT_CAP( OUTPUT_FORMAT_PCM24 );
- PRINT_CAP( OUTPUT_FORMAT_PCM32 );
- PRINT_CAP( OUTPUT_FORMAT_PCMFLOAT );
- PRINT_CAP( REVERB_LIMITED );
-
- Con::printf( "" );
-
- bool tryAgain;
- do
- {
- tryAgain = false;
-
- FMOD_INITFLAGS flags = FMOD_INIT_NORMAL | FMOD_INIT_VOL0_BECOMES_VIRTUAL;
-
- if( smPrefDisableSoftware )
- flags |= FMOD_INIT_SOFTWARE_DISABLE;
- if( smPrefUseSoftwareOcclusion )
- flags |= FMOD_INIT_OCCLUSION_LOWPASS;
- if( smPrefUseSoftwareHRTF )
- flags |= FMOD_INIT_HRTF_LOWPASS;
- if( smPrefEnableProfile )
- flags |= FMOD_INIT_ENABLE_PROFILE;
- if( smPrefGeometryUseClosest )
- flags |= FMOD_INIT_GEOMETRY_USECLOSEST;
-
- if( smEventSystem )
- result = smFunc->FMOD_EventSystem_Init( smEventSystem, 100, flags, ( void* ) 0, FMOD_EVENT_INIT_NORMAL );
- else
- result = smFunc->FMOD_System_Init( smSystem, 100, flags, ( void* ) 0 );
-
- if( result == FMOD_ERR_OUTPUT_CREATEBUFFER ) /* Ok, the speaker mode selected isn't supported by this soundcard. Switch it back to stereo... */
- {
- result = smFunc->FMOD_System_SetSpeakerMode( smSystem, FMOD_SPEAKERMODE_STEREO );
- FMOD_CHECK( "SFXFMODDevice::init - failed on fallback speaker mode setup" );
- tryAgain = true;
- }
- } while( tryAgain );
- FMOD_CHECK( "SFXFMODDevice::init - failed to init system" );
-
- // Print hardware channel info.
- if( caps & FMOD_CAPS_HARDWARE )
- {
- int num3D, num2D, numTotal;
-
- if( smFunc->FMOD_System_GetHardwareChannels( smSystem, &num2D, &num3D, &numTotal ) == FMOD_OK )
- Con::printf( "FMOD Hardware channels: 2d=%i, 3d=%i, total=%i", num2D, num3D, numTotal );
- }
- // Set up filesystem.
-
- //FIXME: Don't do this for now. Crashes on Windows.
- #if 0
- smFunc->FMOD_System_SetFileSystem( smSystem, fmodFileOpenCallback, fmodFileCloseCallback, fmodFileReadCallback, fmodFileSeekCallback, -1 );
- #endif
-
- // Set capabilities.
-
- mCaps = CAPS_Reverb | CAPS_VoiceManagement;
- if( smEventSystem )
- mCaps |= CAPS_FMODDesigner;
- // Start the update thread.
-
- #ifndef TORQUE_DEDICATED // Avoid dependency on platform/async for Linx dedicated.
-
- if( !Con::getBoolVariable( "$_forceAllMainThread" ) )
- {
- SFXInternal::gUpdateThread = new AsyncPeriodicUpdateThread
- ( "FMOD Update Thread", SFXInternal::gBufferUpdateList,
- Con::getIntVariable( "$pref::SFX::updateInterval", SFXInternal::DEFAULT_UPDATE_INTERVAL ) );
- SFXInternal::gUpdateThread->start();
- }
-
- #endif
-
- return true;
- }
- //-----------------------------------------------------------------------------
- SFXBuffer* SFXFMODDevice::createBuffer( const ThreadSafeRef< SFXStream >& stream, SFXDescription* description )
- {
- AssertFatal( stream, "SFXFMODDevice::createBuffer() - Got a null stream!" );
- AssertFatal( description, "SFXFMODDevice::createBuffer() - Got null description!" );
- SFXFMODBuffer *buffer = SFXFMODBuffer::create( stream, description );
- if ( buffer )
- _addBuffer( buffer );
- return buffer;
- }
- //-----------------------------------------------------------------------------
- SFXBuffer* SFXFMODDevice::createBuffer( const String& filename, SFXDescription* description )
- {
- AssertFatal( filename.isNotEmpty(), "SFXFMODDevice::createBuffer() - Got an empty filename!" );
- AssertFatal( description, "SFXFMODDevice::createBuffer() - Got null description!" );
- SFXFMODBuffer* buffer = SFXFMODBuffer::create( filename, description );
- if( buffer )
- _addBuffer( buffer );
-
- return buffer;
- }
- //-----------------------------------------------------------------------------
- SFXVoice* SFXFMODDevice::createVoice( bool is3D, SFXBuffer* buffer )
- {
- AssertFatal( buffer, "SFXFMODDevice::createVoice() - Got null buffer!" );
- SFXFMODBuffer* fmodBuffer = dynamic_cast<SFXFMODBuffer*>( buffer );
- AssertFatal( fmodBuffer, "SFXFMODDevice::createVoice() - Got bad buffer!" );
- SFXFMODVoice* voice = SFXFMODVoice::create( this, fmodBuffer );
- if ( !voice )
- return NULL;
- _addVoice( voice );
- return voice;
- }
- //-----------------------------------------------------------------------------
- void SFXFMODDevice::update()
- {
- Parent::update();
- if( smEventSystem )
- {
- FModAssert( smFunc->FMOD_EventSystem_Update( smEventSystem ), "Failed to update event system!" );
- }
- else
- {
- FModAssert(smFunc->FMOD_System_Update(smSystem), "Failed to update system!");
- }
- }
- //-----------------------------------------------------------------------------
- void SFXFMODDevice::setNumListeners( U32 num )
- {
- smFunc->FMOD_System_Set3DNumListeners( smSystem, num );
- }
- //-----------------------------------------------------------------------------
- void SFXFMODDevice::setListener( U32 index, const SFXListenerProperties& listener )
- {
- FMOD_VECTOR position, forward, up, velocity;
-
- TorqueTransformToFMODVectors( listener.getTransform(), position, forward, up );
- TorqueVectorToFMODVector( listener.getVelocity(), velocity );
- // Do the listener state update, then update!
- smFunc->FMOD_System_Set3DListenerAttributes( smSystem, index, &position, &velocity, &forward, &up );
- }
- //-----------------------------------------------------------------------------
- void SFXFMODDevice::setDistanceModel( SFXDistanceModel model )
- {
- switch( model )
- {
- case SFXDistanceModelLinear:
- m3drolloffmode = FMOD_3D_LINEARROLLOFF;
- break;
-
- case SFXDistanceModelLogarithmic:
- m3drolloffmode = FMOD_3D_INVERSEROLLOFF;
- break;
-
- default:
- AssertWarn( false, "SFXFMODDevice::setDistanceModel - model not implemented" );
- }
- }
- //-----------------------------------------------------------------------------
- void SFXFMODDevice::setDopplerFactor( F32 factor )
- {
- F32 dopplerFactor;
- F32 distanceFactor;
- F32 rolloffFactor;
-
- smFunc->FMOD_System_Get3DSettings( smSystem, &dopplerFactor, &distanceFactor, &rolloffFactor );
- dopplerFactor = factor;
- smFunc->FMOD_System_Set3DSettings( smSystem, dopplerFactor, distanceFactor, rolloffFactor );
- }
- //-----------------------------------------------------------------------------
- void SFXFMODDevice::setRolloffFactor( F32 factor )
- {
- F32 dopplerFactor;
- F32 distanceFactor;
- F32 rolloffFactor;
-
- smFunc->FMOD_System_Get3DSettings( smSystem, &dopplerFactor, &distanceFactor, &rolloffFactor );
- rolloffFactor = factor;
- smFunc->FMOD_System_Set3DSettings( smSystem, dopplerFactor, distanceFactor, rolloffFactor );
- }
- //-----------------------------------------------------------------------------
- void SFXFMODDevice::setReverb( const SFXReverbProperties& reverb )
- {
- FMOD_REVERB_PROPERTIES prop = FMOD_PRESET_GENERIC;
- prop.Environment = 0;
- prop.EnvDiffusion = reverb.mEnvDiffusion;
- prop.Room = reverb.mRoom;
- prop.RoomHF = reverb.mRoomHF;
- prop.RoomLF = reverb.mRoomLF;
- prop.DecayTime = reverb.mDecayTime;
- prop.DecayLFRatio = reverb.mDecayLFRatio;
- prop.DecayHFRatio = reverb.mDecayHFRatio;
- prop.Reflections = reverb.mReflections;
- prop.ReflectionsDelay = reverb.mReflectionsDelay;
- prop.Reverb = reverb.mReverb;
- prop.ReverbDelay = reverb.mReverbDelay;
- prop.ModulationTime = reverb.mModulationTime;
- prop.ModulationDepth = reverb.mModulationDepth;
- prop.HFReference = reverb.mHFReference;
- prop.LFReference = reverb.mLFReference;
- prop.Diffusion = reverb.mDiffusion;
- prop.Density = reverb.mDensity;
- prop.Flags = reverb.mFlags;
-
- // Here we only want to affect 3D sounds. While not quite obvious from the docs,
- // SetReverbProperties sets the global reverb environment for 2D sounds whereas
- // SetAmbientReverbProperties sets the global reverb environment for 3D sounds.
-
- FMOD_RESULT result = smFunc->FMOD_System_SetReverbAmbientProperties( smSystem, &prop );
- if( result != FMOD_OK )
- Con::errorf( "SFXFMODDevice::setReverb - Failed to set reverb (%s)", FMODResultToString( result ).c_str() );
- }
- //-----------------------------------------------------------------------------
- void SFXFMODDevice::resetReverb()
- {
- FMOD_REVERB_PROPERTIES prop = FMOD_PRESET_OFF;
- smFunc->FMOD_System_SetReverbProperties( smSystem, &prop );
- }
- //-----------------------------------------------------------------------------
- void SFXFMODDevice::updateMemUsageStats()
- {
- smFunc->FMOD_System_GetMemoryInfo( smSystem, ( unsigned int ) FMOD_MEMBITS_ALL,
- ( unsigned int ) 0, ( unsigned int* ) &smStatMemUsageCore, ( unsigned int* ) 0 );
-
- if( smEventSystem )
- smFunc->FMOD_EventSystem_GetMemoryInfo( smEventSystem, ( unsigned int ) 0,
- ( unsigned int ) FMOD_EVENT_MEMBITS_ALL, ( unsigned int* ) &smStatMemUsageEvents, ( unsigned int* ) 0 );
- }
- //=============================================================================
- // Console Functions.
- //=============================================================================
- // MARK: ---- Console Functions ----
- //------------------------------------------------------------------------------
- ConsoleFunction( fmodDumpDSPInfo, void, 1, 1, "()"
- "@brief Dump information about the standard DSP effects.\n\n"
- "@ingroup SFXFMOD")
- {
- if( !SFXFMODDevice::smFunc )
- return;
-
- const U32 firstDSPType = FMOD_DSP_TYPE_MIXER;
- const U32 lastDSPType = FMOD_DSP_TYPE_TREMOLO;
-
- for( U32 i = firstDSPType; i <= lastDSPType; ++ i )
- {
- FMOD_DSP* dsp;
- if( SFXFMODDevice::smFunc->FMOD_System_CreateDSPByType( SFXFMODDevice::smSystem, ( FMOD_DSP_TYPE ) i, &dsp ) == FMOD_OK )
- {
- // Print general info.
-
- char name[ 33 ];
- unsigned int version;
- int channels;
- int numParameters;
-
- dMemset( name, 0, sizeof( name ) );
- SFXFMODDevice::smFunc->FMOD_DSP_GetInfo( dsp, name, &version, &channels, ( int* ) NULL, ( int* ) NULL );
- SFXFMODDevice::smFunc->FMOD_DSP_GetNumParameters( dsp, &numParameters );
-
- Con::printf( "----------------------------------------------------------------" );
- Con::printf( "DSP: %s", name );
- Con::printf( "Version: %i.%i", ( version & 0xffff0000 ) >> 16, version & 0xffff );
- Con::printf( "Channels: %i", channels );
- Con::printf( "Parameters: %i", numParameters );
- Con::printf( "" );
-
- // Print parameter info.
-
- for( U32 n = 0; n < numParameters; ++ n )
- {
- char name[ 17 ];
- char label[ 17 ];
- char description[ 1024 ];
- float minValue, maxValue;
- float value;
- char valueString[ 256 ];
-
- dMemset( name, 0, sizeof( name ) );
- dMemset( label, 0, sizeof( label ) );
- dMemset( description, 0, sizeof( description ) );
- dMemset( valueString, 0, sizeof( valueString ) );
-
- SFXFMODDevice::smFunc->FMOD_DSP_GetParameterInfo( dsp, n, name, label, description, sizeof( description ) - 1, &minValue, &maxValue );
- SFXFMODDevice::smFunc->FMOD_DSP_GetParameter( dsp, n, &value, valueString, sizeof( valueString ) - 1 );
-
- Con::printf( "* Parameter %i", n );
- Con::printf( "Name: %s", name );
- Con::printf( "Label: %s", label );
- Con::printf( "Description: %s", description );
- Con::printf( "Min: %f", minValue );
- Con::printf( "Max: %f", maxValue );
- Con::printf( "Value: %f (%s)", value, valueString );
- Con::printf( "" );
- }
-
- // Release the DSP.
-
- SFXFMODDevice::smFunc->FMOD_DSP_Release( dsp );
- }
- }
- }
- //-----------------------------------------------------------------------------
- ConsoleFunction( fmodDumpMemoryStats, void, 1, 1, "()"
- "@return Prints the current memory consumption of the FMOD module\n\n"
- "@ingroup SFXFMOD")
- {
- int current = 0;
- int max = 0;
- if (SFXFMODDevice::smFunc && SFXFMODDevice::smFunc->FMOD_Memory_GetStats.fn)
- SFXFMODDevice::smFunc->FMOD_Memory_GetStats(¤t, &max);
- Con::printf("Fmod current: %d, max: %d", current, max);
- }
|