summaryrefslogtreecommitdiff
path: root/Runtime/Audio/AudioManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Runtime/Audio/AudioManager.cpp')
-rw-r--r--Runtime/Audio/AudioManager.cpp1773
1 files changed, 1773 insertions, 0 deletions
diff --git a/Runtime/Audio/AudioManager.cpp b/Runtime/Audio/AudioManager.cpp
new file mode 100644
index 0000000..0f89245
--- /dev/null
+++ b/Runtime/Audio/AudioManager.cpp
@@ -0,0 +1,1773 @@
+#include "UnityPrefix.h"
+#include "Runtime/Misc/ReproductionLog.h"
+#if ENABLE_AUDIO
+#include "AudioManager.h"
+#include "AudioListener.h"
+#include "AudioSource.h"
+#include "AudioReverbZone.h"
+#include "Runtime/BaseClasses/ManagerContext.h"
+#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h"
+#include "Runtime/Dynamics/RigidBody.h"
+#include "Runtime/Graphics/Transform.h"
+#include "Runtime/Profiler/Profiler.h"
+#include "Runtime/BaseClasses/GameObject.h"
+#include "Runtime/Audio/correct_fmod_includer.h"
+#include "Runtime/Video/MoviePlayback.h"
+#include "Runtime/Utilities/PathNameUtility.h"
+#include "WavReader.h"
+#include "Runtime/Profiler/ProfilerStats.h"
+#include "Runtime/Utilities/UserAuthorizationManager.h"
+#include "Runtime/Misc/UTF8.h"
+#include "Runtime/Misc/BuildSettings.h"
+#include "Runtime/Utilities/Argv.h"
+#include "Runtime/Core/Callbacks/GlobalCallbacks.h"
+
+#if UNITY_IPHONE
+#include "External/Audio/FMOD/builds/iphone/include/fmodiphone.h"
+#include <AudioToolbox/AudioFile.h>
+#include <AudioToolbox/AudioQueue.h>
+#include <AudioToolbox/AudioServices.h>
+#include "Runtime/Misc/PlayerSettings.h"
+#endif
+
+#if UNITY_EDITOR
+#include "Runtime/BaseClasses/IsPlaying.h"
+#if UNITY_WIN
+#include "PlatformDependent/Win/WinUnicode.h"
+#endif
+#endif
+
+#if UNITY_PS3
+#include "External/Audio/FMOD/builds/ps3/include/fmodps3.h"
+struct CellSpurs2;
+extern CellSpurs2* g_pSpursInstance;
+extern uint8_t g_aFmodPriorities[8];
+#endif
+
+#if UNITY_ANDROID
+#include "PlatformDependent/AndroidPlayer/FMOD_FileIO.h"
+#endif
+
+#if UNITY_XENON
+#include "fmodxbox360.h"
+#include "PlatformDependent/Xbox360/Source/Services/Audio.h"
+#endif
+
+#if UNITY_METRO
+#include "PlatformDependent/MetroPlayer/AppCallbacks.h"
+#include "PlatformDependent/MetroPlayer/MetroCapabilities.h"
+#endif
+
+#if UNITY_WP8
+#include "PlatformDependent/WP8Player/WP8Capabilities.h"
+#endif
+
+void* F_CALLBACK FMODMemoryAlloc (unsigned int size, FMOD_MEMORY_TYPE type, const char *sourcestr)
+{
+#if UNITY_XENON
+ if(type & FMOD_MEMORY_XBOX360_PHYSICAL)
+ {
+ return XPhysicalAlloc(size, MAXULONG_PTR, 0, PAGE_READWRITE);
+ }
+ else
+#endif
+ {
+ SET_ALLOC_OWNER(GetAudioManagerPtr());
+ return UNITY_MALLOC_ALIGNED(kMemFMOD, size, 16);
+ }
+}
+
+void F_CALLBACK FMODMemoryFree(void *ptr, FMOD_MEMORY_TYPE type, const char *sourcestr);
+
+void* F_CALLBACK FMODMemoryRealloc (void *ptr, unsigned int size, FMOD_MEMORY_TYPE type, const char *sourcestr)
+{
+#if UNITY_XENON
+ if (type & FMOD_MEMORY_XBOX360_PHYSICAL)
+ {
+ char *newdata = (char *)FMODMemoryAlloc(size, type, sourcestr);
+ if (newdata && ptr)
+ {
+ int copylen;
+ int curlen = XPhysicalSize(ptr);
+ copylen = (size > curlen) ? curlen : size;
+ memcpy(newdata, ptr, copylen);
+ }
+ if (ptr)
+ {
+ FMODMemoryFree(ptr, type, sourcestr);
+ }
+ return newdata;
+ }
+ else
+#endif
+ {
+ return UNITY_REALLOC_ALIGNED(kMemFMOD, ptr, size, 16);
+ }
+}
+
+void F_CALLBACK FMODMemoryFree (void *ptr, FMOD_MEMORY_TYPE type, const char *sourcestr)
+{
+#if UNITY_XENON
+ if (type & FMOD_MEMORY_XBOX360_PHYSICAL)
+ {
+ XPhysicalFree(ptr);
+ }
+ else
+#endif
+ {
+ UNITY_FREE (kMemFMOD, ptr);
+ }
+}
+
+
+using namespace std;
+
+extern double GetTimeSinceStartup();
+
+// ---------------------------------------------------------------------------
+
+static void _InitScriptBufferManager()
+{
+#if ENABLE_AUDIO_FMOD
+ if (GetAudioManagerPtr())
+ GetAudioManager().InitScriptBufferManager();
+#endif
+}
+
+void AudioManager::InitializeClass ()
+{
+#if UNITY_EDITOR
+ RegisterAllowNameConversion (AudioManager::GetClassStringStatic(), "iOS DSP Buffer Size", "m_DSPBufferSize");
+#endif
+
+
+ //@TODO: Refactor this. Its ugly...
+ GlobalCallbacks::Get().managersWillBeReloadedHack.Register(_InitScriptBufferManager);
+}
+
+void AudioManager::CleanupClass ()
+{
+
+}
+
+AudioManager::AudioManager (MemLabelId label, ObjectCreationMode mode)
+ : Super(label, mode)
+ ,m_DefaultVolume(1.0f)
+ ,m_Volume(1.0f)
+ ,m_IsPaused(false)
+ ,m_Rolloffscale(1.0f)
+ ,m_DisableAudio(false)
+#if ENABLE_AUDIO_FMOD
+ ,m_SpeedOfSound(347.0f)
+ ,m_DopplerFactor(1.0f)
+ ,m_speakerMode(FMOD_SPEAKERMODE_STEREO)
+ ,m_activeSpeakerMode(FMOD_SPEAKERMODE_STEREO)
+ ,m_speakerModeCaps(FMOD_SPEAKERMODE_STEREO)
+ ,m_driverCaps(0)
+ ,m_FMODSystem(NULL)
+ ,m_ChannelGroup_FMODMaster(NULL)
+ ,m_ChannelGroup_FX_IgnoreVolume(NULL)
+ ,m_ChannelGroup_FX_UseVolume(NULL)
+ ,m_ChannelGroup_NoFX_IgnoreVolume(NULL)
+ ,m_ChannelGroup_NoFX_UseVolume(NULL)
+ ,m_DSPBufferSize(0)
+ ,m_ScriptBufferManager(NULL)
+ ,m_accPausedTicks(0)
+ ,m_pauseStartTicks(0)
+#endif
+{}
+
+AudioManager::~AudioManager ()
+{
+#if ENABLE_AUDIO_FMOD
+ CloseFMOD();
+ m_FMODSystem->release();
+#endif
+}
+
+#if ENABLE_AUDIO_FMOD
+// ----------------------------------------------------------------------
+// FMOD
+
+bool AudioManager::ValidateFMODResult(FMOD_RESULT result, const char* errmsg)
+{
+ if (result != FMOD_OK)
+ {
+ m_LastErrorString = FMOD_ErrorString(result);
+ m_LastFMODErrorResult = result;
+ ErrorString(std::string(errmsg) + m_LastErrorString);
+ return false;
+ }
+ return true;
+}
+
+
+#if UNITY_METRO
+namespace FMOD {
+
+extern Windows::UI::Core::CoreDispatcher^ (*GetAppcallbackCoreDispatcher)(); // Defined in fmod_output_wasapi.cpp
+
+} // namespace FMOD
+#endif // UNITY_METRO
+
+bool AudioManager::InitFMOD()
+{
+ FMOD_RESULT result;
+
+ string fmoddebuglevel = GetFirstValueForARGV("fmoddebuglevel");
+ if(!fmoddebuglevel.empty())
+ {
+ int level = atoi(fmoddebuglevel.c_str());
+ FMOD::Debug_SetLevel(level);
+ }
+ else
+ {
+#if DEBUGMODE && !UNITY_WINRT
+ // FMOD in verbose mode .. remember to link w. L versions of FMOD
+ FMOD::Debug_SetLevel( FMOD_DEBUG_ALL );
+#else
+ FMOD::Debug_SetLevel(FMOD_DEBUG_LEVEL_NONE);
+#endif
+ }
+
+ if (!m_FMODSystem) // not loaded yet
+ {
+ #if UNITY_METRO
+ // This serves as a getter for the dispatcher from AppCallbacks.cpp
+ FMOD::GetAppcallbackCoreDispatcher = &UnityPlayer::AppCallbacks::GetCoreDispatcher;
+ #endif
+
+ FMOD::Memory_Initialize(NULL, 0, FMODMemoryAlloc, FMODMemoryRealloc, FMODMemoryFree);
+ result = FMOD::System_Create(&m_FMODSystem); // Create the main system object.
+ if(!ValidateFMODResult(result, "FMOD failed to initialize ... ")) return false;
+ #if UNITY_ANDROID
+ m_FMODSystem->setFileSystem(FMOD_FileOpen, FMOD_FileClose, FMOD_FileRead, FMOD_FileSeek, 0,0,-1);
+ #elif UNITY_WII
+ # if WIIWARE
+ m_FMODSystem->setFileSystem(wii::FMOD_FileOpen, wii::FMOD_FileClose, wii::FMOD_FileRead, wii::FMOD_FileSeek, NULL, NULL, -1);
+ # endif
+ #endif
+ }
+
+#if DEBUGMODE
+ if (m_FMODSystem)
+ {
+ FMOD_ADVANCEDSETTINGS settings;
+ memset(&settings, 0, sizeof(settings));
+ settings.profileport = 9264;
+ }
+#endif
+
+#if SUPPORT_REPRODUCE_LOG
+ if (RunningReproduction())
+ {
+ if (!InitReproduction())
+ return false;
+ }
+ else
+ {
+ if (!InitNormal())
+ return false;
+ }
+#else
+ if (!InitNormal())
+ return false;
+#endif // SUPPORT_REPRODUCE_LOG
+
+
+ // 64k for streaming buffer sizes - we're streaming from memory so this should be sufficient
+ // when we're streaming from a www/movie class, buffer sizes are set independently
+ result = m_FMODSystem->setStreamBufferSize(64000, FMOD_TIMEUNIT_RAWBYTES);
+ if(!ValidateFMODResult(result, "FMOD failed to initialize ... ")) return false;
+
+ // Setup system callbacks
+ result = m_FMODSystem->setCallback(AudioManager::systemCallback);
+ if(!ValidateFMODResult(result, "FMOD failed to setup system callbacks ... ")) return false;
+
+ #if UNITY_EDITOR
+ m_EditorChannel = NULL;
+ #endif
+
+ // Setup channel callbacks
+ result = m_FMODSystem->set3DRolloffCallback(AudioSource::rolloffCallback);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel callbacks ... ")) return false;
+
+ // setup channel groups
+
+ Assert(m_ChannelGroup_FMODMaster == NULL);
+ result = m_FMODSystem->getMasterChannelGroup(&m_ChannelGroup_FMODMaster);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ Assert(m_ChannelGroup_FX_IgnoreVolume == NULL);
+ result = m_FMODSystem->createChannelGroup("FX_IgnoreVol", &m_ChannelGroup_FX_IgnoreVolume);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ Assert(m_ChannelGroup_FX_UseVolume == NULL);
+ result = m_FMODSystem->createChannelGroup("FX_UseVol", &m_ChannelGroup_FX_UseVolume);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ Assert(m_ChannelGroup_NoFX_IgnoreVolume == NULL);
+ result = m_FMODSystem->createChannelGroup("NoFX_IgnoreVol", &m_ChannelGroup_NoFX_IgnoreVolume);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ Assert(m_ChannelGroup_NoFX_UseVolume == NULL);
+ result = m_FMODSystem->createChannelGroup("NoFX_UseVol", &m_ChannelGroup_NoFX_UseVolume);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ result = m_ChannelGroup_FMODMaster->addGroup(m_ChannelGroup_FX_IgnoreVolume);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ result = m_ChannelGroup_FX_IgnoreVolume->addGroup(m_ChannelGroup_FX_UseVolume);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ result = m_ChannelGroup_FMODMaster->addGroup(m_ChannelGroup_NoFX_IgnoreVolume);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ result = m_ChannelGroup_NoFX_IgnoreVolume->addGroup(m_ChannelGroup_NoFX_UseVolume);
+ if(!ValidateFMODResult(result, "FMOD failed to setup channel groups ... ")) return false;
+
+ m_activeSpeakerMode = m_speakerMode;
+
+ return true;
+}
+void AudioManager::InitScriptBufferManager()
+{
+ if (m_ScriptBufferManager == 0)
+ {
+ unsigned DSPBufferSize;
+ int maxOutputChannels;
+ int maxInputChannels;
+ m_FMODSystem->getDSPBufferSize(&DSPBufferSize, NULL);
+ m_FMODSystem->getSoftwareFormat(NULL,
+ NULL,
+ &maxOutputChannels,
+ &maxInputChannels,
+ NULL,
+ NULL);
+ m_ScriptBufferManager = new AudioScriptBufferManager(16384 / 4, DSPBufferSize * std::max(maxOutputChannels, maxInputChannels));
+ }
+}
+void AudioManager::ReloadFMODSounds()
+{
+ CloseFMOD();
+ InitFMOD();
+ InitScriptBufferManager();
+
+ // reload any loaded audio clips
+ std::vector<AudioClip*> audioClips;
+ Object::FindObjectsOfType(&audioClips);
+ for(std::vector<AudioClip*>::iterator it = audioClips.begin(); it != audioClips.end(); ++it)
+ (*it)->Reload();
+
+ #if ENABLE_SCRIPTING
+ // Recreate Filters on Monobehaviours
+ std::vector<MonoBehaviour*> monoBehaviours;
+ Object::FindObjectsOfType(&monoBehaviours);
+ for(std::vector<MonoBehaviour*>::iterator it = monoBehaviours.begin(); it != monoBehaviours.end(); ++it)
+ {
+ MonoBehaviour* behaviour = *it;
+ FMOD::DSP* dsp = behaviour->GetOrCreateDSP();
+ if (dsp)
+ dsp->setBypass(!behaviour->GetEnabled());
+ }
+ #endif
+
+ // Awake sources
+ std::vector<AudioSource*> audioSources;
+ Object::FindObjectsOfType(&audioSources);
+ for(std::vector<AudioSource*>::iterator it = audioSources.begin(); it != audioSources.end(); ++it)
+ (*it)->AwakeFromLoad(kDefaultAwakeFromLoad);
+
+ // reload listener filters (if any)
+ TAudioListenersIterator i = m_Listeners.begin();
+ for (;i!=m_Listeners.end();++i)
+ {
+ AudioListener& curListener = **i;
+ curListener.ApplyFilters();
+ }
+
+ // reload reverb zones (if any)
+ TAudioReverbZonesIterator j = m_ReverbZones.begin();
+ for(;j!=m_ReverbZones.end();++j)
+ {
+ AudioReverbZone& curReverbZone = **j;
+ curReverbZone.Init();
+ }
+}
+
+
+void AudioManager::SetSpeakerMode(FMOD_SPEAKERMODE speakerMode)
+{
+ m_speakerMode = speakerMode;
+ if (m_activeSpeakerMode != m_speakerMode)
+ ReloadFMODSounds();
+}
+#endif //ENABLE_AUDIO_FMOD
+
+double AudioManager::GetDSPTime() const
+{
+#if ENABLE_AUDIO_FMOD
+ int sampleRate;
+ m_FMODSystem->getSoftwareFormat(&sampleRate, NULL, NULL, NULL, NULL, NULL);
+ if(m_IsPaused)
+ return (double)(m_pauseStartTicks - m_accPausedTicks) / (double)sampleRate;
+ unsigned clockLo, clockHi;
+ m_FMODSystem->getDSPClock(&clockHi, &clockLo);
+ return (double)(((UInt64)clockHi << 32) + clockLo - m_accPausedTicks) / (double)sampleRate;
+#else
+ return GetTimeSinceStartup();
+#endif
+}
+
+#if SUPPORT_REPRODUCE_LOG && ENABLE_AUDIO_FMOD
+bool AudioManager::InitReproduction( )
+{
+ // set a non-realtime wav writer up as output
+ FMOD_RESULT result = m_FMODSystem->setOutput(FMOD_OUTPUTTYPE_WAVWRITER_NRT);
+ if(!ValidateFMODResult(result, "[Reproduction]FMOD failed to initialize WAV writer output... ")) return false;
+
+ // set lowest possible mixing quality (stereo,11025hz, 8bit)
+ result = m_FMODSystem->setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
+ if(!ValidateFMODResult(result, "[Reproduction]FMOD failed to set speaker mode... ")) return false;
+
+ result = m_FMODSystem->setSoftwareFormat(22050, FMOD_SOUND_FORMAT_PCM8, 0, 6, FMOD_DSP_RESAMPLER_LINEAR);
+ if(!ValidateFMODResult(result, "[Reproduction]FMOD failed to set software format... ")) return false;
+
+ // init (with filename)
+ string reproductionPathTemp = AppendPathName( GetReproductionDirectory(), "/Audio/" );
+ string out = AppendPathName(reproductionPathTemp, "audio_output.wav");
+ result = m_FMODSystem->init(100, FMOD_INIT_STREAM_FROM_UPDATE | FMOD_INIT_SYNCMIXERWITHUPDATE, (void*)out.c_str()); // Initialize FMOD.
+ if(!ValidateFMODResult(result, "[Reproduction]FMOD failed to initialize ... ")) return false;
+
+ return true;
+}
+#endif //SUPPORT_REPRODUCE_LOG && ENABLE_AUDIO_FMODD
+
+#if UNITY_LINUX
+static void LogDriverDetails (FMOD::System *system, int driverID)
+{
+ char driverName[BUFSIZ];
+ FMOD_GUID guid;
+ FMOD_OUTPUTTYPE output;
+ char *outputName;
+
+ system->getOutput (&output);
+ system->getDriverInfo (driverID, driverName, BUFSIZ, &guid);
+
+ switch (output) {
+ case FMOD_OUTPUTTYPE_PULSEAUDIO:
+ outputName = "PulseAudio";
+ break;
+ case FMOD_OUTPUTTYPE_ALSA:
+ outputName = "ALSA";
+ break;
+ case FMOD_OUTPUTTYPE_OSS:
+ outputName = "OSS";
+ break;
+ case FMOD_OUTPUTTYPE_ESD:
+ outputName = "ESD";
+ break;
+ default:
+ outputName = "Unknown";
+ break;
+ }
+ printf_console ("AudioManager: Using %s: %s\n", outputName, driverName);
+}
+#endif
+
+
+#if ENABLE_AUDIO_FMOD
+bool AudioManager::InitNormal( )
+{
+ // Get available sound cards
+ // If no device is found fall back on the NOSOUND driver
+ // @TODO Enable user to choose driver
+ int numDrivers;
+ FMOD_RESULT result = m_FMODSystem->getNumDrivers(&numDrivers);
+ if(!ValidateFMODResult(result, "FMOD failed to get number of drivers ... ")) return false;
+
+ if (numDrivers == 0
+#if !UNITY_EDITOR
+ || m_DisableAudio
+#endif
+ ) // no suitable audio devices/drivers
+ {
+ result = m_FMODSystem->setOutput(FMOD_OUTPUTTYPE_NOSOUND);
+ if(!ValidateFMODResult(result, "FMOD failed to initialize nosound device ... ")) return false;
+ }
+
+ // get driver id
+ int driverID;
+ m_FMODSystem->getDriver(&driverID);
+
+ // setup speakermode
+ // check if current hw is capable of the current speakerMode
+ result = m_FMODSystem->getDriverCaps(driverID,&m_driverCaps,0,&m_speakerModeCaps);
+ if(!ValidateFMODResult(result, "FMOD failed to get driver capabilities ... ")) return false;
+
+ m_activeSpeakerMode = m_speakerMode;
+
+ if (m_speakerModeCaps < m_activeSpeakerMode)
+ {
+ if (m_activeSpeakerMode != FMOD_SPEAKERMODE_SRS5_1_MATRIX)
+ // hardware is not capable of the current speakerMode
+ m_activeSpeakerMode = m_speakerModeCaps;
+ }
+
+#if UNITY_EDITOR
+
+ int samplerate;
+ FMOD_SOUND_FORMAT format;
+ FMOD_DSP_RESAMPLER resampler;
+
+ result = m_FMODSystem->getSoftwareFormat(&samplerate, &format, NULL, NULL, &resampler, NULL);
+ if(!ValidateFMODResult(result, "FMOD failed to get driver capabilities ... ")) return false;
+
+ result = m_FMODSystem->setSoftwareFormat(samplerate, format, 0, 8, resampler);
+ if(!ValidateFMODResult(result, "FMOD failed to get driver capabilities ... ")) return false;
+
+#endif
+
+ result = m_FMODSystem->setSpeakerMode((FMOD_SPEAKERMODE)m_activeSpeakerMode);
+ if(result != FMOD_OK)
+ {
+ ErrorStringMsg("FMOD could not set speaker mode to the one specified in the project settings. Falling back to stereo.");
+ result = m_FMODSystem->setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
+ }
+ if(!ValidateFMODResult(result, "FMOD failed to set speaker mode ... ")) return false;
+
+ FMOD_INITFLAGS initFlags = FMOD_INIT_NORMAL;
+
+ if(HasARGV("fmodprofiler"))
+ initFlags |= FMOD_INIT_ENABLE_PROFILE;
+
+ // Initialize FMOD.
+#if UNITY_IPHONE
+ FMOD_IPHONE_EXTRADRIVERDATA extradriverdata;
+ memset(&extradriverdata, 0, sizeof(FMOD_IPHONE_EXTRADRIVERDATA));
+
+ if (GetPlayerSettings().prepareIOSForRecording)
+ extradriverdata.sessionCategory = FMOD_IPHONE_SESSIONCATEGORY_PLAYANDRECORD;
+ else if (GetPlayerSettings().overrideIPodMusic)
+ extradriverdata.sessionCategory = FMOD_IPHONE_SESSIONCATEGORY_SOLOAMBIENTSOUND;
+ else
+ extradriverdata.sessionCategory = FMOD_IPHONE_SESSIONCATEGORY_AMBIENTSOUND;
+
+ extradriverdata.forceMixWithOthers = !GetPlayerSettings().overrideIPodMusic;
+
+ if (m_DSPBufferSize != 0)
+ {
+ result = m_FMODSystem->setDSPBufferSize(m_DSPBufferSize, 4);
+ if(!ValidateFMODResult(result, "FMOD failed to set DSP Buffer size ... ")) return false;
+ }
+
+ result = m_FMODSystem->init(100, initFlags, &extradriverdata);
+#elif UNITY_PS3
+ if (!IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1) && m_activeSpeakerMode == FMOD_SPEAKERMODE_STEREO)
+ initFlags |= FMOD_INIT_PS3_FORCE2CHLPCM;
+
+ FMOD_PS3_EXTRADRIVERDATA extradriverdata;
+ memset(&extradriverdata, 0, sizeof(FMOD_PS3_EXTRADRIVERDATA));
+ extradriverdata.spurs = g_pSpursInstance;
+ extradriverdata.spursmode = FMOD_PS3_SPURSMODE_CREATECONTEXT;
+ extradriverdata.spurs_taskset_priorities = &g_aFmodPriorities[0];
+ result = m_FMODSystem->setSoftwareChannels(64);
+ result = m_FMODSystem->setOutput((!UNITY_EDITOR && m_DisableAudio) ? FMOD_OUTPUTTYPE_NOSOUND : FMOD_OUTPUTTYPE_PS3);
+ result = m_FMODSystem->setSpeakerMode(FMOD_SPEAKERMODE_7POINT1);
+ result = m_FMODSystem->init(100, initFlags | FMOD_INIT_VOL0_BECOMES_VIRTUAL, &extradriverdata);
+
+#elif UNITY_XENON
+ FMOD_360_EXTRADRIVERDATA extraDriverData;
+ ZeroMemory(&extraDriverData, sizeof(extraDriverData));
+ extraDriverData.xaudio2instance = xenon::Audio::GetXAudio();
+ result = m_FMODSystem->init(100, initFlags, &extraDriverData);
+#elif UNITY_ANDROID
+ result = m_FMODSystem->setOutput((!UNITY_EDITOR && m_DisableAudio) ? FMOD_OUTPUTTYPE_NOSOUND : FMOD_OUTPUTTYPE_AUDIOTRACK);
+ if(!ValidateFMODResult(result, "FMOD failed to force Java Audio Track output ... ")) return false;
+ if (m_DSPBufferSize != 0)
+ {
+ result = m_FMODSystem->setDSPBufferSize(m_DSPBufferSize, 4);
+ if(!ValidateFMODResult(result, "FMOD failed to set DSP Buffer size ... ")) return false;
+ }
+ result = m_FMODSystem->setSpeakerMode(FMOD_SPEAKERMODE_STEREO);
+ result = m_FMODSystem->init(100, initFlags, NULL);
+#else
+ result = m_FMODSystem->init(100, initFlags, NULL);
+#endif
+ if(!ValidateFMODResult(result, "FMOD failed to initialize ... ")) return false;
+
+#if UNITY_LINUX
+ LogDriverDetails (m_FMODSystem, driverID);
+#endif
+
+ return true;
+}
+
+void AudioManager::CloseFMOD()
+{
+ if (m_FMODSystem)
+ {
+ // Cleanup sources
+ std::vector<AudioSource*> audioSources;
+ Object::FindObjectsOfType(&audioSources);
+ for(std::vector<AudioSource*>::iterator it = audioSources.begin(); it != audioSources.end(); ++it)
+ (*it)->Cleanup();
+
+ // Cleanup listener(s)
+ std::vector<AudioListener*> audioListeners;
+ Object::FindObjectsOfType(&audioListeners);
+ for(std::vector<AudioListener*>::iterator it = audioListeners.begin(); it != audioListeners.end(); ++it)
+ (*it)->Cleanup();
+
+ // Cleanup reverb zone(s)
+ std::vector<AudioReverbZone*> audioReverbZones;
+ Object::FindObjectsOfType(&audioReverbZones);
+ for(std::vector<AudioReverbZone*>::iterator it = audioReverbZones.begin(); it != audioReverbZones.end(); ++it)
+ (*it)->Cleanup();
+
+ if (m_ChannelGroup_FX_IgnoreVolume)
+ {
+ m_ChannelGroup_FX_IgnoreVolume->release();
+ m_ChannelGroup_FX_IgnoreVolume = NULL;
+ }
+
+ if (m_ChannelGroup_NoFX_IgnoreVolume)
+ {
+ m_ChannelGroup_NoFX_IgnoreVolume->release();
+ m_ChannelGroup_NoFX_IgnoreVolume = NULL;
+ }
+
+ if (m_ChannelGroup_FX_UseVolume)
+ {
+ m_ChannelGroup_FX_UseVolume->release();
+ m_ChannelGroup_FX_UseVolume = NULL;
+ }
+
+ if (m_ChannelGroup_NoFX_UseVolume)
+ {
+ m_ChannelGroup_NoFX_UseVolume->release();
+ m_ChannelGroup_NoFX_UseVolume = NULL;
+ }
+
+ // m_ChannelGroup_FMODMaster is the FMOD master group so we should not call release on it
+ m_ChannelGroup_FMODMaster = NULL;
+
+ // cleanup any loaded audio clips
+ std::vector<AudioClip*> audioClips;
+ Object::FindObjectsOfType(&audioClips);
+ for(std::vector<AudioClip*>::iterator it = audioClips.begin(); it != audioClips.end(); ++it)
+ (*it)->Cleanup();
+
+ m_FMODSystem->close();
+ }
+
+ delete m_ScriptBufferManager;
+ m_ScriptBufferManager = NULL;
+}
+
+
+
+int AudioManager::GetMemoryAllocated() const
+{
+ int a = 0;
+ FMOD::Memory_GetStats(&a, NULL);
+ return a;
+}
+
+float AudioManager::GetCPUUsage() const
+{
+ float c = 0.0f;
+ if (m_FMODSystem)
+ m_FMODSystem->getCPUUsage(NULL, NULL, NULL,NULL, &c);
+ return c;
+}
+
+#if ENABLE_PROFILER
+void AudioManager::GetProfilerData( AudioStats& audioStats )
+{
+ if (m_FMODSystem)
+ {
+ FMOD::Memory_GetStats(&audioStats.audioMemUsage, &audioStats.audioMaxMemUsage);
+ float cpuUsage;
+ m_FMODSystem->getCPUUsage(NULL, NULL, NULL, NULL, &cpuUsage);
+ audioStats.audioCPUusage = RoundfToInt(cpuUsage * 10.0F);
+ m_FMODSystem->getChannelsPlaying(&audioStats.audioVoices);
+ audioStats.pausedSources = m_PausedSources.size_slow();
+ audioStats.playingSources = m_Sources.size_slow();
+ audioStats.audioClipCount = AudioClip::s_AudioClipCount;
+ audioStats.audioSourceCount = AudioSource::s_AudioSourceCount;
+ FMOD_MEMORY_USAGE_DETAILS details;
+ m_FMODSystem->getMemoryInfo(FMOD_EVENT_MEMBITS_ALL, 0, &audioStats.audioMemDetailsUsage, &details);
+ audioStats.audioMemDetails.other = details.other; /* [out] Memory not accounted for by other types */
+ audioStats.audioMemDetails.string = details.string; /* [out] String data */
+ audioStats.audioMemDetails.system = details.system; /* [out] System object and various internals */
+ audioStats.audioMemDetails.plugins = details.plugins; /* [out] Plugin objects and internals */
+ audioStats.audioMemDetails.output = details.output; /* [out] Output module object and internals */
+ audioStats.audioMemDetails.channel = details.channel; /* [out] Channel related memory */
+ audioStats.audioMemDetails.channelgroup = details.channelgroup; /* [out] ChannelGroup objects and internals */
+ audioStats.audioMemDetails.codec = details.codec; /* [out] Codecs allocated for streaming */
+ audioStats.audioMemDetails.file = details.file; /* [out] File buffers and structures */
+ audioStats.audioMemDetails.sound = details.sound; /* [out] Sound objects and internals */
+ audioStats.audioMemDetails.secondaryram = details.secondaryram; /* [out] Sound data stored in secondary RAM */
+ audioStats.audioMemDetails.soundgroup = details.soundgroup; /* [out] SoundGroup objects and internals */
+ audioStats.audioMemDetails.streambuffer = details.streambuffer; /* [out] Stream buffer memory */
+ audioStats.audioMemDetails.dspconnection = details.dspconnection; /* [out] DSPConnection objects and internals */
+ audioStats.audioMemDetails.dsp = details.dsp; /* [out] DSP implementation objects */
+ audioStats.audioMemDetails.dspcodec = details.dspcodec; /* [out] Realtime file format decoding DSP objects */
+ audioStats.audioMemDetails.profile = details.profile; /* [out] Profiler memory footprint. */
+ audioStats.audioMemDetails.recordbuffer = details.recordbuffer; /* [out] Buffer used to store recorded data from microphone */
+ audioStats.audioMemDetails.reverb = details.reverb; /* [out] Reverb implementation objects */
+ audioStats.audioMemDetails.reverbchannelprops = details.reverbchannelprops; /* [out] Reverb channel properties structs */
+ audioStats.audioMemDetails.geometry = details.geometry; /* [out] Geometry objects and internals */
+ audioStats.audioMemDetails.syncpoint = details.syncpoint; /* [out] Sync point memory. */
+ audioStats.audioMemDetails.eventsystem = details.eventsystem; /* [out] EventSystem and various internals */
+ audioStats.audioMemDetails.musicsystem = details.musicsystem; /* [out] MusicSystem and various internals */
+ audioStats.audioMemDetails.fev = details.fev; /* [out] Definition of objects contained in all loaded projects e.g. events, groups, categories */
+ audioStats.audioMemDetails.memoryfsb = details.memoryfsb; /* [out] Data loaded with preloadFSB */
+ audioStats.audioMemDetails.eventproject = details.eventproject; /* [out] EventProject objects and internals */
+ audioStats.audioMemDetails.eventgroupi = details.eventgroupi; /* [out] EventGroup objects and internals */
+ audioStats.audioMemDetails.soundbankclass = details.soundbankclass; /* [out] Objects used to manage wave banks */
+ audioStats.audioMemDetails.soundbanklist = details.soundbanklist; /* [out] Data used to manage lists of wave bank usage */
+ audioStats.audioMemDetails.streaminstance = details.streaminstance; /* [out] Stream objects and internals */
+ audioStats.audioMemDetails.sounddefclass = details.sounddefclass; /* [out] Sound definition objects */
+ audioStats.audioMemDetails.sounddefdefclass = details.sounddefdefclass; /* [out] Sound definition static data objects */
+ audioStats.audioMemDetails.sounddefpool = details.sounddefpool; /* [out] Sound definition pool data */
+ audioStats.audioMemDetails.reverbdef = details.reverbdef; /* [out] Reverb definition objects */
+ audioStats.audioMemDetails.eventreverb = details.eventreverb; /* [out] Reverb objects */
+ audioStats.audioMemDetails.userproperty = details.userproperty; /* [out] User property objects */
+ audioStats.audioMemDetails.eventinstance = details.eventinstance; /* [out] Event instance base objects */
+ audioStats.audioMemDetails.eventinstance_complex = details.eventinstance_complex; /* [out] Complex event instance objects */
+ audioStats.audioMemDetails.eventinstance_simple = details.eventinstance_simple; /* [out] Simple event instance objects */
+ audioStats.audioMemDetails.eventinstance_layer = details.eventinstance_layer; /* [out] Event layer instance objects */
+ audioStats.audioMemDetails.eventinstance_sound = details.eventinstance_sound; /* [out] Event sound instance objects */
+ audioStats.audioMemDetails.eventenvelope = details.eventenvelope; /* [out] Event envelope objects */
+ audioStats.audioMemDetails.eventenvelopedef = details.eventenvelopedef; /* [out] Event envelope definition objects */
+ audioStats.audioMemDetails.eventparameter = details.eventparameter; /* [out] Event parameter objects */
+ audioStats.audioMemDetails.eventcategory = details.eventcategory; /* [out] Event category objects */
+ audioStats.audioMemDetails.eventenvelopepoint = details.eventenvelopepoint; /* [out] Event envelope point objects */
+ audioStats.audioMemDetails.eventinstancepool = details.eventinstancepool; /* [out] Event instance pool memory */
+ }
+}
+#endif
+#endif // ENABLE_AUDIO_FMOD
+
+#if ENABLE_WWW && ENABLE_AUDIO_FMOD
+FMOD::Sound* AudioManager::CreateFMODSoundFromWWW(WWW* webStream,
+ bool threeD,
+ FMOD_SOUND_TYPE suggestedtype,
+ FMOD_SOUND_FORMAT format,
+ unsigned freq,
+ unsigned channels,
+ bool stream)
+{
+ if (!m_FMODSystem)
+ return NULL;
+
+ FMOD::Sound* sound = NULL;
+ FMOD_CREATESOUNDEXINFO exInfo;
+ memset(&exInfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
+ exInfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
+ exInfo.decodebuffersize = 16384;
+ exInfo.suggestedsoundtype = suggestedtype;
+ exInfo.format = format;
+ exInfo.defaultfrequency = freq;
+ exInfo.numchannels = channels;
+ exInfo.useropen = AudioClip::WWWOpen;
+ exInfo.userclose = AudioClip::WWWClose;
+ exInfo.userread = AudioClip::WWWRead;
+ exInfo.userseek = AudioClip::WWWSeek;
+ exInfo.userdata = (void*)webStream;
+
+
+ FMOD_MODE mode = FMOD_SOFTWARE | (threeD?FMOD_3D:FMOD_2D) | (stream?FMOD_CREATESTREAM:FMOD_CREATESAMPLE) | (suggestedtype==FMOD_SOUND_TYPE_MPEG?FMOD_MPEGSEARCH:FMOD_IGNORETAGS) | FMOD_LOOP_NORMAL | FMOD_3D_CUSTOMROLLOFF ;
+
+ if (suggestedtype==FMOD_SOUND_TYPE_RAW)
+ mode |= FMOD_OPENRAW;
+
+ FMOD_RESULT err = m_FMODSystem->createSound(
+ (const char*)webStream,
+ mode,
+ &exInfo,
+ &sound );
+
+ if (err != FMOD_OK)
+ {
+ m_LastErrorString = FMOD_ErrorString(err);
+ m_LastFMODErrorResult = err;
+ return NULL;
+ }
+ else
+ {
+ // FMOD_LOOP_NORMAL is set on createSound() - this prepares the sound for looping
+ // now turn it off (and let the user set it later)
+ sound->setMode( FMOD_LOOP_OFF );
+ return sound;
+ }
+
+}
+#endif // ENABLE_WWW && ENABLE_AUDIO_FMOD
+
+#if ENABLE_AUDIO_FMOD
+FMOD::Sound* AudioManager::CreateFMODSoundFromMovie(AudioClip* clip, bool threeD)
+{
+ if (!m_FMODSystem)
+ return NULL;
+
+ Assert ( clip->GetMovie() );
+
+ FMOD::Sound* sound = NULL;
+ FMOD_CREATESOUNDEXINFO exInfo;
+ memset(&exInfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
+ exInfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
+ // @FIX FMODs position is increased even though the pcm reader thread is starving (FMOD just repeats the last buffer).
+ // @FIX This can lead to out-of-synch problems and that the audio is stopped before the movie is done.
+ // @FIX fix: double the length of the clip as a headroom (and stop the audio when video frames are done)
+ // @FIX duration: Theora format is really stupid for streams, so if we´re not lucky that the duration is in the meta-tags we set it to infinity
+ if (clip->GetMovie()->GetMovieTotalDuration() < 0)
+ exInfo.length = 0xffffffff;
+ else
+ exInfo.length = (unsigned int)(clip->GetMovie()->GetMovieTotalDuration() * clip->GetFrequency() * clip->GetChannelCount() * ( clip->GetBitsPerSample() / 8 ) * 2);
+ exInfo.decodebuffersize = 4096;
+ exInfo.format = FMOD_SOUND_FORMAT_PCM16;
+ exInfo.defaultfrequency = clip->GetFrequency();
+ exInfo.numchannels = clip->GetChannelCount();
+ exInfo.pcmreadcallback = AudioClip::pcmread;
+ exInfo.userdata = (void*)clip;
+
+
+ FMOD_MODE mode = FMOD_SOFTWARE | (threeD?FMOD_3D:FMOD_2D) | FMOD_CREATESTREAM | FMOD_OPENUSER | FMOD_IGNORETAGS | FMOD_LOOP_NORMAL | FMOD_3D_CUSTOMROLLOFF ;
+
+ FMOD_RESULT err = m_FMODSystem->createSound(
+ 0,
+ mode,
+ &exInfo,
+ &sound );
+
+ if (err != FMOD_OK)
+ {
+ m_LastErrorString = FMOD_ErrorString(err);
+ m_LastFMODErrorResult = err;
+ return NULL;
+ }
+ else
+ {
+ // FMOD_LOOP_NORMAL is set on createSound() - this prepares the sound for looping
+ // now turn it off (and let the user set it later)
+ sound->setMode( FMOD_LOOP_OFF );
+ return sound;
+ }
+}
+
+
+FMOD::Sound* AudioManager::CreateFMODSound(const void* buffer, FMOD_CREATESOUNDEXINFO exInfo, FMOD_MODE mode, bool useHardwareDecoder)
+{
+ Assert(exInfo.cbsize == sizeof(FMOD_CREATESOUNDEXINFO));
+
+#if !(UNITY_ANDROID || UNITY_XENON || UNITY_LINUX || UNITY_BB10 || UNITY_TIZEN)
+ // On Android the file paths have been converted to ascii and fmod doesn't seem to accept unicode strings
+ dynamic_array<UnicodeChar> unicodeBuf (kMemTempAlloc);
+ if((mode & (FMOD_OPENMEMORY | FMOD_OPENMEMORY_POINT | FMOD_OPENUSER)) == 0 && (mode & FMOD_UNICODE) == 0)
+ {
+ ConvertUTF8toUTF16((const char*)buffer, unicodeBuf);
+ unicodeBuf.push_back(0);
+ unicodeBuf.push_back(0); // FMOD reads unicode strings as shorts, so it expects 2 bytes
+ buffer = (const char*)&unicodeBuf[0];
+ mode |= FMOD_UNICODE;
+ }
+#endif
+
+#if UNITY_IPHONE
+ if (mode & FMOD_CREATESTREAM)
+ {
+ exInfo.suggestedsoundtype = exInfo.suggestedsoundtype==FMOD_SOUND_TYPE_MPEG?FMOD_SOUND_TYPE_AUDIOQUEUE:exInfo.suggestedsoundtype;
+ FMOD_AUDIOQUEUE_CODECPOLICY policy = FMOD_AUDIOQUEUE_CODECPOLICY_SOFTWAREONLY;
+ if (useHardwareDecoder)
+ policy = FMOD_AUDIOQUEUE_CODECPOLICY_DEFAULT; // try hardware, if it fails then try software
+ exInfo.audioqueuepolicy = policy;
+ }
+#endif
+ FMOD::Sound* sound = NULL;
+ FMOD_RESULT result = m_FMODSystem->createSound(
+ (const char*)buffer,
+ mode,
+ &exInfo,
+ &sound );
+#if UNITY_IPHONE
+ // if the Apple's AudioQueue codec(sf/hw) fails completely (this can happen with some obscure mp3s that FMOD software MP3 codec can read/import, but Apple's decoder can't)
+ // then use FMOD's SOFTWARE codec instead
+ // @TODO Wait for FMOD to handle this internally
+ if (FMOD_ERR_FORMAT == result)
+ {
+ exInfo.suggestedsoundtype = FMOD_SOUND_TYPE_MPEG;
+ result = m_FMODSystem->createSound(
+ (const char*)buffer,
+ mode,
+ &exInfo,
+ &sound );
+ }
+#endif
+
+ if (result != FMOD_OK)
+ {
+ m_LastErrorString = FMOD_ErrorString(result);
+ m_LastFMODErrorResult = result;
+#if UNITY_WII
+ ErrorStringMsg ("ERROR: %s\n", m_LastErrorString.c_str());
+#endif
+ return NULL;
+ }
+ else
+ {
+ // FMOD_LOOP_NORMAL is set on createSound() - this prepares the sound for looping
+ // now turn it off (and let the user set it later)
+ sound->setMode( FMOD_LOOP_OFF );
+ return sound;
+ }
+}
+#endif //ENABLE_AUDIO_FMOD
+
+#if UNITY_EDITOR && ENABLE_AUDIO_FMOD
+FMOD::Sound* AudioManager::CreateFMODSound(const std::string& path, bool threeD, bool hardware, bool openOnly /* = false */)
+{
+ FMOD::Sound* sound = NULL;
+
+ if (!m_FMODSystem)
+ return NULL;
+
+ Assert( m_FMODSystem );
+
+ const char* strPtr = path.c_str();
+ FMOD_MODE mode = (openOnly?FMOD_OPENONLY:0x0) | FMOD_SOFTWARE | (threeD?FMOD_3D:FMOD_2D) | FMOD_LOOP_OFF | FMOD_MPEGSEARCH;
+#if !UNITY_ANDROID
+ // On Android the file paths have been converted to ascii and fmod doesn't seem to accept unicode strings
+ dynamic_array<UnicodeChar> unicodeBuf (kMemTempAlloc);
+ if((mode & (FMOD_OPENMEMORY | FMOD_OPENMEMORY_POINT | FMOD_OPENUSER)) == 0 && (mode & FMOD_UNICODE) == 0)
+ {
+ ConvertUTF8toUTF16(strPtr, unicodeBuf);
+ unicodeBuf.push_back(0);
+ unicodeBuf.push_back(0); // FMOD reads unicode strings as shorts, so it expects 2 bytes
+ strPtr = (const char*)&unicodeBuf[0];
+ mode |= FMOD_UNICODE;
+ }
+#endif
+
+ FMOD_RESULT err = m_FMODSystem->createSound(strPtr, mode, 0, &sound);
+ if (err != FMOD_OK)
+ {
+ m_LastErrorString = FMOD_ErrorString(err);
+ m_LastFMODErrorResult = err;
+ return NULL;
+ }
+ else
+ return sound;
+}
+
+#endif // UNITY_EDITOR && ENABLE_AUDIO_FMOD
+
+
+#if ENABLE_AUDIO_FMOD
+FMOD::Channel* AudioManager::GetFreeFMODChannel(FMOD::Sound* sound, bool paused /*=true*/)
+{
+ if (!m_FMODSystem)
+ return NULL;
+
+ FMOD::Channel* channel;
+ FMOD_RESULT err = m_FMODSystem->playSound(FMOD_CHANNEL_FREE, sound, paused || (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a3) && m_IsPaused), &channel);
+ if (err != FMOD_OK)
+ {
+ m_LastErrorString = FMOD_ErrorString(err);
+ m_LastFMODErrorResult = err;
+ return NULL;
+ }
+ else
+ return channel;
+
+}
+#endif //ENABLE_AUDIO_FMOD
+
+
+void AudioManager::UpdateListener (
+ const Vector3f& position,
+ const Vector3f& velocity,
+ const Vector3f& up,
+ const Vector3f& forward)
+{
+#if ENABLE_AUDIO_FMOD
+ if (!m_FMODSystem)
+ return;
+
+ m_FMODSystem->set3DListenerAttributes(
+ 0,
+ reinterpret_cast<const FMOD_VECTOR*>( &position ),
+ reinterpret_cast<const FMOD_VECTOR*>( &velocity ),
+ reinterpret_cast<const FMOD_VECTOR*>( &forward ),
+ reinterpret_cast<const FMOD_VECTOR*>( &up )
+ );
+#endif //ENABLE_AUDIO_FMOD
+
+}
+
+int AudioManager::GetAutomaticUpdateMode(GameObject *go)
+{
+ Rigidbody* body = go->QueryComponent (Rigidbody);
+ if (body)
+ return kVelocityUpdateModeFixed;
+
+ Transform* parent = go->GetComponent (Transform).GetParent ();
+ while (parent)
+ {
+ go = parent->GetGameObjectPtr ();
+ if (go)
+ body = go->QueryComponent (Rigidbody);
+ else
+ body = NULL;
+ if (body)
+ return kVelocityUpdateModeFixed;
+
+ parent = parent->GetParent ();
+ }
+ return kVelocityUpdateModeDynamic;
+}
+
+void AudioManager::SetPause (bool pause)
+{
+ if(m_IsPaused == pause)
+ return;
+ m_IsPaused = pause;
+#if ENABLE_AUDIO_FMOD
+ unsigned clockLo, clockHi;
+ m_FMODSystem->getDSPClock(&clockHi, &clockLo);
+ UInt64 dspTicks = ((UInt64)clockHi << 32) + clockLo;
+#else
+ UInt64 dspTicks = 0;
+#endif
+ if (m_IsPaused)
+ {
+ m_pauseStartTicks = dspTicks;
+ // Pause all audio sources and put them in the m_PausedSources, when resuming we start playing them again
+ for (TAudioSourcesIterator i=m_Sources.begin();i != m_Sources.end();)
+ {
+ AudioSource& source = **(i);
+ i++;
+
+ if (source.IsPlaying())
+ {
+ source.Pause();
+ m_PausedSources.push_back(source.m_Node);
+ }
+ source.PauseOneShots();
+ }
+ }
+ else
+ {
+ UInt64 pauseDuration = dspTicks - m_pauseStartTicks;
+ m_accPausedTicks += pauseDuration;
+ // Resume all paused audio sources
+ for (TAudioSourcesIterator i=m_PausedSources.begin();i != m_PausedSources.end();)
+ {
+ AudioSource& source = **(i);
+ ++i;
+
+ // Play is pushing the source to the m_Sources list
+ // don't play if editor is pause
+ source.Play();
+ if(source.HasScheduledTime())
+ source.CorrectScheduledTimeAfterUnpause(pauseDuration);
+ }
+
+ for (TAudioSourcesIterator i=m_Sources.begin();i != m_Sources.end();)
+ {
+ AudioSource& source = **(i);
+ ++i;
+ source.ResumeOneShots();
+ }
+ Assert( m_PausedSources.empty() );
+ }
+}
+
+void AudioManager::SetVolume (float volume)
+{
+#if ENABLE_AUDIO_FMOD
+ if (!m_FMODSystem)
+ return;
+ m_ChannelGroup_FX_UseVolume->setVolume(volume);
+ m_ChannelGroup_NoFX_UseVolume->setVolume(volume);
+#endif
+#if UNITY_FLASH
+ __asm __volatile__("SoundMixer.soundTransform = new SoundTransform(Math.min(Math.max(0.0, %0), 1.0));"::"f"(volume));
+#endif
+ m_Volume = volume;
+}
+
+float AudioManager::GetVolume () const
+{
+ return m_Volume;
+}
+
+
+template<class TransferFunction>
+void AudioManager::Transfer (TransferFunction& transfer)
+{
+ Super::Transfer (transfer);
+
+ transfer.Transfer (m_DefaultVolume, "m_Volume", kSimpleEditorMask);
+ transfer.Transfer (m_Rolloffscale, "Rolloff Scale");
+
+#if ENABLE_AUDIO_FMOD
+ if (!(transfer.IsWritingGameReleaseData () && transfer.GetBuildingTarget().platform==kBuildFlash || transfer.GetBuildingTarget().platform==kBuildWebGL)){
+ transfer.Transfer (m_SpeedOfSound, "m_SpeedOfSound");
+ transfer.Transfer (m_DopplerFactor, "Doppler Factor");
+
+ transfer.Transfer ((SInt32&)m_speakerMode, "Default Speaker Mode"); // Remember to specify the TransferName in the doxygen comment in the .h file
+
+ TRANSFER (m_DSPBufferSize);
+ }
+#endif
+
+ TRANSFER (m_DisableAudio);
+}
+
+
+void AudioManager::AwakeFromLoad (AwakeFromLoadMode awakeMode)
+{
+ Super::AwakeFromLoad (awakeMode);
+#if ENABLE_AUDIO_FMOD
+ if (!m_FMODSystem)
+ {
+ InitFMOD();
+ m_IsPaused = false;
+ }
+
+ if (!m_FMODSystem)
+ return;
+
+ // has the speakermode changed?
+ if (m_activeSpeakerMode != m_speakerMode)
+ {
+ // bootstrap FMOD
+ ReloadFMODSounds();
+ if (!m_FMODSystem)
+ return;
+ }
+ m_Volume = m_DefaultVolume;
+ m_ChannelGroup_FX_UseVolume->setVolume(m_Volume);
+ m_ChannelGroup_NoFX_UseVolume->setVolume(m_Volume);
+ m_FMODSystem->set3DSettings(m_DopplerFactor, 1, m_Rolloffscale);
+#endif
+}
+
+PROFILER_INFORMATION(gAudioUpdateProfile, "AudioManager.Update", kProfilerAudio);
+PROFILER_INFORMATION(gAudioFixedUpdateProfile, "AudioManager.FixedUpdate", kProfilerAudio);
+
+#if UNITY_EDITOR
+void AudioManager::ListenerCheck()
+{
+ if (!IsWorldPlaying())
+ return;
+
+ int listenerCount = m_Listeners.size_slow();
+ // @TODO enable multiple listeners
+ if (listenerCount == 0 && !m_Sources.empty())
+ {
+ LogString("There are no audio listeners in the scene. Please ensure there is always one audio listener in the scene");
+ m_ChannelGroup_FX_UseVolume->setVolume(0.0f);
+ m_ChannelGroup_NoFX_UseVolume->setVolume(0.0f);
+ }
+ else
+ {
+ if (listenerCount > 1)
+ LogString(Format("There are %d audio listeners in the scene. Please ensure there is always exactly one audio listener in the scene.", listenerCount));
+ m_ChannelGroup_FX_UseVolume->setVolume(m_Volume);
+ m_ChannelGroup_NoFX_UseVolume->setVolume(m_Volume);
+ }
+}
+#endif
+
+
+#define Unity_HiWord(x) ((UInt32)((UInt64)(x) >> 32))
+#define Unity_LoWord(x) ((UInt32)(x))
+#if UNITY_WII
+void AudioManager::UpdateOnDiskEject()
+{
+ for (TAudioSourcesIterator i = m_Sources.begin(); i != m_Sources.end(); i++)
+ {
+ AudioSource& curSource = **i;
+ curSource.Update();
+ }
+ m_FMODSystem->update();
+}
+#endif
+void AudioManager::ProcessScheduledSources()
+{
+#if ENABLE_AUDIO_FMOD
+ // start scheduled sources
+ // Get mixer clock
+ unsigned hiclock, loclock;
+ m_FMODSystem->getDSPClock(&hiclock, &loclock);
+#endif
+ for (TScheduledSourcesIterator s = m_ScheduledSources.begin(); s != m_ScheduledSources.end(); s++)
+ {
+ const AudioScheduledSource& p = *s;
+ AudioSource* curSource = p.source;
+
+ Assert(curSource != NULL);
+ Assert(curSource->m_Channel != NULL);
+
+
+#if ENABLE_AUDIO_FMOD
+ if (p.time != 0.0)
+ {
+ int sampleRate;
+ m_FMODSystem->getSoftwareFormat(&sampleRate, NULL, NULL, NULL, NULL, NULL);
+ if(p.time > 0.0)
+ {
+ // exact scheduled
+ UInt64 sample = (UInt64)(p.time * sampleRate) + m_accPausedTicks;
+ curSource->m_Channel->setDelay(FMOD_DELAYTYPE_DSPCLOCK_START, Unity_HiWord(sample), Unity_LoWord(sample));
+ }
+ else
+ {
+ UInt64 sample = ((UInt64)hiclock << 32) + loclock + (UInt64)(-p.time * sampleRate);
+ curSource->m_Channel->setDelay(FMOD_DELAYTYPE_DSPCLOCK_START, Unity_HiWord(sample), Unity_LoWord(sample));
+ }
+ curSource->m_HasScheduledStartDelay = true;
+ }
+#endif
+ // play (TODO: if paused in the same frame - pause here)
+ bool paused = curSource->m_pause || (IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a3) && m_IsPaused && !curSource->m_AudioParameters.ignoreListenerPause);
+ curSource->m_Channel->setPaused(paused);
+ AddAudioSource(curSource, paused); // to make sure source is put into active or paused sources lists
+ }
+ // clean queue
+ m_ScheduledSources.clear();
+}
+
+void AudioManager::Update()
+{
+ PROFILER_AUTO (gAudioUpdateProfile, NULL);
+
+#if ENABLE_AUDIO_FMOD
+ if (!m_FMODSystem)
+ return;
+#endif
+
+ ProcessScheduledSources();
+
+#if UNITY_EDITOR
+ ListenerCheck();
+#endif
+
+ for (TAudioListenersIterator l = m_Listeners.begin(); l != m_Listeners.end(); l++)
+ {
+ AudioListener& curListener = **l;
+ curListener.Update();
+ }
+
+ for (TAudioSourcesIterator i = m_Sources.begin(); i != m_Sources.end(); i++)
+ {
+ AudioSource& curSource = **i;
+#if UNITY_EDITOR
+ if (!IsWorldPlaying() && curSource.m_Channel)
+ {
+ if (curSource.GetGameObject().IsMarkedVisible())
+ curSource.m_Channel->setMute(curSource.GetMute());
+ else
+ curSource.m_Channel->setMute(true);
+ }
+#endif
+ curSource.Update();
+ }
+#if ENABLE_AUDIO_FMOD
+ // update reverb zones position
+ for (TAudioReverbZonesIterator r = m_ReverbZones.begin(); r != m_ReverbZones.end(); ++r)
+ {
+ AudioReverbZone& curReverbZone = **r;
+ curReverbZone.Update();
+ }
+
+ m_FMODSystem->update();
+#endif
+}
+
+
+void AudioManager::FixedUpdate()
+{
+#if ENABLE_AUDIO_FMOD
+ if (!m_FMODSystem)
+ return;
+#endif
+ PROFILER_AUTO (gAudioFixedUpdateProfile, NULL);
+
+ #if UNITY_EDITOR
+ ListenerCheck();
+ #endif
+
+ for (TAudioListenersIterator l = m_Listeners.begin(); l != m_Listeners.end(); l++)
+ {
+ AudioListener& curListener = **l;
+ curListener.FixedUpdate();
+ }
+
+ TAudioSourcesIterator i;
+ for (i = m_Sources.begin(); i != m_Sources.end(); i++)
+ {
+ AudioSource& curSource = **i;
+ curSource.FixedUpdate();
+ }
+}
+
+void AudioManager::AddAudioSource( AudioSource* s, bool paused )
+{
+ Assert(s);
+ if(paused)
+ m_PausedSources.push_back(s->m_Node);
+ else
+ m_Sources.push_back(s->m_Node);
+}
+
+void AudioManager::RemoveAudioSource( AudioSource* s )
+{
+ Assert(s);
+ UnScheduleSource(s);
+ s->m_Node.RemoveFromList(); // note: removes either from m_Sources or m_PausedSources
+}
+
+void AudioManager::StopSources()
+{
+ TAudioSourcesIterator i = m_Sources.begin();
+ while (!m_Sources.empty())
+ {
+ AudioSource& curSource = **i;
+ ++i;
+ curSource.Stop(true);
+ }
+ i = m_PausedSources.begin();
+ while (!m_PausedSources.empty())
+ {
+ AudioSource& curSource = **i;
+ ++i;
+ curSource.Stop(true);
+ }
+}
+
+void AudioManager::AddAudioListener (AudioListener* s)
+{
+ Assert(s);
+ m_Listeners.push_back( s->GetNode() );
+}
+
+void AudioManager::RemoveAudioListener (AudioListener* s)
+{
+ Assert(s);
+ s->GetNode().RemoveFromList();
+}
+
+AudioListener* AudioManager::GetAudioListener() const
+{
+ if (!m_Listeners.empty())
+ return m_Listeners.back().GetData();
+ else
+ return NULL;
+}
+
+/// Schedule source to be played in sync. In this frame if time==0, delayed by -time if time<0 or scheduled at time
+void AudioManager::ScheduleSource(AudioSource* s, double time)
+{
+ s->m_ScheduledSource.RemoveFromList();
+ s->m_ScheduledSource.time = time;
+ m_ScheduledSources.push_back(s->m_ScheduledSource);
+}
+
+void AudioManager::UnScheduleSource(AudioSource* s)
+{
+ s->m_ScheduledSource.RemoveFromList();
+}
+
+#if ENABLE_AUDIO_FMOD
+void AudioManager::AddAudioReverbZone(AudioReverbZone* zone)
+{
+ Assert(zone);
+ m_ReverbZones.push_back(zone->m_Node);
+}
+
+void AudioManager::RemoveAudioReverbZone(AudioReverbZone* zone)
+{
+ Assert(zone);
+ zone->m_Node.RemoveFromList();
+}
+
+
+
+#endif
+
+#if ENABLE_MICROPHONE
+// Microphone(s)
+bool HasMicrophoneAuthorization ()
+{
+ #if UNITY_WINRT
+ #if UNITY_METRO
+ namespace Capabilities = metro::Capabilities;
+ #elif UNITY_WP8
+ namespace Capabilities = WP8::Capabilities;
+ #else
+ #error Unknown WinRT flavour (did you implement capability detection for the OS?)
+ #endif
+
+ Capabilities::IsSupported(Capabilities::kMicrophone, "because you're using Microphone functionality");
+ #endif // UNITY_WINRT
+
+ return GetUserAuthorizationManager().HasUserAuthorization(UserAuthorizationManager::kMicrophone);
+}
+
+const std::vector<std::string> AudioManager::GetRecordDevices() const
+{
+ std::vector<std::string> devices;
+ m_MicrophoneNameToIDMap.clear();
+
+ if (!m_FMODSystem)
+ return devices;
+
+ if (!HasMicrophoneAuthorization())
+ return devices;
+
+ int numDevices;
+ FMOD_RESULT result = m_FMODSystem->getRecordNumDrivers(&numDevices);
+
+ if (result != FMOD_OK)
+ return devices;
+
+ if (numDevices > 0)
+ {
+ for (int i=0; i < numDevices; ++i)
+ {
+ char name[255];
+
+ m_FMODSystem->getRecordDriverInfo(i, name, 255, NULL);
+
+ std::string strName = (char*)name;
+ // update map with a unique name
+ std::string origName = strName;
+ int no = 0;
+ while (m_MicrophoneNameToIDMap.find(strName) != m_MicrophoneNameToIDMap.end())
+ {
+ char post[3];
+ sprintf(post, " %i", ++no);
+ strName = origName + post;
+ }
+ devices.push_back(strName);
+ m_MicrophoneNameToIDMap[strName] = i;
+ }
+ }
+
+ return devices;
+}
+
+int AudioManager::GetMicrophoneDeviceIDFromName(const std::string& name) const
+{
+ if ( m_MicrophoneNameToIDMap.empty() )
+ GetRecordDevices();
+
+ // Double lookup on return is totally unnecessary, because we can cache the iterator
+ std::map<std::string, int>::const_iterator devit = m_MicrophoneNameToIDMap.find( name );
+ if ( devit != m_MicrophoneNameToIDMap.end() )
+ return devit->second;
+ else
+ return 0; // the default device is always 0.
+}
+
+
+void ReportError (char const* msg, FMOD_RESULT result)
+{
+ ErrorString (Format ("%s. result=%d (%s)", msg, result, FMOD_ErrorString (result)));
+}
+
+void CapsToSoundFormat (FMOD_CAPS caps, FMOD_SOUND_FORMAT *soundFormat, int *sampleSizeInBytes)
+{
+ *soundFormat = FMOD_SOUND_FORMAT_PCM16;
+ *sampleSizeInBytes = 2;
+
+ if ((caps & FMOD_CAPS_OUTPUT_FORMAT_PCM16) == FMOD_CAPS_OUTPUT_FORMAT_PCM16)
+ {
+ *soundFormat = FMOD_SOUND_FORMAT_PCM16;
+ *sampleSizeInBytes = 2;
+ }
+ else
+ if ((caps & FMOD_CAPS_OUTPUT_FORMAT_PCM8) == FMOD_CAPS_OUTPUT_FORMAT_PCM8)
+ {
+ *soundFormat = FMOD_SOUND_FORMAT_PCM8;
+ *sampleSizeInBytes = 1;
+ }
+ else
+ if ((caps & FMOD_CAPS_OUTPUT_FORMAT_PCM24) == FMOD_CAPS_OUTPUT_FORMAT_PCM24)
+ {
+ *soundFormat = FMOD_SOUND_FORMAT_PCM24;
+ *sampleSizeInBytes = 3;
+ }
+ else if ((caps & FMOD_CAPS_OUTPUT_FORMAT_PCM32) == FMOD_CAPS_OUTPUT_FORMAT_PCM32)
+ {
+ *soundFormat = FMOD_SOUND_FORMAT_PCM32;
+ *sampleSizeInBytes = 4;
+ }
+ else if ((caps & FMOD_CAPS_OUTPUT_FORMAT_PCMFLOAT) == FMOD_CAPS_OUTPUT_FORMAT_PCMFLOAT)
+ {
+ *soundFormat = FMOD_SOUND_FORMAT_PCMFLOAT;
+ *sampleSizeInBytes = sizeof (float);
+ }
+}
+
+void AudioManager::GetDeviceCaps(int deviceID, int *minFreq, int *maxFreq) const
+{
+ FMOD_CAPS caps = 0;
+ FMOD_RESULT result = m_FMODSystem->getRecordDriverCaps (deviceID, &caps, minFreq, maxFreq);
+
+ if (result != FMOD_OK)
+ {
+ ReportError ("Failed to get record driver caps", result);
+ }
+}
+
+FMOD::Sound* AudioManager::CreateSound (int deviceID, int lengthSec, int frequency)
+{
+ FMOD::Sound* sound;
+ FMOD_CAPS caps = 0;
+ FMOD_RESULT result = m_FMODSystem->getRecordDriverCaps (deviceID, &caps, NULL, NULL);
+
+ if (result != FMOD_OK)
+ {
+ ReportError ("Failed to get record driver caps", result);
+ return NULL;
+ }
+
+ FMOD_SOUND_FORMAT soundFormat = FMOD_SOUND_FORMAT_NONE;
+ int sampleSizeInBytes = 1;
+ CapsToSoundFormat (caps, &soundFormat, &sampleSizeInBytes);
+
+ FMOD_CREATESOUNDEXINFO exinfo;
+ memset(&exinfo, 0, sizeof(FMOD_CREATESOUNDEXINFO));
+
+ exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO);
+ exinfo.numchannels = 1;
+ exinfo.format = soundFormat;
+ exinfo.defaultfrequency = frequency;
+ exinfo.length = exinfo.defaultfrequency * sampleSizeInBytes * exinfo.numchannels * lengthSec;
+
+ FMOD_MODE mode = FMOD_2D | FMOD_SOFTWARE | FMOD_OPENUSER;
+ result = m_FMODSystem->createSound (0, mode, &exinfo, &sound);
+
+ if (result != FMOD_OK)
+ {
+ ReportError ("Failed to create sound clip for recording", result);
+ return NULL;
+ }
+
+ return sound;
+}
+
+PPtr<AudioClip> AudioManager::StartRecord(int deviceID, bool loop, int lengthSec, int frequency)
+{
+ if (!m_FMODSystem)
+ return NULL;
+
+ if (!HasMicrophoneAuthorization())
+ return NULL;
+
+ if (lengthSec <= 0)
+ {
+ ErrorString("Length of the recording must be greater than zero (0)");
+ return NULL;
+ }
+
+ if (frequency <= 0)
+ {
+ ErrorString("Frequency must be greater than zero (0)");
+ return NULL;
+ }
+
+ FMOD::Sound* sound = CreateSound (deviceID, lengthSec, frequency);
+
+ if (sound == NULL)
+ return NULL;
+
+ FMOD_RESULT result = m_FMODSystem->recordStart (deviceID, sound, loop);
+
+ if (result == FMOD_OK)
+ {
+ PPtr<AudioClip> audioClip = NEW_OBJECT(AudioClip);
+ audioClip->Reset();
+ audioClip->HackSetAwakeWasCalled();
+
+ audioClip->InitWSound(sound);
+ audioClip->SetName("Microphone");
+
+ return audioClip;
+ }
+ else
+ {
+ ReportError ("Starting Microphone failed", result);
+ return NULL;
+ }
+}
+
+bool AudioManager::EndRecord(int deviceID)
+{
+ if (!m_FMODSystem)
+ return false;
+
+ m_FMODSystem->recordStop(deviceID);
+
+ return true;
+}
+
+bool AudioManager::IsRecording(int deviceID) const
+{
+ if (!m_FMODSystem)
+ return false;
+
+ bool isRecording;
+ m_FMODSystem->isRecording(deviceID, &isRecording);
+ return isRecording;
+}
+
+unsigned AudioManager::GetRecordPosition(int deviceID) const
+{
+ if (!m_FMODSystem)
+ return 0;
+
+ unsigned pos;
+ m_FMODSystem->getRecordPosition(deviceID, &pos);
+ return pos;
+}
+
+#endif // ENABLE_MICROPHONE
+
+#if ENABLE_AUDIO_FMOD
+AudioScriptBufferManager& AudioManager::GetScriptBufferManager()
+{
+ return *GetScriptBufferManagerPtr();
+}
+
+AudioScriptBufferManager* AudioManager::GetScriptBufferManagerPtr()
+{
+ if (m_ScriptBufferManager == 0)
+ {
+ InitScriptBufferManager ();
+ }
+
+ return m_ScriptBufferManager;
+}
+#endif
+
+#if UNITY_EDITOR
+void AudioManager::PlayClip(AudioClip& clip, int startSample, bool loop, bool twoD)
+{
+ if (!m_FMODSystem)
+ return;
+
+ // update FMOD to get any device changes
+ m_FMODSystem->update();
+
+ if (m_EditorChannel)
+ m_EditorChannel->stop();
+
+ m_EditorChannel = clip.CreateChannel();
+
+ if(m_EditorChannel != NULL)
+ {
+ if (twoD && clip.Is3D())
+ {
+ m_EditorChannel->setMode(FMOD_2D);
+ }
+
+ m_EditorChannel->setChannelGroup(m_ChannelGroup_NoFX_IgnoreVolume);
+
+ FMOD_REVERB_CHANNELPROPERTIES rev;
+ memset(&rev, 0, sizeof(rev));
+ rev.Room = -10000;
+ m_EditorChannel->setReverbProperties(&rev);
+
+ if (loop)
+ m_EditorChannel->setMode(FMOD_LOOP_NORMAL);
+
+ // movie audio
+ if (clip.GetMovie())
+ clip.GetMovie()->SetAudioChannel(m_EditorChannel);
+
+ m_EditorChannel->setPaused(false);
+ }
+}
+
+
+void AudioManager::LoopClip(const AudioClip& clip, bool loop)
+{
+ if (m_EditorChannel)
+ m_EditorChannel->setMode(loop?FMOD_LOOP_NORMAL:FMOD_LOOP_OFF);
+}
+
+
+void AudioManager::StopClip(const AudioClip& clip)
+{
+ if (m_EditorChannel)
+ m_EditorChannel->stop();
+
+ if (clip.GetMovie())
+ clip.GetMovie()->SetAudioChannel(NULL);
+}
+
+void AudioManager::StopAllClips()
+{
+ if (m_EditorChannel)
+ m_EditorChannel->stop();
+}
+void AudioManager::PauseClip(const AudioClip& clip)
+{
+ if (m_EditorChannel)
+ m_EditorChannel->setPaused(true);
+
+ if (clip.GetMovie())
+ clip.GetMovie()->SetAudioChannel(NULL);
+}
+void AudioManager::ResumeClip(const AudioClip& clip)
+{
+ if (m_EditorChannel)
+ m_EditorChannel->setPaused(false);}
+
+bool AudioManager::IsClipPlaying(const AudioClip& clip)
+{
+ bool isPlaying = false;
+ if (m_EditorChannel)
+ m_EditorChannel->isPlaying(&isPlaying);
+ return isPlaying;
+}
+
+float AudioManager::GetClipPosition(const AudioClip& clip)
+{
+ unsigned int pos = 0;
+ if (m_EditorChannel)
+ m_EditorChannel->getPosition(&pos, FMOD_TIMEUNIT_MS);
+ return (float)pos / 1000.f;
+}
+
+unsigned int AudioManager::GetClipSamplePosition(const AudioClip& clip)
+{
+ unsigned int pos = 0;
+ if (m_EditorChannel)
+ m_EditorChannel->getPosition(&pos, FMOD_TIMEUNIT_PCM);
+ return pos;
+}
+
+void AudioManager::SetClipSamplePosition(const AudioClip& clip, unsigned int iSamplePosition)
+{
+ if (m_EditorChannel)
+ m_EditorChannel->setPosition(iSamplePosition, FMOD_TIMEUNIT_PCM);
+}
+
+#endif // UNITY_EDITOR
+
+
+
+IMPLEMENT_CLASS_HAS_INIT (AudioManager)
+IMPLEMENT_OBJECT_SERIALIZE (AudioManager)
+GET_MANAGER (AudioManager)
+GET_MANAGER_PTR (AudioManager)
+
+#endif //ENABLE_AUDIO