diff options
Diffstat (limited to 'Runtime/Audio')
46 files changed, 10507 insertions, 0 deletions
diff --git a/Runtime/Audio/AudioBehaviour.cpp b/Runtime/Audio/AudioBehaviour.cpp new file mode 100644 index 0000000..eac56cc --- /dev/null +++ b/Runtime/Audio/AudioBehaviour.cpp @@ -0,0 +1,16 @@ +#include "UnityPrefix.h" +#include "AudioBehaviour.h" + +#if ENABLE_AUDIO + +IMPLEMENT_CLASS (AudioBehaviour) + +AudioBehaviour::AudioBehaviour (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{} + +AudioBehaviour::~AudioBehaviour () +{ +} + +#endif
\ No newline at end of file diff --git a/Runtime/Audio/AudioBehaviour.h b/Runtime/Audio/AudioBehaviour.h new file mode 100644 index 0000000..46275cb --- /dev/null +++ b/Runtime/Audio/AudioBehaviour.h @@ -0,0 +1,21 @@ +#ifndef __AUDIOTYPES_H__ +#define __AUDIOTYPES_H__ +#if ENABLE_AUDIO + +#include "Runtime/GameCode/Behaviour.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" + +// macros/helpers +#define UNITYVEC2FMODVEC(v) *(reinterpret_cast<FMOD_VECTOR*>(&v)) +#define UNITYVEC2FMODVECPTR(v) (reinterpret_cast<FMOD_VECTOR*>(&v)) + +class AudioBehaviour : public Behaviour +{ +public: + REGISTER_DERIVED_ABSTRACT_CLASS (AudioBehaviour, Behaviour) + + AudioBehaviour (MemLabelId label, ObjectCreationMode mode); +}; + +#endif //ENABLE_AUDIO +#endif // __AUDIOTYPES_H__ diff --git a/Runtime/Audio/AudioChorusFilter.cpp b/Runtime/Audio/AudioChorusFilter.cpp new file mode 100644 index 0000000..ef1294c --- /dev/null +++ b/Runtime/Audio/AudioChorusFilter.cpp @@ -0,0 +1,86 @@ +#include "UnityPrefix.h" +#include "AudioChorusFilter.h" +#include "Runtime/Utilities/Utility.h" + +#if ENABLE_AUDIO_FMOD + +AudioChorusFilter::AudioChorusFilter (MemLabelId label, ObjectCreationMode mode) : +Super(label, mode), +m_DryMix(0.5f), +m_WetMix1(0.5f), +m_WetMix2(0.5f), +m_WetMix3(0.5f), +m_Delay(40.0f), +m_Rate(0.8f), +m_Depth(0.03f) +{ + m_Type = FMOD_DSP_TYPE_CHORUS; +} + +AudioChorusFilter::~AudioChorusFilter() +{} + + +void AudioChorusFilter::AddToManager() +{ + Super::AddToManager(); +} + +void AudioChorusFilter::Reset() +{ + Super::Reset(); + + m_DryMix = 0.5f; + m_WetMix1 = 0.5f; + m_WetMix2 = 0.5f; + m_WetMix3 = 0.5f; + m_Delay = 40.0f; + m_Rate = 0.8f; + m_Depth = 0.03f; +} + +void AudioChorusFilter::CheckConsistency() +{ + Super::CheckConsistency(); + m_DryMix = clamp(m_DryMix,0.0f,1.0f); + m_WetMix1 = clamp(m_WetMix1,0.0f,1.0f); + m_WetMix2 = clamp(m_WetMix2, 0.0f, 1.0f); + m_WetMix3 = clamp(m_WetMix3, 0.0f, 1.0f); + m_Delay = clamp(m_Delay, 0.1f, 100.0f); + m_Rate = clamp(m_Rate,0.0f,20.0f); + m_Depth = clamp(m_Depth,0.0f,1.0f); +} + +void AudioChorusFilter::Update() +{ + if (m_DSP) + { + m_DSP->setParameter(FMOD_DSP_CHORUS_DRYMIX, m_DryMix); + m_DSP->setParameter(FMOD_DSP_CHORUS_WETMIX1, m_WetMix1); + m_DSP->setParameter(FMOD_DSP_CHORUS_WETMIX2, m_WetMix2); + m_DSP->setParameter(FMOD_DSP_CHORUS_WETMIX3, m_WetMix3); + m_DSP->setParameter(FMOD_DSP_CHORUS_DELAY, m_Delay); + m_DSP->setParameter(FMOD_DSP_CHORUS_RATE, m_Rate); + m_DSP->setParameter(FMOD_DSP_CHORUS_DEPTH, m_Depth); + } +} + + +template<class TransferFunc> +void AudioChorusFilter::Transfer (TransferFunc& transfer) +{ + Super::Transfer (transfer); + TRANSFER(m_DryMix); + TRANSFER(m_WetMix1); + TRANSFER(m_WetMix2); + TRANSFER(m_WetMix3); + TRANSFER(m_Delay); + TRANSFER(m_Rate); + TRANSFER(m_Depth); +} + + +IMPLEMENT_CLASS (AudioChorusFilter) +IMPLEMENT_OBJECT_SERIALIZE (AudioChorusFilter) + +#endif //ENABLE_AUDIO diff --git a/Runtime/Audio/AudioChorusFilter.h b/Runtime/Audio/AudioChorusFilter.h new file mode 100644 index 0000000..c656149 --- /dev/null +++ b/Runtime/Audio/AudioChorusFilter.h @@ -0,0 +1,51 @@ +#ifndef __AUDIOCHORUS_FILTER_H__ +#define __AUDIOCHORUS_FILTER_H__ + +#if ENABLE_AUDIO_FMOD +#include "AudioSourceFilter.h" + +class AudioChorusFilter : public AudioFilter +{ +public: + REGISTER_DERIVED_CLASS (AudioChorusFilter, AudioFilter) + DECLARE_OBJECT_SERIALIZE (AudioChorusFilter) + AudioChorusFilter (MemLabelId label, ObjectCreationMode mode); + + virtual void CheckConsistency (); + virtual void Update(); + virtual void AddToManager(); + virtual void Reset(); + + float GetDelay() const { return m_Delay; } + void SetDelay(const float delay) { m_Delay = delay; Update(); SetDirty(); } + + float GetRate() const { return m_Rate; } + void SetRate(const float rate) { m_Rate = rate; Update(); SetDirty(); } + + float GetDryMix() const { return m_DryMix; } + void SetDryMix(const float drymix) { m_DryMix = drymix; Update(); SetDirty(); } + + float GetWetMix1() const { return m_WetMix1; } + void SetWetMix1(const float wetmix) { m_WetMix1 = wetmix; Update(); SetDirty(); } + + float GetWetMix2() const { return m_WetMix2; } + void SetWetMix2(const float wetmix) { m_WetMix2 = wetmix; Update(); SetDirty();} + + float GetWetMix3() const { return m_WetMix3; } + void SetWetMix3(const float wetmix) { m_WetMix3 = wetmix; Update(); SetDirty(); } + + float GetDepth() const { return m_Depth; } + void SetDepth(const float depth) { m_Depth = depth; Update(); SetDirty();} + +private: + float m_DryMix; // FMOD_DSP_CHORUS_DRYMIX, Volume of original signal to pass to output. 0.0 to 1.0. Default = 0.5. + float m_WetMix1; // FMOD_DSP_CHORUS_WETMIX1, Volume of 1st chorus tap. 0.0 to 1.0. Default = 0.5. + float m_WetMix2; // FMOD_DSP_CHORUS_WETMIX2, Volume of 2nd chorus tap. This tap is 90 degrees out of phase of the first tap. 0.0 to 1.0. Default = 0.5. + float m_WetMix3; // FMOD_DSP_CHORUS_WETMIX3, Volume of 3rd chorus tap. This tap is 90 degrees out of phase of the second tap. 0.0 to 1.0. Default = 0.5. + float m_Delay; // FMOD_DSP_CHORUS_DELAY, Chorus delay in ms. 0.1 to 100.0. Default = 40.0 ms. + float m_Rate; // FMOD_DSP_CHORUS_RATE, Chorus modulation rate in hz. 0.0 to 20.0. Default = 0.8 hz. + float m_Depth; // FMOD_DSP_CHORUS_DEPTH, Chorus modulation depth. 0.0 to 1.0. Default = 0.03. +}; + +#endif //ENABLE_AUDIO +#endif // __AUDIOSOURCE_FILTER_H__ diff --git a/Runtime/Audio/AudioClip.Callbacks.cpp b/Runtime/Audio/AudioClip.Callbacks.cpp new file mode 100644 index 0000000..e76de98 --- /dev/null +++ b/Runtime/Audio/AudioClip.Callbacks.cpp @@ -0,0 +1,270 @@ +#include "UnityPrefix.h" +#include "AudioClip.h" +#include "OggReader.h" +#include "Runtime/Audio/correct_fmod_includer.h" +#include "Runtime/Mono/MonoScopedThreadAttach.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Profiler/TimeHelper.h" +#include "Runtime/Audio/AudioManager.h" +#if ENABLE_WWW +#include "Runtime/Export/WWW.h" +#endif + +#if UNITY_EDITOR +#include "Editor/Src/AssetPipeline/MonoCompile.h" +#endif + +#include "Runtime/Scripting/Scripting.h" + +#if ENABLE_AUDIO_FMOD + + + +/** + * Streaming callbacks + **/ + +/** + * file callback function used by FMOD + * we're using this to stream data from WWW stream + **/ + + +#if ENABLE_WWW +FMOD_RESULT F_CALLBACK AudioClip::WWWOpen( + const char * wwwstream, + int unicode, + unsigned int * filesize, + void ** handle, + void ** userdata +) +{ + WWW* stream = (WWW*) wwwstream; + + if (stream) + { + // predict filesize + stream->LockPartialData(); + // make sure we've read enough to determine filesize + if (stream->GetPartialSize() == 0) + { + stream->UnlockPartialData(); + return FMOD_ERR_NOTREADY; + } + *filesize = (unsigned int)((1.0 / stream->GetProgress()) * stream->GetPartialSize()); + wwwUserData* ud = new wwwUserData(); + ud->pos = 0; + ud->filesize = *filesize; + ud->stream = stream; + *userdata = (void*)ud; + *handle = (void*) wwwstream; + stream->UnlockPartialData(); + + return FMOD_OK; + } + + return FMOD_ERR_INVALID_PARAM; +} + + +FMOD_RESULT F_CALLBACK AudioClip::WWWClose( + void * handle, + void * userdata +) +{ + if (!handle) + { + return FMOD_ERR_INVALID_PARAM; + } + + wwwUserData* ud = (wwwUserData*) userdata; + delete ud; + return FMOD_OK; +} + + +FMOD_RESULT F_CALLBACK AudioClip::WWWRead( + void * handle, + void * buffer, + unsigned int sizebytes, + unsigned int * bytesread, + void * userdata +) +{ + if (!handle) + { + return FMOD_ERR_INVALID_PARAM; + } + + wwwUserData* ud = (wwwUserData*) userdata; + + Assert (ud->stream); + Assert (ud->stream->GetAudioClip()); + + ud->stream->LockPartialData(); + + // read sizebytes + const UInt8* bufferStart = ud->stream->GetPartialData(); + unsigned avail = ud->stream->GetPartialSize(); + + if ( ud->pos > avail) + { + ud->stream->UnlockPartialData(); + return FMOD_ERR_NOTREADY; + } + + *bytesread = avail - ud->pos<sizebytes?(avail-ud->pos):sizebytes; + memcpy (buffer, bufferStart + ud->pos, *bytesread); + + // update pos + ud->pos = ud->pos + *bytesread; + + ud->stream->UnlockPartialData(); + + if (*bytesread < sizebytes) + return FMOD_ERR_FILE_EOF; + + return FMOD_OK; +} + +FMOD_RESULT F_CALLBACK AudioClip::WWWSeek( + void * handle, + unsigned int pos, + void * userdata +) +{ + if (!handle) + { + return FMOD_ERR_INVALID_PARAM; + } + + wwwUserData* ud = (wwwUserData*) userdata; + + Assert( ud->stream ); + Assert( ud->stream->GetAudioClip() ); + + if ( ud->stream->GetAudioClip()->IsWWWStreamed() || ud->filesize < pos) + return FMOD_ERR_FILE_COULDNOTSEEK; + + ud->pos = pos; + + return FMOD_OK; +} + +#endif + +/** + * Buffer streaming callbacks + **/ + +/** + * Callbacks for PCM streaming + **/ +FMOD_RESULT F_CALLBACK AudioClip::pcmread( + FMOD_SOUND * sound, + void * data, + unsigned int datalen + ) +{ + FMOD::Sound* pSound = (FMOD::Sound*) sound; + AudioClip* ac = NULL; + pSound->getUserData((void**)&ac); + + if (ac->GetQueuedAudioData(&data, datalen)) + return FMOD_OK; + + return FMOD_ERR_NOTREADY; +} + +/** + * Callbacks for PCM streaming + **/ +FMOD_RESULT F_CALLBACK AudioClip::ScriptPCMReadCallback( + FMOD_SOUND * sound, + void * data, + unsigned int datalen + ) +{ +#if SUPPORT_MONO_THREADS || UNITY_WINRT + +#if UNITY_EDITOR + if (IsCompiling()) + return FMOD_OK; +#endif + + FMOD::Sound* pSound = (FMOD::Sound*) sound; + AudioClip* ac = NULL; + pSound->getUserData((void**)&ac); + + Assert (ac); +#if ENABLE_MONO + ScopedThreadAttach attach(ac->monoDomain); +#endif + + // reuse mono array + ScriptingArrayPtr array = SCRIPTING_NULL; + GetAudioManager().GetScriptBufferManager().GetPCMReadArray(datalen / 4, array); + + Assert(array != SCRIPTING_NULL); + + // invoke + ScriptingObjectPtr instance = Scripting::ScriptingWrapperFor(ac); + ScriptingExceptionPtr exception; + + ScriptingInvocation invocation(ac->m_CachedPCMReaderCallbackMethod); + invocation.AddArray(array); + invocation.object = instance; + invocation.Invoke(&exception); + if (exception) + { + // handle + Scripting::LogException(exception, Scripting::GetInstanceIDFromScriptingWrapper(instance)); + } + else + { + memcpy(data, &Scripting::GetScriptingArrayElement<float>( array, 0 ), datalen); + } +#endif // SUPPORT_MONO_THREADS + + return FMOD_OK; +} + +FMOD_RESULT F_CALLBACK AudioClip::ScriptPCMSetPositionCallback( + FMOD_SOUND * sound, + int subsound, + unsigned int position, + FMOD_TIMEUNIT postype) +{ +#if SUPPORT_MONO_THREADS || UNITY_WINRT + Assert(postype == FMOD_TIMEUNIT_PCM); + + FMOD::Sound* pSound = (FMOD::Sound*) sound; + AudioClip* ac = NULL; + pSound->getUserData((void**)&ac); + + Assert (ac); +#if ENABLE_MONO + ScopedThreadAttach attach(ac->monoDomain); +#endif + + // invoke + ScriptingObjectPtr instance = Scripting::ScriptingWrapperFor(ac); + + ScriptingExceptionPtr exception; + ScriptingInvocation invocation(ac->m_CachedSetPositionCallbackMethod); + invocation.AddInt(position); + invocation.object = instance; + invocation.Invoke(&exception); + + if (exception) + { + // handle + Scripting::LogException(exception, Scripting::GetInstanceIDFromScriptingWrapper(instance)); + } +#endif // SUPPORT_MONO_THREADS + + return FMOD_OK; +} + +#endif //ENABLE_AUDIO + diff --git a/Runtime/Audio/AudioClip.cpp b/Runtime/Audio/AudioClip.cpp new file mode 100644 index 0000000..2f1f719 --- /dev/null +++ b/Runtime/Audio/AudioClip.cpp @@ -0,0 +1,1346 @@ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" + +#if ENABLE_AUDIO_FMOD + +#include "AudioClip.h" +#include "AudioSource.h" +#include "Utilities/Conversion.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Serialize/SwapEndianArray.h" +#include "AudioManager.h" +#include "Runtime/Video/MoviePlayback.h" +#include "Runtime/Serialize/PersistentManager.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Misc/UTF8.h" +#include "Runtime/Scripting/ScriptingManager.h" +#include "Runtime/Utilities/PathNameUtility.h" + +#include "WavReader.h" +#include "OggReader.h" + +#if UNITY_EDITOR //in editor, include nonfmod vorbis first, to make sure we actually use that, and not the fmod version. +#include "../../External/Audio/libvorbis/include/vorbis/codec.h" +#include "Editor/Src/EditorUserBuildSettings.h" +#include "Editor/Src/AssetPipeline/AudioImporter.h" +#endif + +#include "Runtime/Audio/correct_fmod_includer.h" + +#if ENABLE_WWW +#include "Runtime/Export/WWW.h" +#endif + +#include "Runtime/Misc/BuildSettings.h" + +#if UNITY_EDITOR +#include <fstream> +#endif + + +//kMono8 = 1, kMono16, kStereo8, kStereo16, kOggVorbis +static FMOD_SOUND_FORMAT FORMAT_TO_FMOD_FORMAT[] = { FMOD_SOUND_FORMAT_NONE , FMOD_SOUND_FORMAT_PCM8, FMOD_SOUND_FORMAT_PCM16, FMOD_SOUND_FORMAT_PCM8, FMOD_SOUND_FORMAT_PCM16, FMOD_SOUND_FORMAT_PCM16 }; +static FMOD_SOUND_TYPE FORMAT_TO_FMOD_TYPE[] = {FMOD_SOUND_TYPE_UNKNOWN, FMOD_SOUND_TYPE_RAW, FMOD_SOUND_TYPE_RAW, FMOD_SOUND_TYPE_RAW, FMOD_SOUND_TYPE_RAW, FMOD_SOUND_TYPE_OGGVORBIS }; + + +#if ENABLE_PROFILER +int AudioClip::s_AudioClipCount = 0; +#endif + + +void AudioClip::InitializeClass () +{ + #if UNITY_EDITOR + RegisterAllowNameConversion (AudioClip::GetClassStringStatic(), "m_UseRuntimeDecompression", "m_DecompressOnLoad"); + #endif +} + +AudioClip::AudioClip (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode), +m_Channels(0), +m_Sound(NULL), +m_Frequency(0), +m_BitsPerSample(0), +m_3D(true), +m_UseHardware(false), +m_Format(FMOD_SOUND_FORMAT_NONE), +m_Type(FMOD_SOUND_TYPE_UNKNOWN), +#if UNITY_EDITOR +m_EditorSoundType(FMOD_SOUND_TYPE_UNKNOWN), +m_EditorSoundFormat(FMOD_SOUND_FORMAT_NONE), +#endif +#if ENABLE_WWW +m_StreamData(NULL), +m_ReadAllowed(true), +#endif +m_ExternalStream(false), +m_MoviePlayback(NULL), +m_OpenState(FMOD_OPENSTATE_CONNECTING), +m_LoadFlag(kDecompressOnLoad), +m_WWWStreamed(false), +m_PCMArray(SCRIPTING_NULL), +m_PCMArrayGCHandle(0), +m_CachedSetPositionCallbackMethod(SCRIPTING_NULL), +m_CachedPCMReaderCallbackMethod(SCRIPTING_NULL), +m_DecodeBufferSize(0), +#if ENABLE_MONO +monoDomain(NULL), +#endif +m_UserGenerated(false), +m_UserLengthSamples(0), +m_UserIsStream(true), +m_AudioData(kMemAudio) +{ +#if ENABLE_PROFILER + s_AudioClipCount++; +#endif +} + +#if ENABLE_WWW +bool AudioClip::InitStream (WWW* streamData, MoviePlayback* movie, bool realStream /* = false */, FMOD_SOUND_TYPE fmodSoundType /* = FMOD_SOUND_TYPE_UNKNOWN */) +{ + AssertIf(m_MoviePlayback != NULL); + AssertIf(m_StreamData != NULL); + + // Web streaming + if (streamData) + { + // If the audiotype isn't specified, guess the audio type from the url to avoid going thru all available codecs (this seriously hit performance for a net stream) + std::string ext = ToLower(GetPathNameExtension(streamData->GetUrl())); + + if (fmodSoundType == FMOD_SOUND_TYPE_UNKNOWN) + m_Type = GetFormatFromExtension(ext); + else + m_Type = fmodSoundType; + + if (m_Type == FMOD_SOUND_TYPE_UNKNOWN) + { + ErrorStringObject(Format("Unable to determine the audio type from the URL (%s) . Please specify the type.", streamData->GetUrl() ), this); + // right now we're trying to load sound in AwakeFromLoad (and do only that) - so we skip the call + HackSetAwakeWasCalled(); + return false; + } + + if (realStream && IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_1_a3)) + { + if(m_Type == FMOD_SOUND_TYPE_XM || m_Type == FMOD_SOUND_TYPE_IT || m_Type == FMOD_SOUND_TYPE_MOD || m_Type == FMOD_SOUND_TYPE_S3M) + { + ErrorStringObject("Tracker files (XM/IT/MOD/S3M) cannot be streamed in realtime but must be fully downloaded before they can play.", this); + HackSetAwakeWasCalled(); // to avoid error message: "Awake has not been called '' (AudioClip). Figure out where the object gets created and call AwakeFromLoad correctly." + return false; + } + } + + if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1)) + { +#if UNITY_EDITOR + BuildTargetPlatform targetPlatform = GetEditorUserBuildSettings().GetActiveBuildTarget (); + if ( + (m_Type == FMOD_SOUND_TYPE_OGGVORBIS && ((targetPlatform == kBuild_Android)||(targetPlatform == kBuild_iPhone)||(targetPlatform == kBuildBB10)||(targetPlatform == kBuildTizen))) + || + (m_Type == FMOD_SOUND_TYPE_MPEG && !((targetPlatform == kBuild_Android)||(targetPlatform == kBuild_iPhone)||(targetPlatform == kBuildBB10)||(targetPlatform == kBuildTizen))) + ) +#else + if ( + (m_Type == FMOD_SOUND_TYPE_OGGVORBIS && (UNITY_ANDROID | UNITY_IPHONE | UNITY_BB10 | UNITY_TIZEN) != 0) + || + (m_Type == FMOD_SOUND_TYPE_MPEG && (UNITY_ANDROID | UNITY_IPHONE | UNITY_BB10 | UNITY_TIZEN) == 0) + ) +#endif + { + ErrorStringObject(Format("Streaming of '%s' on this platform is not supported", ext.c_str()), this); + // right now we're trying to load sound in AwakeFromLoad (and do only that) - so we skip the call + HackSetAwakeWasCalled(); + return false; + } + } + + m_StreamData = streamData; + m_StreamData->SetAudioClip( this ); + m_StreamData->Retain(); // Make sure the WWW object doesn't dissappear if the mono side of the WWW object is deleted before we are done. + m_ExternalStream = true; + m_WWWStreamed = realStream; + // reserve queue space + m_AudioQueueMutex.Lock(); + m_AudioBufferQueue.reserve(kAudioQueueSize); + m_AudioQueueMutex.Unlock(); + LoadSound(); + } + + m_MoviePlayback = movie; + + if (movie) + { + m_ExternalStream = true; + LoadSound(); + } + + #if !UNITY_RELEASE + // right now we're trying to load sound in AwakeFromLoad (and do only that) - so we skip the call + HackSetAwakeWasCalled(); + #endif + + return true; +} +#endif + +bool AudioClip::CreateUserSound(const std::string& name, unsigned lengthSamples, short channels, unsigned frequency, bool _3D, bool stream) +{ + Reset(); + Cleanup(); + + m_UserGenerated = true; + m_UserLengthSamples = lengthSamples; + m_UserIsStream = stream; + m_Channels = channels; + m_Frequency = frequency; + m_3D = _3D; + m_Format = FMOD_SOUND_FORMAT_PCMFLOAT; + m_BitsPerSample = 32; + SetName(name.c_str()); + + CreateScriptCallback(); + + m_Sound = CreateSound(); + +#if !UNITY_RELEASE + // right now we're trying to load sound in AwakeFromLoad (and do only that) - so we skip the call + HackSetAwakeWasCalled(); +#endif + + return true; +} + +bool AudioClip::InitWSound (FMOD::Sound* sound) +{ + Assert(sound); + Cleanup(); + CreateScriptCallback(); + m_Sound = sound; + m_OpenState = FMOD_OPENSTATE_READY; + GetSoundProps(); + sound->setUserData((void*)this); + + return true; +} + + + +AudioClip::~AudioClip () +{ +#if ENABLE_PROFILER + s_AudioClipCount--; +#endif + + Cleanup(); +#if ENABLE_WWW + if (m_StreamData) + { + m_StreamData->SetAudioClip( NULL ); + m_StreamData->Release(); + } +#endif + if (m_MoviePlayback) + m_MoviePlayback->SetMovieAudioClip(NULL); +} + +FMOD_SOUND_TYPE AudioClip::GetFormatFromExtension(const std::string& ext) +{ + std::string ext_lower = ToLower(ext); + FMOD_SOUND_TYPE type = FMOD_SOUND_TYPE_UNKNOWN; + if (ext_lower == "ogg") + type = FMOD_SOUND_TYPE_OGGVORBIS; + else + if (ext_lower == "mp2" || ext_lower == "mp3") + type = FMOD_SOUND_TYPE_MPEG; + else + if (ext_lower == "wav") + type = FMOD_SOUND_TYPE_WAV; + else + if (ext_lower == "it") + type = FMOD_SOUND_TYPE_IT; + else + if (ext_lower == "xm") + type = FMOD_SOUND_TYPE_XM; + else + if (ext_lower == "s3m") + type = FMOD_SOUND_TYPE_S3M; + else + if (ext_lower == "mod") + type = FMOD_SOUND_TYPE_MOD; + + return type; +} + +bool AudioClip::IsFormatSupportedByPlatform(const std::string& ext) +{ + FMOD_SOUND_TYPE type = GetFormatFromExtension(ext); + if(type == FMOD_SOUND_TYPE_UNKNOWN) + return false; + if(type == FMOD_SOUND_TYPE_OGGVORBIS && (UNITY_IPHONE || UNITY_ANDROID || UNITY_BB10 || UNITY_TIZEN)) + return false; + if(type == FMOD_SOUND_TYPE_MPEG && !UNITY_IPHONE && !UNITY_ANDROID && !UNITY_BB10 && !UNITY_TIZEN) + return false; + return true; +} + +void AudioClip::Cleanup() +{ + if (!m_CachedSounds.empty()) + { + for (TFMODSounds::iterator it = m_CachedSounds.begin(); it != m_CachedSounds.end(); ++it) + { + if ((*it).second) + (*it).second->stop(); + if ((*it).first) + (*it).first->release(); + } + m_CachedSounds.clear(); + } + else + { + if (m_Sound) + { + m_Sound->release(); + } + } + + m_Sound = NULL; +} + + +void AudioClip::CleanupClass() +{ + +} + + +bool AudioClip::ReadyToPlay () +{ + if( +#if ENABLE_WWW + !m_StreamData && +#endif + !m_MoviePlayback) + return true; + + if (m_OpenState == FMOD_OPENSTATE_READY) + return true; + + + // try to... + LoadSound(); + + if (!m_Sound) + { + m_OpenState = FMOD_OPENSTATE_CONNECTING; + return false; + } + + m_OpenState = FMOD_OPENSTATE_READY; + + return true; +} + +void AudioClip::SetData(const float* data, unsigned lengthSamples, unsigned offsetSamples /*= 0*/) +{ + if (m_Sound) + { + if (IsFMODStream()) + { + ErrorStringObject("Cannot set data on streamed sample", this); + return; + } + + //Address of a pointer that will point to the first part of the locked data. + void *ptr1 = NULL; + + //Address of a pointer that will point to the second part of the locked data. This will be null if the data locked hasn't wrapped at the end of the buffer. + void *ptr2 = NULL; + + //Length of data in bytes that was locked for ptr1 + unsigned len1 = 0; + + // Length of data in bytes that was locked for ptr2. This will be 0 if the data locked hasn't wrapped at the end of the buffer. + unsigned len2 = 0; + + unsigned samplesToCopy = lengthSamples; + unsigned clipSampleCount = GetSampleCount(); + if (lengthSamples > clipSampleCount) + { + WarningString(Format("Data too long to fit the audioclip: %s. %i sample(s) discarded", GetName(), (lengthSamples - clipSampleCount))); + samplesToCopy = clipSampleCount; + } + + //Offset in bytes to the position you want to lock in the sample buffer. + int offsetBytes = offsetSamples * m_Channels * (m_BitsPerSample / 8); + + //Number of bytes you want to lock in the sample buffer. + int lengthBytes = samplesToCopy * m_Channels * (m_BitsPerSample / 8); + + FMOD_RESULT result = m_Sound->lock(offsetBytes, lengthBytes, &ptr1, &ptr2, &len1, &len2); + + FMOD_ASSERT(result); + + if (ptr2 == NULL) + { + ArrayFromNormFloat(m_Format, data, data + samplesToCopy * m_Channels, ptr1); + } + else // wrap + { + ArrayFromNormFloat(m_Format, data, data + (len1 / sizeof(float)), ptr1); + ArrayFromNormFloat(m_Format, data + len1 / sizeof(float), data + (len1 + len2) / sizeof(float), ptr2); + } + + m_Sound->unlock(ptr1, ptr2, len1, len2); + } +} + + +void AudioClip::GetData(float* data, unsigned lengthSamples, unsigned offsetSamples /*= 0*/) const +{ + if (m_Sound) + { + if (IsFMODStream()) + { + ErrorStringObject("Cannot get data from streamed sample", this); + return; + } + + //Address of a pointer that will point to the first part of the locked data. + void *ptr1 = NULL; + + //Address of a pointer that will point to the second part of the locked data. This will be null if the data locked hasn't wrapped at the end of the buffer. + void *ptr2 = NULL; + + //Length of data in bytes that was locked for ptr1 + unsigned len1 = 0; + + // Length of data in bytes that was locked for ptr2. This will be 0 if the data locked hasn't wrapped at the end of the buffer. + unsigned len2 = 0; + + unsigned samplesToCopy = lengthSamples; + unsigned clipSampleCount = GetSampleCount(); + if (lengthSamples > clipSampleCount) + { + WarningString(Format("Data longer than the AudioClip: %s. %i sample(s) copied", GetName(), clipSampleCount)); + samplesToCopy = clipSampleCount; + } + + //Offset in bytes to the position you want to lock in the sample buffer. + int offsetBytes = offsetSamples * m_Channels * (m_BitsPerSample / 8); + + //Number of bytes you want to lock in the sample buffer. + int lengthBytes = samplesToCopy * m_Channels * (m_BitsPerSample / 8); + + unsigned totalLengthBytes; + m_Sound->getLength(&totalLengthBytes, FMOD_TIMEUNIT_PCMBYTES); + Assert ( totalLengthBytes >= lengthBytes ); + + FMOD_RESULT result = m_Sound->lock(offsetBytes, lengthBytes, &ptr1, &ptr2, &len1, &len2); + + FMOD_ASSERT(result); + + if (ptr2 == NULL) + { + ArrayToNormFloat(m_Format, ptr1, ((char*)ptr1 + len1), data); + } + else // wrap + { + if (len1 + len2 > lengthBytes) + { + WarningString(Format("Array can not hold the number of samples (%d)", (len1 + len2) - lengthBytes)); + } + else + { + ArrayToNormFloat(m_Format, ptr1, ((char*)ptr1 + len1), data); + ArrayToNormFloat(m_Format, ptr2, ((char*)ptr2 + len2), data + (len1 / 4)); + } + } + + m_Sound->unlock(ptr1, ptr2, len1, len2); + } +} + + +// Set the attached movie clip +void AudioClip::SetMoviePlayback(MoviePlayback* movie) +{ + m_MoviePlayback = movie; + + if (!movie) + return; + + m_ExternalStream = true; + + // unload any attached www data +#if ENABLE_WWW + if (m_StreamData) + m_StreamData->Release(); + m_StreamData = NULL; +#endif + + m_Channels = movie->GetMovieAudioChannelCount(); + m_Frequency = movie->GetMovieAudioRate(); + m_Format = FMOD_SOUND_FORMAT_PCM16; + m_Type = FMOD_SOUND_TYPE_RAW; + m_BitsPerSample = 16; + + m_OpenState = FMOD_OPENSTATE_CONNECTING; +} + + +MoviePlayback* AudioClip::GetMoviePlayback() const +{ + return m_MoviePlayback; +} + + +/** + * Queue PCM data into clip + * This is read by streamed sound + * @param buffer Audio data to queue + * @param size Size of Audio data + **/ +bool AudioClip::QueueAudioData(void* buffer, unsigned size) +{ + Mutex::AutoLock lock (m_AudioQueueMutex); + if (m_AudioBufferQueue.size() + size < kAudioQueueSize) + { + m_AudioBufferQueue.insert ( m_AudioBufferQueue.end(), (UInt8*)buffer, (UInt8*)buffer+size ); + return true; + } + + return false; +} + + +/** + * Top audio data from the quene + * @note buf must allocate size bytes + * @param size The size in bytes you want to top + * @return The audio data + **/ +bool AudioClip::GetQueuedAudioData(void** buf, unsigned size) +{ + Mutex::AutoLock lock(m_AudioQueueMutex); + if (m_AudioBufferQueue.size() < size) + return false; + + memcpy( *buf, &m_AudioBufferQueue[0], size); + + m_AudioBufferQueue.erase(m_AudioBufferQueue.begin(), m_AudioBufferQueue.begin() + size); + + return true; +} + + +void AudioClip::ClearQueue() +{ + Mutex::AutoLock lock(m_AudioQueueMutex); + + m_AudioBufferQueue.clear(); +} + + + +unsigned int AudioClip::GetSampleCount() const +{ + if (m_MoviePlayback) + { + if (m_MoviePlayback->GetMovieTotalDuration() < 0) + return 0; + else + return (unsigned int)(m_MoviePlayback->GetMovieTotalDuration() * m_Frequency * m_Channels); + } + + if (m_Sound == NULL) + return 0; + unsigned int sc = 0; + m_Sound->getLength(&sc, FMOD_TIMEUNIT_PCM); + return sc; +} + + +unsigned int AudioClip::GetChannelCount() const +{ + return m_Channels; +} +unsigned int AudioClip::GetBitRate() const +{ + return m_Frequency * m_BitsPerSample; +} + +unsigned int AudioClip::GetBitsPerSample() const +{ + return m_BitsPerSample; +} + +unsigned int AudioClip::GetFrequency() const +{ + return m_Frequency; +} + + +int AudioClip::GetRuntimeMemorySize() const +{ + return Super::GetRuntimeMemorySize() + GetSize(); +} + +// Return the total memory used by this audioclip for the current target +unsigned int AudioClip::GetSize() const +{ + unsigned totalSize = 0; + unsigned fmodSize = 0; + if (m_Sound) m_Sound->getMemoryInfo(FMOD_MEMBITS_ALL, 0, &fmodSize, NULL); + + // compressed in memory + if ( m_LoadFlag == kCompressedInMemory ) + { + switch (m_Type) { + case FMOD_SOUND_TYPE_XMA: + case FMOD_SOUND_TYPE_GCADPCM: + totalSize += m_AudioData.capacity(); + break; + case FMOD_SOUND_TYPE_OGGVORBIS: + totalSize += m_AudioData.capacity(); // stream from memory + default: + totalSize += fmodSize; + break; + } + } + else + if ( m_LoadFlag == kDecompressOnLoad ) + // decompressed + { + totalSize += fmodSize; + } + else + // stream from disc + if ( m_LoadFlag == kStreamFromDisc ) + { + unsigned streamBufferSize = 0; + GetAudioManager().GetFMODSystem()->getStreamBufferSize(&streamBufferSize, NULL); + totalSize += streamBufferSize; + } + + return totalSize; +} + +unsigned int AudioClip::GetLength() const +{ + if (m_MoviePlayback) + return (unsigned int)(m_MoviePlayback->GetMovieTotalDuration() * 1000.0f); + + if (m_Sound == NULL) + return 0; + unsigned int sc = 0; + m_Sound->getLength(&sc, FMOD_TIMEUNIT_MS); + return sc; +} + +float AudioClip::GetLengthSec() const +{ + if (m_MoviePlayback) + return m_MoviePlayback->GetMovieTotalDuration(); + + if (m_Sound == NULL) + return 0; + unsigned int sc = 0; + m_Sound->getLength(&sc, FMOD_TIMEUNIT_MS); + return sc / 1000.f; +} + +MoviePlayback* AudioClip::GetMovie() const +{ + return m_MoviePlayback; +} + + +bool AudioClip::IsOggCompressible() const +{ + return (m_Type != FMOD_SOUND_TYPE_MOD) && + (m_Type != FMOD_SOUND_TYPE_S3M) && + (m_Type != FMOD_SOUND_TYPE_MIDI) && + (m_Type != FMOD_SOUND_TYPE_XM) && + (m_Type != FMOD_SOUND_TYPE_IT) && + (m_Type != FMOD_SOUND_TYPE_SF2) && + ((m_Type == FMOD_SOUND_TYPE_WAV) && + (m_Format != FMOD_SOUND_FORMAT_PCM32)); +} + +bool AudioClip::IsFMODStream () const +{ + bool isStream = false; + + if (m_Sound) + { + FMOD_MODE mode; + m_Sound->getMode(&mode); + isStream = (mode & FMOD_CREATESTREAM) != 0; + } + + return isStream; +} + + +FMOD::Channel* AudioClip::GetCachedChannel() +{ + // Assert this is a stream + Assert(GetMode() & FMOD_CREATESTREAM); + + TFMODSounds::iterator it = m_CachedSounds.begin(); + + for (; it != m_CachedSounds.end(); ++it) + { + m_Sound = (*it).first; + FMOD::Channel* channel = (*it).second; + FMOD_RESULT result = FMOD_OK; + bool isPlaying = false, isPaused = false; + if (channel) + { + result = channel->isPlaying(&isPlaying); + result = channel->getPaused(&isPaused); + } + + if (!isPlaying && !isPaused) + { + if (channel != NULL) + { + // Detach from old audiosource + AudioSource* audioSource = AudioSource::GetAudioSourceFromChannel( channel ); + if (audioSource) + { + audioSource->Stop(channel); + result = GetAudioManager().GetFMODSystem()->playSound(FMOD_CHANNEL_FREE, m_Sound, true, &channel); + } + else + result = GetAudioManager().GetFMODSystem()->playSound(FMOD_CHANNEL_FREE, m_Sound, true, &channel); + } + else + { + result = GetAudioManager().GetFMODSystem()->playSound(FMOD_CHANNEL_FREE, m_Sound, true, &channel); + } + + Assert (result == FMOD_OK); + (*it).second = channel; + + if (channel) + return channel; + } + } + + // no sounds available in the pool - create one + m_Sound = CreateSound(); + + if (m_Sound) + { + FMOD::Channel* channel = GetAudioManager().GetFreeFMODChannel( m_Sound ); + if (channel != NULL) + m_CachedSounds.push_back( std::make_pair( m_Sound, channel ) ); + return channel; + } + + return NULL; +} + +#if UNITY_IPHONE +// IPHONE hardware +// Rationale: Lazy create sound on channel request (Play()) to not hold onto the hw decoder more than needed, +// if the sound is already created, then release it, to give it a chance to get the hw decoder upon create. +// if another stream is owing the hw decoder, fallback on software (CreateFMODSound() handles that) +// This add some latency on Play (but that's acceptable for streams) +FMOD::Channel* AudioClip::CreateIOSStreamChannel() +{ + FMOD::Channel* channel = NULL; + if (m_UseHardware) + { + if (m_Sound) m_Sound->release(); + if (!m_StreamingInfo.IsValid()) + { + m_Sound = GetAudioManager().CreateFMODSound((const char*)&m_AudioData[0], GetExInfo(), GetMode(), m_UseHardware); + } + else + { + m_Sound = GetAudioManager().CreateFMODSound(m_StreamingInfo.path.c_str(), GetExInfo(), GetMode(), m_UseHardware); + } + + Assert(m_Sound); + channel = GetAudioManager().GetFreeFMODChannel(m_Sound); + } + else + channel = GetCachedChannel(); + + return channel; +} +#endif // UNITY_IPHONE + +FMOD::Channel* AudioClip::CreateChannel(AudioSource* forSource) +{ + FMOD::Channel* channel = NULL; + if (GetMode() & FMOD_CREATESTREAM) + { +#if ENABLE_WWW + if (m_WWWStreamed) + { + // Recreate sound if it's streamed WWW/Custom (reusing a sound will trigger a seek - which is not supported) + if (m_Sound) + m_Sound->release(); + m_Sound = CreateSound(); + Assert(m_Sound); + channel = GetAudioManager().GetFreeFMODChannel(m_Sound); + } + else +#endif // ENABLE_WWW +#if UNITY_IPHONE + channel = CreateIOSStreamChannel(); +#else // UNITY_IPHONE + channel = GetCachedChannel(); +#endif // UNITY_IPHONE + } + else + { + channel = GetAudioManager().GetFreeFMODChannel(m_Sound); + } + + if (channel) channel->setMode(Is3D()?FMOD_3D:FMOD_2D); + + return channel; +} + +FMOD_CREATESOUNDEXINFO AudioClip::GetExInfo() const +{ + FMOD_CREATESOUNDEXINFO exinfo; + memset(&exinfo, 0, sizeof(exinfo)); + exinfo.cbsize = sizeof(FMOD_CREATESOUNDEXINFO); +#if UNITY_EDITOR + exinfo.suggestedsoundtype = m_EditorAudioData.empty()?m_Type:m_EditorSoundType; + exinfo.format = m_EditorAudioData.empty()?m_Format:m_EditorSoundFormat; + exinfo.length = m_EditorAudioData.empty()?m_AudioData.size():m_EditorAudioData.size(); +#else + exinfo.suggestedsoundtype = m_Type; + exinfo.format = m_Format; + exinfo.length = m_AudioData.size(); +#endif + + exinfo.defaultfrequency = m_Frequency; + exinfo.numchannels = m_Channels; + if (m_UserGenerated) + { + exinfo.length = m_UserLengthSamples * m_Channels * 4; + exinfo.pcmreadcallback = AudioClip::ScriptPCMReadCallback; + exinfo.pcmsetposcallback = AudioClip::ScriptPCMSetPositionCallback; + } + + if (m_StreamingInfo.IsValid()) + { + exinfo.length = m_StreamingInfo.size; + exinfo.fileoffset = m_StreamingInfo.offset; + } + + exinfo.userdata = (void*)this; + + return exinfo; +} + +FMOD_MODE AudioClip::GetMode() const +{ + FMOD_MODE mode = (m_3D?FMOD_3D:FMOD_2D) | FMOD_LOOP_NORMAL | FMOD_3D_CUSTOMROLLOFF | FMOD_MPEGSEARCH; + +#if UNITY_WII + mode |= (m_UseHardware || m_Type == FMOD_SOUND_TYPE_GCADPCM) ? FMOD_HARDWARE : FMOD_SOFTWARE; +#else + mode |= FMOD_SOFTWARE; +#endif + + + if (m_ExternalStream) + { + mode |= FMOD_CREATESTREAM; + } + else + if (m_UserGenerated) + { + mode |= FMOD_OPENUSER; + if (m_UserIsStream) + mode |= FMOD_CREATESTREAM; + } + else + if (m_StreamingInfo.IsValid()) + mode |= FMOD_CREATESTREAM; + else + { + mode |= FMOD_OPENMEMORY; + + if (m_LoadFlag == kCompressedInMemory) + { + // if MP2/MP3, ADPCM, CELT, XMA then we can load the audio compressed directly into FMOD + if (m_Type == FMOD_SOUND_TYPE_MPEG || + m_Type == FMOD_SOUND_TYPE_XMA || + m_Type == FMOD_SOUND_TYPE_GCADPCM || + m_Type == FMOD_SOUND_TYPE_WAV || + m_Type == FMOD_SOUND_TYPE_AIFF || + m_Type == FMOD_SOUND_TYPE_IT || + m_Type == FMOD_SOUND_TYPE_XM || + m_Type == FMOD_SOUND_TYPE_S3M || + m_Type == FMOD_SOUND_TYPE_MOD ) + { + // From docs - "...Can only be used in combination with FMOD_SOFTWARE...." + if ((mode & FMOD_SOFTWARE) != 0) mode |= FMOD_CREATECOMPRESSEDSAMPLE; + } + else + // if sound is ogg we have to stream it to FMOD to keep it compressed in memory + // @TODO use CELT instead of OGG. Soon. + { + mode |= FMOD_CREATESTREAM; + } + } + } + + if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1)) + { + if(m_Type == FMOD_SOUND_TYPE_MPEG || + m_Type == FMOD_SOUND_TYPE_MOD || + m_Type == FMOD_SOUND_TYPE_IT || + m_Type == FMOD_SOUND_TYPE_S3M || + m_Type == FMOD_SOUND_TYPE_XM) + { + mode |= FMOD_ACCURATETIME; + } + } + + return mode; +} + +const UInt8* AudioClip::GetAudioData() const +{ +#if UNITY_EDITOR + if (!m_EditorAudioData.empty()) + return &m_EditorAudioData[0]; + else +#endif + return &m_AudioData[0]; +} + +FMOD::Sound* AudioClip::CreateSound() +{ + // if external streaming (WWW or movie) + if (m_ExternalStream) + { +#if ENABLE_WWW + if (m_StreamData) + { + // Wait for the entire file to download before reporting ready + // @TODO we need a proper net stream solution + if (!m_WWWStreamed && ( m_StreamData->GetProgress() != 1.0f )) + return NULL; + + return GetAudioManager().CreateFMODSoundFromWWW(m_StreamData, + m_3D, m_Type, m_Format, m_Frequency, m_Channels, m_WWWStreamed); + } + else +#endif + if (m_MoviePlayback) + { + m_Frequency = m_MoviePlayback->GetMovieAudioRate(); + m_Channels = m_MoviePlayback->GetMovieAudioChannelCount(); + if (m_Frequency > 0 && m_Channels > 0) + return GetAudioManager().CreateFMODSoundFromMovie(this, m_3D); + } + } + else + { + if (m_UserGenerated) + { + m_Sound = GetAudioManager().CreateFMODSound(GetName(), GetExInfo(), GetMode(), m_UseHardware); + } + else + if (!m_StreamingInfo.IsValid()) + { + if (m_AudioData.empty()) + return NULL; + + FMOD_MODE mode = GetMode(); + + m_Sound = GetAudioManager().CreateFMODSound(GetAudioData(), GetExInfo(), mode, m_UseHardware); + + // Audio data is only loaded into FMOD once (if its not a stream). We can clear it after upload +#if !UNITY_EDITOR + if (!(mode & FMOD_CREATESTREAM)) + m_AudioData.clear(); +#endif + } + else + { + // assert that no audio data is loaded in memory + Assert (m_AudioData.empty()); + + m_Sound = GetAudioManager().CreateFMODSound(m_StreamingInfo.path.c_str(), GetExInfo(), GetMode(), m_UseHardware); + } + } + + return m_Sound; +} + +void AudioClip::GetSoundProps() +{ + if (!m_Sound) + return; + + m_Sound->getFormat(m_Type == FMOD_SOUND_TYPE_UNKNOWN? &m_Type : NULL, m_Format == FMOD_SOUND_FORMAT_NONE ? &m_Format : NULL, &m_Channels, &m_BitsPerSample); + + float ffrequency; + m_Sound->getDefaults( &ffrequency,NULL, NULL, NULL); + + m_Frequency = (int)ffrequency; + + FMOD_MODE mode; + m_Sound->getMode(&mode); + m_3D = (mode & FMOD_3D); +} + +void AudioClip::Reload() +{ + if (m_AudioData.size() == 0) + { + GetPersistentManager().ReloadFromDisk(this); + // FYI: ReloadFromDisk() will call LoadSound() and calling LoadSound() twice is a bad thing. + } + else + LoadSound(); +} + +bool AudioClip::LoadSound() +{ + Cleanup(); + + AssertBreak(m_Sound == NULL); + m_Sound = CreateSound(); + + if (m_Sound == NULL) + { + return false; + } + + m_OpenState = FMOD_OPENSTATE_READY; + + if (m_ExternalStream&&!m_Sound) + { + m_OpenState = FMOD_OPENSTATE_CONNECTING; + return false; + } + + if ((GetMode()&FMOD_CREATESTREAM)&&m_Sound&&!m_UseHardware&&!m_WWWStreamed) + { + m_CachedSounds.push_back( std::make_pair( m_Sound, (FMOD::Channel*)NULL) ); + } + + GetSoundProps(); + +#if UNITY_IPHONE + // if it's a stream and it uses hardware, we release the sound here to relinqiush the hw decoder. + if (m_UseHardware&&(GetMode()&FMOD_CREATESTREAM)) + { + m_Sound->release(); + m_Sound = NULL; + } +#endif + + + return true; +} + +void AudioClip::ReleaseSound() +{ + if (m_Sound) + m_Sound->release(); + m_Sound = NULL; +} + + +bool AudioClip::SetAudioDataSwap(dynamic_array<UInt8>& buffer, + bool threeD, + bool hardware, + int loadFlag, + bool externalStream, + FMOD_SOUND_TYPE type, + FMOD_SOUND_FORMAT format) +{ + m_ExternalStream = externalStream; + m_LoadFlag = loadFlag; + m_3D = threeD; + m_UseHardware = hardware; + m_Type = type; + m_Format = format; + Assert(buffer.owns_data()); + m_AudioData.swap(buffer); + + #if !UNITY_RELEASE + // right now we're trying to load sound in AwakeFromLoad (and do only that) - so we skip the call + HackSetAwakeWasCalled(); + #endif + + return LoadSound(); +} + + +bool AudioClip::SetAudioData(const UInt8* buffer, + unsigned size, + bool threeD, + bool hardware, + int loadFlag, + bool externalStream, + FMOD_SOUND_TYPE type, + FMOD_SOUND_FORMAT format) +{ + m_ExternalStream = externalStream; + m_LoadFlag = loadFlag; + m_3D = threeD; + m_UseHardware = hardware; + m_Type = type; + m_Format = format; + m_AudioData.assign(buffer, buffer + size); + Assert (m_AudioData.size() == size); + + #if !UNITY_RELEASE + // right now we're trying to load sound in AwakeFromLoad (and do only that) - so we skip the call + HackSetAwakeWasCalled(); + #endif + + return LoadSound(); +} + + + +#if UNITY_EDITOR + +void AudioClip::SetEditorAudioData( const dynamic_array<UInt8>& buffer, FMOD_SOUND_TYPE type, FMOD_SOUND_FORMAT format ) +{ + m_EditorAudioData.assign(&buffer[0], &buffer[0] + buffer.size()); + m_EditorSoundType = type; + m_EditorSoundFormat = format; +} + +bool AudioClip::WriteRawDataToFile( const string& path ) const +{ + // first load entire file into byte array + // open the file + std::ofstream file(path.c_str(), std::ios::binary); + + if(!file.is_open()) + { + return false; // file problably doesnt exist + } + + file.write((const char*)&m_AudioData[0], m_AudioData.size()); + + if (!file.good()) + { + file.close(); + return false; + } + + file.close(); + + return true; +} +#endif + +void AudioClip::ConvertOldAsset(int frequency, int size, int channels, int bitsPerSample, UInt8* raw) +{ + UInt8* data; + UInt8* wav = CreateWAV(frequency, size, channels, bitsPerSample, &data); + + // get header info + const int wav_size = GetWAVSize(wav); + + // copy in raw data + memcpy(data, raw, size); + + m_AudioData.clear(); + m_AudioData.assign( wav, wav + wav_size); + Assert ( wav_size == m_AudioData.size() ); + m_Channels = channels; + m_Frequency = frequency; + m_Format = FMOD_SOUND_FORMAT_PCM16; + m_Type = FMOD_SOUND_TYPE_WAV; + + delete[] wav; +} + + +void AudioClip::CreateScriptCallback() +{ + #if ENABLE_SCRIPTING + #if ENABLE_MONO + monoDomain = mono_domain_get(); + #endif + + // cache script methods + ScriptingObjectPtr instance = Scripting::ScriptingWrapperFor(this); + + if (instance) + { + // cache delegate invokers + m_CachedPCMReaderCallbackMethod = GetScriptingMethodRegistry().GetMethod( "UnityEngine", "AudioClip", "InvokePCMReaderCallback_Internal" ); + m_CachedSetPositionCallbackMethod = GetScriptingMethodRegistry().GetMethod ( "UnityEngine", "AudioClip", "InvokePCMSetPositionCallback_Internal" ); + } + #endif +} + + +template<class TransferFunc> +void AudioClip::TransferToFlash(TransferFunc& transfer) +{ + Assert(transfer.IsWriting()); + int count = GetSampleCount(); + transfer.Transfer(count, "samplecount"); + transfer.Transfer (m_3D, "m_3D"); + if (transfer.GetBuildingTarget().platform==kBuildWebGL) + // Since we can't do unaligned + transfer.Align(); + transfer.Transfer(m_AudioData, "m_AudioData"); + +} + +template<class TransferFunc> +void AudioClip::Transfer (TransferFunc& transfer) { + Super::Transfer (transfer); + + //if we're exporting to flash, write out data completely differently + if (transfer.IsWritingGameReleaseData () && (transfer.GetBuildingTarget().platform==kBuildFlash || transfer.GetBuildingTarget().platform==kBuildWebGL)) + { + TransferToFlash(transfer); + return; + } + + // 10/06-11: v4: Added editor only audio data + // 25/05-10: v4: Stream replaces decompressOnLoad + // 18/04-09: v3: FMOD asset data + // : v1-2: OpenAL asset data + transfer.SetVersion (4); + + transfer.Transfer ((SInt32&)m_Format, "m_Format", kNotEditableMask); + + if (transfer.IsCurrentVersion()) + { + transfer.Transfer ((SInt32&)m_Type, "m_Type", kNotEditableMask); + transfer.Transfer (m_3D, "m_3D", kNotEditableMask); + transfer.Transfer (m_UseHardware, "m_UseHardware", kNotEditableMask); + + transfer.Align(); + + transfer.Transfer (m_LoadFlag, "m_Stream", kNotEditableMask); + + if (m_LoadFlag == kStreamFromDisc) + transfer.EnableResourceImage(kStreamingResourceImage); + + if (!transfer.ReadStreamingInfo (&m_StreamingInfo)) + transfer.Transfer(m_AudioData, "m_AudioData"); + + TRANSFER_EDITOR_ONLY(m_EditorAudioData); + TRANSFER_EDITOR_ONLY((SInt32&)m_EditorSoundType); + TRANSFER_EDITOR_ONLY((SInt32&)m_EditorSoundFormat); + } + else if (transfer.IsOldVersion(1) || transfer.IsOldVersion(2)) + { // old/openal data + SInt32 oldFormat = m_Format; + m_Format = FORMAT_TO_FMOD_FORMAT[ oldFormat ]; + m_Type = FORMAT_TO_FMOD_TYPE[ oldFormat ]; + + if (transfer.IsOldVersion (2)) + transfer.Transfer (m_Frequency, "m_Frequency", kNotEditableMask); + else + transfer.Transfer (m_Frequency, "m_Freq", kNotEditableMask); + unsigned size = 0; + transfer.Transfer (size, "m_Size", kNotEditableMask); + + + // assumes that m_size is the sizes in bytes + transfer.TransferTypeless (&size, "audio data", kHideInEditorMask); + + // clear audio data + m_AudioData.clear(); + m_AudioData.resize_uninitialized(size); + + // transfer data + transfer.TransferTypelessData (size, &m_AudioData[0]); + + // data always have to be in little endian (WAV format) +#if !UNITY_BIG_ENDIAN + if (transfer.ConvertEndianess ()) + { + if (m_Type != FMOD_SOUND_TYPE_OGGVORBIS && m_Format == FMOD_SOUND_FORMAT_PCM16) + SwapEndianArray (&m_AudioData[0], 2, size / 2); + } +#else + if (!transfer.ConvertEndianess ()) + { + if (m_Type != FMOD_SOUND_TYPE_OGGVORBIS && m_Format == FMOD_SOUND_FORMAT_PCM16) + SwapEndianArray (&m_AudioData[0], 2, size / 2); + } +#endif + + bool decompressOnLoad = false; + transfer.Transfer (decompressOnLoad, "m_DecompressOnLoad", kNotEditableMask); + m_LoadFlag = decompressOnLoad ? kDecompressOnLoad : kCompressedInMemory; + + // make a qualified guess for old asset data + //kMono8 = 1, kMono16, kStereo8, kStereo16, kOggVorbis + m_Channels = 2; + if (oldFormat == 1 || oldFormat == 2) + m_Channels = 1; + if (oldFormat == 5) + { + // we don't have any option but parsing the ogg file to obtain the channel count + // use our own ogg reader for that + int vorbisChannels = 0; + if (CheckOggVorbisFile (&m_AudioData[0], m_AudioData.size(), &vorbisChannels)) + m_Channels = vorbisChannels; + } + + // mono sounds were always 3D in OpenAL (in <=Unity 2.5) and 2D sounds not + m_3D = m_Channels == 1; + + // convert old data to new + if (oldFormat != 5) + ConvertOldAsset(m_Frequency, size, m_Channels, 16, &m_AudioData[0]); + } + else if (transfer.IsOldVersion(3)) // FMOD version + { + transfer.Transfer ((SInt32&)m_Type, "m_Type", kNotEditableMask); + + transfer.Transfer (m_3D, "m_3D", kNotEditableMask); + + transfer.Align(); + + transfer.Transfer(m_AudioData, "m_AudioData"); + + bool decompressOnLoad = false; + transfer.Transfer (decompressOnLoad, "m_DecompressOnLoad", kNotEditableMask); + m_LoadFlag = decompressOnLoad ? kDecompressOnLoad : kCompressedInMemory; + } +} + +void AudioClip::AwakeFromLoadThreaded () +{ + Super::AwakeFromLoadThreaded(); + LoadSound(); +} + + +void AudioClip::AwakeFromLoad (AwakeFromLoadMode awakeMode) +{ + Super::AwakeFromLoad (awakeMode); + + // Load data if not done from another thread already + if ((awakeMode & kDidLoadThreaded) == 0) + { + LoadSound(); + } +} + +IMPLEMENT_CLASS_HAS_INIT (AudioClip) +IMPLEMENT_OBJECT_SERIALIZE (AudioClip) + +#endif //ENABLE_AUDIO diff --git a/Runtime/Audio/AudioClip.h b/Runtime/Audio/AudioClip.h new file mode 100644 index 0000000..09a397f --- /dev/null +++ b/Runtime/Audio/AudioClip.h @@ -0,0 +1,10 @@ +#ifndef AUDIOCLIP_H +#define AUDIOCLIP_H + +#if !(UNITY_FLASH || UNITY_WEBGL) +#include "Runtime/Audio/AudioClip_FMOD.h" +#else +#include "Runtime/Audio/AudioClip_Flash.h" +#endif // !UNITY_FLASH + +#endif // AUDIOCLIP_H
\ No newline at end of file diff --git a/Runtime/Audio/AudioClip_FMOD.h b/Runtime/Audio/AudioClip_FMOD.h new file mode 100644 index 0000000..535ca8d --- /dev/null +++ b/Runtime/Audio/AudioClip_FMOD.h @@ -0,0 +1,347 @@ +#ifndef AUDIOCLIP_FMOD_H +#define AUDIOCLIP_FMOD_H + +#if ENABLE_AUDIO + +#include "Runtime/BaseClasses/NamedObject.h" +#include "Runtime/Misc/Allocator.h" +#include "Runtime/Threads/Mutex.h" +#include "Runtime/Audio/correct_fmod_includer.h" +#include "Runtime/Utilities/dynamic_array.h" +#include "Runtime/Serialize/CacheWrap.h" +#include "Runtime/Mono/MonoIncludes.h" +#include "Runtime/Scripting/ScriptingUtility.h" + + +#if ENABLE_WWW +class WWW; +#endif + +class AudioSource; +class MoviePlayback; +namespace FMOD { class Channel; class Sound; } + +const unsigned kAudioQueueSize = (4 * 16384); + + +class AudioClip : public NamedObject +{ + +public: + REGISTER_DERIVED_CLASS (AudioClip, NamedObject) + DECLARE_OBJECT_SERIALIZE (AudioClip) + + AudioClip (MemLabelId label, ObjectCreationMode mode); + // virtual ~AudioClip (); - declared-by-macro + +#if ENABLE_WWW + // WARNING: don't call AwakeFromLoad if you use InitStream + bool InitStream (WWW* streamData, MoviePlayback* movie, bool realStream = false, FMOD_SOUND_TYPE fmodSoundType = FMOD_SOUND_TYPE_UNKNOWN ); + + // Is reading the data from this clip allowed by webplayer security + void SetReadAllowed (bool allowed) { m_ReadAllowed = allowed; } + bool GetReadAllowed () const { return m_ReadAllowed; } +#endif + bool InitWSound (FMOD::Sound* sound); + bool CreateUserSound(const std::string& name, unsigned lengthSamples, short channels, unsigned frequency, bool _3D, bool stream); + + void AwakeFromLoad (AwakeFromLoadMode awakeMode); + void AwakeFromLoadThreaded (); + + +public: + enum { kDecompressOnLoad = 0, kCompressedInMemory = 1, kStreamFromDisc }; + + FMOD_SOUND_TYPE GetType() const { return m_Type; } + void SetType(FMOD_SOUND_TYPE type) { m_Type = type; } + FMOD_SOUND_FORMAT GetFormat() const { return m_Format; } + + bool Get3D() const { return m_3D; } + void Set3D(bool threeD) { m_3D = threeD; } + + FMOD::Channel* CreateChannel(AudioSource* forSource = NULL); + FMOD::Sound* CreateSound(); + + unsigned int GetSampleCount() const; + unsigned int GetChannelCount() const; + unsigned int GetBitRate() const; + unsigned int GetFrequency() const; + unsigned int GetSize() const; + unsigned int GetLength() const; + float GetLengthSec() const; + unsigned int GetBitsPerSample() const; + MoviePlayback* GetMovie() const; + + void SetData(const float* data, unsigned lengthSamples, unsigned offsetSamples = 0); + void GetData(float* data, unsigned lengthSamples, unsigned offsetSamples = 0) const; + + virtual int GetRuntimeMemorySize () const; + + void AddSyncPoint( string name, UInt32 atSample ); + + bool Is3D() const { return m_3D; } + bool IsMovieAudio() const { return m_MoviePlayback != NULL; } + FMOD::Sound* GetSound() const { return m_Sound; } + void ReleaseSound(); + + bool IsOggCompressible() const; + + bool IsStream() const { return (GetMode()&FMOD_CREATESTREAM); } + + bool IsWWWStreamed() const { return m_WWWStreamed; } + + bool IsFMODStream() const; + + bool ReadyToPlay(); + + int GetLoadFlag() const { return m_LoadFlag; } + void SetLoadFlag( unsigned flag ) { Assert(flag < 3); m_LoadFlag = flag; } + + bool IsHardware() const { return m_UseHardware; } + + // Attached movie clip + void SetMoviePlayback(MoviePlayback* movie); + MoviePlayback* GetMoviePlayback() const; + + /** + * Queue data in to clip + * This is read by streamed sound + * @param buffer Audio data to queue + * @param size Size of Audio data + **/ + bool QueueAudioData(void* buffer, unsigned size); + + /** + * Top audio data from the quene + * @note buf must allocate size bytes + * @param size The size in bytes you want to top + * @return The audio data + **/ + bool GetQueuedAudioData(void** buf, unsigned size); + + void ClearQueue(); + + // Set audiodata + // WARNING: don't call AwakeFromLoad if you use SetAudioData + bool SetAudioDataSwap(dynamic_array<UInt8>& buffer, + bool threeD, + bool hardware, + int loadFlag, + bool externalStream, + FMOD_SOUND_TYPE type, + FMOD_SOUND_FORMAT format); + + + bool SetAudioData(const UInt8* buffer, + unsigned size, + bool threeD, + bool hardware, + int loadFlag, + bool externalStream, + FMOD_SOUND_TYPE type, + FMOD_SOUND_FORMAT format); + + // Set audiodata for playing in the editor + void SetEditorAudioData(const dynamic_array<UInt8>& buffer, FMOD_SOUND_TYPE type, FMOD_SOUND_FORMAT); + + +#if UNITY_EDITOR + bool WriteRawDataToFile(const std::string& path) const; +#endif + + static void InitializeClass (); + static void CleanupClass (); + + void Cleanup(); + void Reload(); + + static FMOD_SOUND_TYPE GetFormatFromExtension(const std::string& ext); + static bool IsFormatSupportedByPlatform(const std::string& ext); + +private: + // Serialized data + int m_Frequency; + FMOD_SOUND_TYPE m_Type; + FMOD_SOUND_FORMAT m_Format; + int m_Channels; + bool m_3D; + bool m_UseHardware; ///< IPhone only (for now) + int m_BitsPerSample; + typedef dynamic_array<UInt8> TAudioData; + TAudioData m_AudioData; // contains the entire audio file +#if UNITY_EDITOR + TAudioData m_EditorAudioData; // if m_AudioData contains "undecodable" data (XMA, M4A) use this instead (usually OGGVORBIS) + FMOD_SOUND_TYPE m_EditorSoundType; + FMOD_SOUND_FORMAT m_EditorSoundFormat; +#endif + + // buffer + typedef UNITY_VECTOR(kMemAudioData, UInt8) TAudioQueue; + TAudioQueue m_AudioBufferQueue; + Mutex m_AudioQueueMutex; + + // Load flag + int m_LoadFlag; + + StreamingInfo m_StreamingInfo; + + bool m_UserGenerated; + unsigned m_UserLengthSamples; + bool m_UserIsStream; + +#if ENABLE_WWW + WWW *m_StreamData; + bool m_ReadAllowed; +#endif + + bool m_ExternalStream; + + //this is used, if this clip is tied to a movie to control playback + MoviePlayback *m_MoviePlayback; + +private: + FMOD::Sound* m_Sound; + FMOD_OPENSTATE m_OpenState; + + // Script callbacks + #if ENABLE_MONO + MonoDomain* monoDomain; + #endif + ScriptingArrayPtr m_PCMArray; + int m_PCMArrayGCHandle; + ScriptingMethodPtr m_CachedPCMReaderCallbackMethod; + ScriptingMethodPtr m_CachedSetPositionCallbackMethod; + + ScriptingArrayPtr GetScriptPCMArray(unsigned length); + unsigned m_DecodeBufferSize; + +private: + // Cached sounds for streamed audio. + // FMOD doesn't support multiple channels for a streamed sound (due to file ptrs, buffers) + typedef std::vector<std::pair<FMOD::Sound*, FMOD::Channel*> > TFMODSounds; + mutable TFMODSounds m_CachedSounds; + + bool LoadSound(); + void GetSoundProps(); + FMOD::Channel* GetCachedChannel(); + FMOD_CREATESOUNDEXINFO GetExInfo() const; + FMOD_MODE GetMode() const; + const UInt8* GetAudioData() const; + +#if UNITY_IPHONE + FMOD::Channel* CreateIOSStreamChannel(); +#endif + + /** + * Convert old asset data to new (by reconstructing the a wav header) + * @param frequency Frequency + * @param size The size of the data + * @param data Array of raw data + **/ + void ConvertOldAsset(int frequency, int size, int channels, int bitsPerSample, UInt8* raw); + + template<class TransferFunc> + void TransferToFlash(TransferFunc& transfer); + + void CreateScriptCallback(); +private: + // Streaming + bool m_WWWStreamed; + + struct movieUserData { + MoviePlayback* movie; + unsigned read; + }; + +#if ENABLE_WWW + struct wwwUserData { + bool seek; + WWW* stream; + unsigned pos; + unsigned filesize; + }; + + + // Callbacks for WWW streaming + static FMOD_RESULT F_CALLBACK WWWOpen( + const char * www, // WWW class pointer + int unicode, + unsigned int * filesize, + void ** handle, + void ** userdata + ); + + static FMOD_RESULT F_CALLBACK WWWClose( + void * handle, + void * userdata + ); + static FMOD_RESULT F_CALLBACK WWWRead( + void * handle, + void * buffer, + unsigned int sizebytes, + unsigned int * bytesread, + void * userdata + ); + static FMOD_RESULT F_CALLBACK WWWSeek( + void * handle, + unsigned int pos, + void * userdata + ); +#endif + // Callbacks for movie streaming + static FMOD_RESULT F_CALLBACK movieopen( + const char * buffer, + int unicode, + unsigned int * filesize, + void ** handle, + void ** userdata + ); + + static FMOD_RESULT F_CALLBACK movieclose( + void * handle, + void * userdata + ); + static FMOD_RESULT F_CALLBACK movieread( + void * handle, + void * buffer, + unsigned int sizebytes, + unsigned int * bytesread, + void * userdata + ); + static FMOD_RESULT F_CALLBACK movieseek( + void * handle, + unsigned int pos, + void * userdata + ); + + + // Callbacks for PCM streaming + static FMOD_RESULT F_CALLBACK pcmread( + FMOD_SOUND * sound, + void * data, + unsigned int datalen + ); + + // Callbacks for PCM streaming + static FMOD_RESULT F_CALLBACK ScriptPCMReadCallback( + FMOD_SOUND * sound, + void * data, + unsigned int datalen + ); + + static FMOD_RESULT F_CALLBACK ScriptPCMSetPositionCallback( + FMOD_SOUND * sound, + int subsound, + unsigned int position, + FMOD_TIMEUNIT postype); + + + friend class AudioManager; + +#if ENABLE_PROFILER + static int s_AudioClipCount; +#endif +}; + +#endif //ENABLE_AUDIO +#endif diff --git a/Runtime/Audio/AudioClip_Flash.cpp b/Runtime/Audio/AudioClip_Flash.cpp new file mode 100644 index 0000000..a5d9ebb --- /dev/null +++ b/Runtime/Audio/AudioClip_Flash.cpp @@ -0,0 +1,122 @@ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" + +#if (UNITY_FLASH || UNITY_WEBGL) && ENABLE_AUDIO + + +#include "AudioClip.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" + +#if ENABLE_WWW +#include "Runtime/Export/WWW.h" +#endif + +AudioClip::AudioClip (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +{ + m_Mp3InMemoryLength = NULL; +} + +AudioClip::~AudioClip() +{ +#if UNITY_WEBGL + if (m_Mp3InMemoryLength) + UNITY_FREE (kMemAudio, m_Mp3InMemory); +#endif +} + +template<> +void AudioClip::Transfer<StreamedBinaryRead<false> > (StreamedBinaryRead<false>& transfer) { + Super::Transfer (transfer); + + transfer.Transfer(m_SampleCount,"samplecount"); + transfer.Transfer (m_3D, "m_3D"); +#if UNITY_WEBGL + transfer.Align(); +#endif + transfer.Transfer(m_Mp3InMemoryLength,"mp3length"); + +#if UNITY_WEBGL + m_Mp3InMemory = (UInt8*)UNITY_MALLOC(kMemAudio,m_Mp3InMemoryLength); + transfer.TransferTypelessData (m_Mp3InMemoryLength, &m_Mp3InMemory[0]); +#else + CachedReader& reader = transfer.GetCachedReader(); + UInt8* ptr = reader.GetCacher()->GetAddressOfMemory(); + m_Mp3InMemory = ptr + reader.GetPosition(); + reader.SetAbsoluteMemoryPosition(m_Mp3InMemory + m_Mp3InMemoryLength); +#endif + transfer.Align(); +} + +template<class TransferFunc> +void AudioClip::Transfer (TransferFunc& transfer) { + Super::Transfer (transfer); +} + +bool AudioClip::InitStream (WWW* streamData, void* movie, bool realStream) +{ +#if UNITY_WEBGL // No WWW yet on WebGL. + return false; +#else + if(realStream) + ErrorString("Streaming MP3 is currently unsupported on Flash."); + + bool succeeded = false; + if(streamData != NULL && streamData->GetError() == NULL){ + m_StreamData = streamData; + if(LoadSoundFrom((void*)m_StreamData->GetData(), m_StreamData->GetSize())){ + m_StreamData->SetAudioClip(this); + m_IsWWWAudioClip = true; + succeeded=true; + } + } + + if(!succeeded){ + m_StreamData = NULL; + ErrorString("Failed to load Audio from stream"); + } + + return succeeded; +#endif +} + +AudioChannel* AudioClip::CreateChannel(AudioSource* forSource) +{ + return new AudioChannel(m_SoundObject,forSource->GetLoop(),m_SampleCount); +} + +void AudioClip::LoadSound() +{ + LoadSoundFrom(m_Mp3InMemory,m_Mp3InMemoryLength); +} + +float AudioClip::GetLengthSec() +{ + if(m_SoundObject != NULL){ + return (float)Ext_Sound_Get_Length(m_SoundObject); + } + return 0.0f; +} + +bool AudioClip::LoadSoundFrom(void* ptr, size_t length) +{ + m_SoundObject = Ext_Sound_Load(ptr, length); + if(m_SoundObject != NULL){ + double length = Ext_Sound_Get_Length(m_SoundObject); + if(length != 0){ + return true; + } + } + return false; +} + +void AudioClip::AwakeFromLoad (AwakeFromLoadMode awakeMode) +{ + Super::AwakeFromLoad (awakeMode); + LoadSound(); +} + +IMPLEMENT_CLASS (AudioClip) +IMPLEMENT_OBJECT_SERIALIZE (AudioClip) + +#endif //UNITY_FLASH diff --git a/Runtime/Audio/AudioClip_Flash.h b/Runtime/Audio/AudioClip_Flash.h new file mode 100644 index 0000000..b3a9f10 --- /dev/null +++ b/Runtime/Audio/AudioClip_Flash.h @@ -0,0 +1,73 @@ +#ifndef AUDIOCLIP_FLASH_H +#define AUDIOCLIP_FLASH_H +#include "Configuration/UnityConfigure.h" + +#if (UNITY_FLASH || UNITY_WEBGL) && ENABLE_AUDIO + +#include "Runtime/BaseClasses/NamedObject.h" +#include "Runtime/Misc/Allocator.h" +#include "Runtime/Utilities/dynamic_array.h" +#include "Runtime/Serialize/CacheWrap.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Audio/AudioSource.h" +#include "PlatformDependent/FlashSupport/cpp/AudioChannel.h" + +#if ENABLE_WWW +class WWW; +#endif + +class AudioClip : public NamedObject +{ + +public: + REGISTER_DERIVED_CLASS (AudioClip, NamedObject) + DECLARE_OBJECT_SERIALIZE (AudioClip) + + AudioClip (MemLabelId label, ObjectCreationMode mode); + // virtual ~AudioClip (); - declared-by-macro + + void AwakeFromLoad (AwakeFromLoadMode awakeMode); + float GetLengthSec(); + + bool ReadyToPlay() { return true; } + int GetFrequency() { return 44100; } + unsigned int GetSampleCount() { return m_SampleCount; } + int GetChannelCount() { return 2; } + AudioChannel* CreateChannel(AudioSource* forSource = NULL); + + bool Get3D() const { return m_3D; } + void Set3D(bool threeD) { m_3D = threeD; } + + void SetData(const float* data, unsigned lengthSamples, unsigned offsetSamples = 0) {} + void GetData(float* data, unsigned lengthSamples, unsigned offsetSamples = 0) const {} + + void SetReadAllowed (bool allowed) { m_ReadAllowed = allowed; } + bool GetReadAllowed () const { return m_ReadAllowed; } + + bool InitStream (WWW* streamData, void* movie, bool realStream = false); + +private: + void LoadSound(); + bool LoadSoundFrom(void* ptr, size_t length); + + bool m_IsWWWAudioClip; + bool m_ReadAllowed; + bool m_3D; + UInt8* m_Mp3InMemory; + int m_Mp3InMemoryLength; + unsigned int m_SampleCount; +#if UNITY_FLASH + AS3Handle m_SoundObject; +#elif UNITY_WEBGL + int m_SoundObject; +#endif + WWW* m_StreamData; + + + + friend class AudioManager; + +}; + +#endif //UNITY_FLASH +#endif diff --git a/Runtime/Audio/AudioCustomFilter.cpp b/Runtime/Audio/AudioCustomFilter.cpp new file mode 100644 index 0000000..4907c64 --- /dev/null +++ b/Runtime/Audio/AudioCustomFilter.cpp @@ -0,0 +1,210 @@ +/* + * AudioCustomFilter.cpp + * Xcode + * + * Created by Søren Christiansen on 9/12/10. + * Copyright 2010 Unity Technologies. All rights reserved. + * + */ +#include "UnityPrefix.h" +#if ENABLE_AUDIO_FMOD +#include "AudioCustomFilter.h" +#include "AudioSource.h" +#include "Runtime/Mono/MonoBehaviour.h" +#include "Runtime/Mono/MonoIncludes.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Mono/MonoScriptCache.h" +#include "Runtime/Mono/MonoScopedThreadAttach.h" +#include "Runtime/Threads/Mutex.h" +#include "Runtime/Threads/Thread.h" +#include "Runtime/Profiler/TimeHelper.h" + +#if UNITY_EDITOR +#include "Editor/Src/AssetPipeline/MonoCompile.h" +#endif + +#include "Runtime/Scripting/Scripting.h" + +AudioCustomFilter::AudioCustomFilter(MonoBehaviour* behaviour) : +m_behaviour(behaviour), +#if ENABLE_MONO +monoDomain(NULL), +#endif +m_DSP(NULL), +m_InScriptCallback(false), +m_playingSource(NULL) +#if UNITY_EDITOR +, +processTime(0.0), +channelCount(0) +#endif +{ + Init(); +} + +AudioCustomFilter::~AudioCustomFilter() +{ + Cleanup(); +} + +void AudioCustomFilter::Init() +{ + // setup dsp/callbacks + if (!m_DSP) + { + FMOD_DSP_DESCRIPTION dspdesc; + FMOD_RESULT result; + + memset(&dspdesc, 0, sizeof(FMOD_DSP_DESCRIPTION)); + +#if UNITY_EDITOR + strcpy(dspdesc.name, m_behaviour->GetScriptClassName().c_str()); +#endif + dspdesc.channels = 0; // 0 = whatever comes in, else specify. + dspdesc.read = AudioCustomFilter::readCallback; + dspdesc.userdata = this; + + result = GetAudioManager().GetFMODSystem()->createDSP(&dspdesc, &m_DSP); + FMOD_ASSERT(result); + + m_DSP->setBypass(true); + } + + #if ENABLE_MONO + monoDomain = mono_domain_get(); + #endif +} + +void AudioCustomFilter::Cleanup() +{ + if (m_DSP) + { + // is this a playing dsp? + if (m_playingSource) + { + // Doesn't matter whether argument is true or false, since sources playing DSP's don't play one-shots at the same time. + m_playingSource->Stop(true); + } + + FMOD_RESULT result = m_DSP->release(); + FMOD_ASSERT(result); + m_DSP = NULL; + } +} + +FMOD::DSP* AudioCustomFilter::GetOrCreateDSP() +{ + if (!m_DSP) + Init(); + return m_DSP; +} + +FMOD::DSP* AudioCustomFilter::GetDSP() +{ + return m_DSP; +} + +void AudioCustomFilter::WaitForScriptCallback() +{ + while (m_InScriptCallback) + Thread::Sleep(0.01); // wait 10ms + +} + +#if UNITY_EDITOR +float AudioCustomFilter::GetMaxIn(short channel) const +{ + if (m_DSP) + { + bool active, bypass; + m_DSP->getActive(&active); + m_DSP->getBypass(&bypass); + return active&&!bypass?maxIn[channel]:0.0f; + } + else return 0.0f; +} + +float AudioCustomFilter::GetMaxOut(short channel) const +{ + if (m_DSP) + { + bool active, bypass; + m_DSP->getActive(&active); + m_DSP->getBypass(&bypass); + return active&&!bypass?maxOut[channel]:0.0f; + } + else return 0.0f; +} +#endif // UNITY_EDITOR + +FMOD_RESULT F_CALLBACK AudioCustomFilter::readCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels) +{ +#if SUPPORT_MONO_THREADS + +#if UNITY_EDITOR + if (IsCompiling()) + return FMOD_OK; +#endif + + AudioCustomFilter* filter; + FMOD_RESULT result; + FMOD::DSP* fmod_dsp = (FMOD::DSP*)dsp_state->instance; + result = fmod_dsp->getUserData((void**)&filter); + + Assert (filter); + +#if UNITY_EDITOR + ABSOLUTE_TIME start_time = START_TIME; +#endif + if (!filter->m_behaviour->GetEnabled()) + return FMOD_OK; + + filter->m_InScriptCallback = true; + { + ScopedThreadAttach attach(filter->monoDomain); + + // reuse mono array + MonoArray* array = NULL; + GetAudioManager().GetScriptBufferManager().GetDSPFilterArray(length * inchannels, array); + + Assert(array); + + memcpy( &GetMonoArrayElement<float>( array, 0 ), inbuffer, length * 4 * inchannels ); + + ScriptingObjectPtr instance = Scripting::ScriptingWrapperFor( filter->m_behaviour ); + if (instance) + { + MonoMethod* method = filter->m_behaviour->GetMethod(MonoScriptCache::kAudioFilterRead)->monoMethod; + void* args[] = { array, &inchannels }; + MonoException* exception; + mono_runtime_invoke( method, instance, args, &exception ); + if (exception) + { + // handle + Scripting::LogException(exception, Scripting::GetInstanceIDFromScriptingWrapper(instance)); + } + else + { + memcpy(outbuffer, &GetMonoArrayElement<float>( array, 0 ), length * 4 * inchannels); + } + } + } + filter->m_InScriptCallback = false; + +#if UNITY_EDITOR + filter->processTime = GetProfileTime(ELAPSED_TIME(start_time)); + filter->channelCount = inchannels; + for (int c = 0; c < inchannels && c < MAX_CHANNELS; c++) + filter->maxOut[c] = filter->maxIn[c] = -1.0f; + for (int i = 0;i < length; i += inchannels) + for (int c = 0; c < inchannels && c < MAX_CHANNELS; c++) + { + if (filter->maxIn[c]<inbuffer[i+c]) filter->maxIn[c] = inbuffer[i+c]; + if (filter->maxOut[c]<outbuffer[i+c]) filter->maxOut[c] = outbuffer[i+c]; + } +#endif +#endif // SUPPORT_THREADS + + return FMOD_OK; +} +#endif // ENABLE_AUDIO_FMOD diff --git a/Runtime/Audio/AudioCustomFilter.h b/Runtime/Audio/AudioCustomFilter.h new file mode 100644 index 0000000..650a74b --- /dev/null +++ b/Runtime/Audio/AudioCustomFilter.h @@ -0,0 +1,68 @@ +/* + * AudioCustomFilter.h + * Xcode + * + * Created by Søren Christiansen on 9/12/10. + * Copyright 2010 Unity Technologies. All rights reserved. + * + */ +#ifndef __AUDIO_CUSTOM_FILTER_H__ +#define __AUDIO_CUSTOM_FILTER_H__ + +#if ENABLE_AUDIO_FMOD + +#include "AudioSourceFilter.h" + +class MonoBehaviour; +struct MonoThread; +struct MonoDomain; +class AudioSource; + +#define MAX_CHANNELS 8 + +class AudioCustomFilter +{ +public: + AudioCustomFilter(MonoBehaviour* behaviour); + virtual ~AudioCustomFilter(); + + FMOD::DSP* GetOrCreateDSP(); + FMOD::DSP* GetDSP(); +public: + #if ENABLE_MONO + MonoDomain* monoDomain; + #endif + + void SetPlayingSource(AudioSource* source) { m_playingSource = source; } + AudioSource* GetPlayingSource() const { return m_playingSource; } + void WaitForScriptCallback(); + void Cleanup(); +private: + FMOD::DSP* m_DSP; + + MonoBehaviour* m_behaviour; + + bool m_InScriptCallback; + AudioSource* m_playingSource; +private: + void Init(); + +public: +#if UNITY_EDITOR + float GetMaxIn(short channel) const; + float GetMaxOut(short channel) const; + + + int channelCount; + UInt64 processTime; // time in nanoseconds +private: + float maxIn[MAX_CHANNELS]; + float maxOut[MAX_CHANNELS]; +#endif + +private: + static FMOD_RESULT F_CALLBACK readCallback(FMOD_DSP_STATE *dsp_state, float *inbuffer, float *outbuffer, unsigned int length, int inchannels, int outchannels); +}; + +#endif // ENABLE_AUDIO_FMOD +#endif // __AUDIO_CUSTOM_FILTER_H__
\ No newline at end of file diff --git a/Runtime/Audio/AudioDistortionFilter.cpp b/Runtime/Audio/AudioDistortionFilter.cpp new file mode 100644 index 0000000..c59aa28 --- /dev/null +++ b/Runtime/Audio/AudioDistortionFilter.cpp @@ -0,0 +1,52 @@ +#include "UnityPrefix.h" +#include "AudioDistortionFilter.h" +#include "Runtime/Utilities/Utility.h" + +#if ENABLE_AUDIO_FMOD + +AudioDistortionFilter::AudioDistortionFilter (MemLabelId label, ObjectCreationMode mode) : +Super(label, mode), +m_DistortionLevel(0.5f) +{ + m_Type = FMOD_DSP_TYPE_DISTORTION; +} + +void AudioDistortionFilter::Reset() +{ + Super::Reset(); + m_DistortionLevel = 0.5f; +} + +AudioDistortionFilter::~AudioDistortionFilter() +{} + +void AudioDistortionFilter::AddToManager() +{ + Super::AddToManager(); +} + +void AudioDistortionFilter::CheckConsistency() +{ + Super::CheckConsistency(); + m_DistortionLevel = clamp(m_DistortionLevel,0.0f,1.0f); +} + +void AudioDistortionFilter::Update() +{ + if (m_DSP) + m_DSP->setParameter(FMOD_DSP_DISTORTION_LEVEL, m_DistortionLevel); +} + + +template<class TransferFunc> +void AudioDistortionFilter::Transfer (TransferFunc& transfer) +{ + Super::Transfer (transfer); + TRANSFER(m_DistortionLevel); +} + + +IMPLEMENT_CLASS (AudioDistortionFilter) +IMPLEMENT_OBJECT_SERIALIZE (AudioDistortionFilter) + +#endif
\ No newline at end of file diff --git a/Runtime/Audio/AudioDistortionFilter.h b/Runtime/Audio/AudioDistortionFilter.h new file mode 100644 index 0000000..1bc86ce --- /dev/null +++ b/Runtime/Audio/AudioDistortionFilter.h @@ -0,0 +1,26 @@ +#ifndef __AUDIODISTORTION_FILTER_H__ +#define __AUDIODISTORTION_FILTER_H__ + +#if ENABLE_AUDIO_FMOD +#include "AudioSourceFilter.h" + +class AudioDistortionFilter : public AudioFilter { +public: + REGISTER_DERIVED_CLASS (AudioDistortionFilter, AudioFilter) + DECLARE_OBJECT_SERIALIZE (AudioDistortionFilter) + AudioDistortionFilter (MemLabelId label, ObjectCreationMode mode); + + virtual void CheckConsistency (); + virtual void Update(); + virtual void AddToManager(); + virtual void Reset(); + + float GetDistortionLevel() const { return m_DistortionLevel; } + void SetDistortionLevel(const float distortionLevel) { m_DistortionLevel = distortionLevel; Update(); SetDirty(); } + +private: + float m_DistortionLevel; // Distortion value. 0.0 to 1.0. Default = 0.5. +}; + +#endif // ENABLE_AUDIO +#endif // AUDIODISTORTION diff --git a/Runtime/Audio/AudioEchoFilter.cpp b/Runtime/Audio/AudioEchoFilter.cpp new file mode 100644 index 0000000..875ba7b --- /dev/null +++ b/Runtime/Audio/AudioEchoFilter.cpp @@ -0,0 +1,72 @@ +#include "UnityPrefix.h" + +#if ENABLE_AUDIO_FMOD + +#include "AudioEchoFilter.h" +#include "Runtime/Utilities/Utility.h" + + +AudioEchoFilter::AudioEchoFilter (MemLabelId label, ObjectCreationMode mode) : +Super(label, mode), +m_Delay(500), +m_DecayRatio(0.5f), +m_DryMix(1.0f), +m_WetMix(1.0f) +{ + m_Type = FMOD_DSP_TYPE_ECHO; +} + +AudioEchoFilter::~AudioEchoFilter() +{} + +void AudioEchoFilter::AddToManager() +{ + Super::AddToManager(); +} + +void AudioEchoFilter::Reset() +{ + Super::Reset(); + + m_Delay = 500; + m_DecayRatio = 0.5f; + m_DryMix = 1.0f; + m_WetMix = 1.0f; +} + +void AudioEchoFilter::CheckConsistency() +{ + Super::CheckConsistency(); + m_Delay = clamp<int>(m_Delay,10,5000); + m_DecayRatio = clamp (m_DecayRatio,0.0f,1.0f); + m_DryMix = clamp(m_DryMix,0.0f,1.0f); + m_WetMix = clamp(m_WetMix,0.0f,1.0f); +} + +void AudioEchoFilter::Update() +{ + if (m_DSP) + { + m_DSP->setParameter(FMOD_DSP_ECHO_DELAY, m_Delay); + m_DSP->setParameter(FMOD_DSP_ECHO_DECAYRATIO, m_DecayRatio); + m_DSP->setParameter(FMOD_DSP_ECHO_DRYMIX, m_DryMix); + m_DSP->setParameter(FMOD_DSP_ECHO_WETMIX, m_WetMix); + } +} + + +template<class TransferFunc> +void AudioEchoFilter::Transfer (TransferFunc& transfer) +{ + Super::Transfer (transfer); + TRANSFER(m_Delay); + TRANSFER(m_DecayRatio); + TRANSFER(m_WetMix); + TRANSFER(m_DryMix); +} + + +IMPLEMENT_CLASS (AudioEchoFilter) +IMPLEMENT_OBJECT_SERIALIZE (AudioEchoFilter) + +#endif //ENABLE_AUDIO diff --git a/Runtime/Audio/AudioEchoFilter.h b/Runtime/Audio/AudioEchoFilter.h new file mode 100644 index 0000000..3bdc5f9 --- /dev/null +++ b/Runtime/Audio/AudioEchoFilter.h @@ -0,0 +1,41 @@ +#ifndef __AUDIOECHO_FILTER_H__ +#define __AUDIOECHO_FILTER_H__ + +#if ENABLE_AUDIO_FMOD + +#include "AudioSourceFilter.h" + +class AudioEchoFilter : public AudioFilter +{ +public: + REGISTER_DERIVED_CLASS (AudioEchoFilter, AudioFilter) + DECLARE_OBJECT_SERIALIZE (AudioEchoFilter) + AudioEchoFilter (MemLabelId label, ObjectCreationMode mode); + + virtual void CheckConsistency (); + virtual void AddToManager(); + virtual void Reset(); + + void Update(); + + float GetDelay() const { return m_Delay; } + void SetDelay(const float delay) { m_Delay = (unsigned)delay; Update(); SetDirty(); } + + float GetDecayRatio() const { return m_DecayRatio; } + void SetDecayRatio(const float decay) { m_DecayRatio = decay; Update(); SetDirty(); } + + float GetDryMix() const { return m_DryMix; } + void SetDryMix(const float drymix) { m_DryMix = drymix; Update(); SetDirty(); } + + float GetWetMix() const { return m_WetMix; } + void SetWetMix(const float wetmix) { m_WetMix = wetmix; Update(); SetDirty(); } + +private: + unsigned m_Delay; // Echo delay in ms. 10 to 5000. Default = 500. + float m_DecayRatio; // Echo decay per delay. 0 to 1. 1.0 = No decay, 0.0 = total decay (ie simple 1 line delay). Default = 0.5. + float m_DryMix; // Volume of original signal to pass to output. 0.0 to 1.0. Default = 1.0. + float m_WetMix; // Volume of echo signal to pass to output. 0.0 to 1.0. Default = 1.0. +}; + +#endif //ENABLE_AUDIO +#endif // ___AUDIOECHO_FILTER_H__ diff --git a/Runtime/Audio/AudioHighPassFilter.cpp b/Runtime/Audio/AudioHighPassFilter.cpp new file mode 100644 index 0000000..8e37717 --- /dev/null +++ b/Runtime/Audio/AudioHighPassFilter.cpp @@ -0,0 +1,61 @@ +#include "UnityPrefix.h" +#if ENABLE_AUDIO_FMOD +#include "AudioHighPassFilter.h" +#include "Runtime/Utilities/Utility.h" + + +AudioHighPassFilter::AudioHighPassFilter (MemLabelId label, ObjectCreationMode mode) : +Super(label, mode), +m_CutoffFrequency(5000.0f), +m_HighpassResonanceQ(1.0f) +{ + m_Type = FMOD_DSP_TYPE_HIGHPASS; +} + +AudioHighPassFilter::~AudioHighPassFilter() +{} + +void AudioHighPassFilter::AddToManager() +{ + Super::AddToManager(); +} + +void AudioHighPassFilter::Reset() +{ + Super::Reset(); + + m_CutoffFrequency = 5000.0f; + m_HighpassResonanceQ = 1.0f; +} + +void AudioHighPassFilter::CheckConsistency() +{ + Super::CheckConsistency(); + // @TODO get output freq from audiomanager + m_CutoffFrequency = clamp(m_CutoffFrequency, 10.0f, 22000.0f); + m_HighpassResonanceQ = clamp(m_HighpassResonanceQ, 1.0f, 10.0f); +} + +void AudioHighPassFilter::Update() +{ + if (m_DSP) + { + m_DSP->setParameter(FMOD_DSP_HIGHPASS_CUTOFF, m_CutoffFrequency); + m_DSP->setParameter(FMOD_DSP_HIGHPASS_RESONANCE, m_HighpassResonanceQ); + } +} + + +template<class TransferFunc> +void AudioHighPassFilter::Transfer (TransferFunc& transfer) +{ + Super::Transfer (transfer); + TRANSFER(m_CutoffFrequency); + TRANSFER(m_HighpassResonanceQ); +} + + +IMPLEMENT_CLASS (AudioHighPassFilter) +IMPLEMENT_OBJECT_SERIALIZE (AudioHighPassFilter) + +#endif //ENABLE_AUDIO
\ No newline at end of file diff --git a/Runtime/Audio/AudioHighPassFilter.h b/Runtime/Audio/AudioHighPassFilter.h new file mode 100644 index 0000000..8b228dd --- /dev/null +++ b/Runtime/Audio/AudioHighPassFilter.h @@ -0,0 +1,33 @@ +#ifndef __AUDIOHIGHPASS_FILTER_H__ +#define __AUDIOHIGHPASS_FILTER_H__ + +#include "AudioSourceFilter.h" +#if ENABLE_AUDIO_FMOD + +class AudioHighPassFilter : public AudioFilter +{ +public: + REGISTER_DERIVED_CLASS (AudioHighPassFilter, AudioFilter) + DECLARE_OBJECT_SERIALIZE (AudioHighPassFilter) + + AudioHighPassFilter (MemLabelId label, ObjectCreationMode mode); + + virtual void CheckConsistency (); + virtual void AddToManager(); + virtual void Reset(); + + void Update(); + + float GetCutoffFrequency() const { return m_CutoffFrequency; } + void SetCutoffFrequency(float value) { m_CutoffFrequency = value; Update(); SetDirty();} + + float GetHighpassResonanceQ() const { return m_HighpassResonanceQ; } + void SetHighpassResonanceQ(float value) { m_HighpassResonanceQ = value; Update(); SetDirty();} + +private: + float m_CutoffFrequency; + float m_HighpassResonanceQ; +}; + +#endif //ENABLE_AUDIO_FMOD +#endif // __AUDIOHIGHPASS_FILTER_H__ diff --git a/Runtime/Audio/AudioListener.cpp b/Runtime/Audio/AudioListener.cpp new file mode 100644 index 0000000..5b083e5 --- /dev/null +++ b/Runtime/Audio/AudioListener.cpp @@ -0,0 +1,177 @@ +#include "UnityPrefix.h" +#include "AudioManager.h" +#include "AudioListener.h" +#include "Runtime/Graphics/Transform.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/Input/TimeManager.h" +#include "Runtime/Camera/Camera.h" +#include "Runtime/BaseClasses/IsPlaying.h" +#include "Runtime/Camera/RenderManager.h" +#include "AudioSourceFilter.h" +#include "AudioEchoFilter.h" +#include "AudioChorusFilter.h" +#include "correct_fmod_includer.h" +#include "Runtime/Misc/BuildSettings.h" +#include "Runtime/Mono/MonoBehaviour.h" + +#if ENABLE_AUDIO + +AudioListener::AudioListener (MemLabelId label, ObjectCreationMode mode) +: Super(label, mode) +, m_Node(this) +, m_VelocityUpdateMode (kVelocityUpdateModeAuto) +, m_LastPosition(Vector3f(0,0,0)) +{ +} + +AudioListener::~AudioListener () +{ + GetAudioManager().RemoveAudioListener(this); +} + +void AudioListener::Cleanup() +{ +#if ENABLE_AUDIO_FMOD + const GameObject* go = GetGameObjectPtr(); + if (!go) + return; + for (int i=0;i<go->GetComponentCount();i++) + { + AudioFilter* filter = dynamic_pptr_cast<AudioFilter*> (&go->GetComponentAtIndex(i)); + if (filter == NULL) + continue; + + filter->Cleanup(); + } +#endif +} + +void AudioListener::RemoveFromManager () +{ + GetAudioManager().RemoveAudioListener(this); +} + +void AudioListener::AddToManager () +{ + m_LastPosition = GetCurrentTransform().GetPosition(); + GetAudioManager().AddAudioListener(this); +#if ENABLE_AUDIO_FMOD + ApplyFilters(); +#endif +} + +void AudioListener::AwakeFromLoad (AwakeFromLoadMode awakeMode) +{ + Super::AwakeFromLoad (awakeMode); +} + +void AudioListener::SetAlternativeTransform(Transform* t) +{ + m_AltTransform = t; +} + +const Transform& AudioListener::GetCurrentTransform() const +{ +#if UNITY_EDITOR + if (!IsWorldPlaying()) + { + Transform* altTransform = m_AltTransform; + if (altTransform) + return *altTransform; + } +#endif + return GetComponent(Transform); +} + +void AudioListener::DoUpdate () +{ + const Transform& transform = GetCurrentTransform(); + const Vector3f pos = transform.GetPosition(); + + Vector3f vel = (pos - m_LastPosition) * GetInvDeltaTime (); + GetAudioManager().UpdateListener( + pos, + vel, + NormalizeSafe(transform.TransformDirection( Vector3f (0.0f, 1.0f, 0.0f) )), + NormalizeSafe(transform.TransformDirection( Vector3f (0.0f, 0.0f, 1.0f) )) + ); + m_LastPosition = pos; +} + +void AudioListener::Update() +{ + if(m_VelocityUpdateMode == kVelocityUpdateModeAuto) + m_VelocityUpdateMode = GetAudioManager().GetAutomaticUpdateMode( GetGameObjectPtr() ); + + if(m_VelocityUpdateMode==kVelocityUpdateModeDynamic) + DoUpdate(); +} + +void AudioListener::FixedUpdate() +{ + if(m_VelocityUpdateMode == kVelocityUpdateModeAuto) + m_VelocityUpdateMode = GetAudioManager().GetAutomaticUpdateMode( GetGameObjectPtr()); + + if(m_VelocityUpdateMode==kVelocityUpdateModeFixed) + DoUpdate(); +} + +// Apply filters +#if ENABLE_AUDIO_FMOD +void AudioListener::ApplyFilters() +{ + const GameObject& go = GetGameObject(); + for (int i=0;i<go.GetComponentCount();i++) + { + FMOD::DSP* dsp = NULL; + + AudioFilter* filter = NULL; + filter = dynamic_pptr_cast<AudioFilter*> (&go.GetComponentAtIndex(i)); + if ( filter && GetBuildSettings().hasAdvancedVersion ) + dsp = filter->GetDSP(); + + #if ENABLE_SCRIPTING + if (!dsp) + { + MonoBehaviour* behaviour = dynamic_pptr_cast<MonoBehaviour*> (&go.GetComponentAtIndex(i)); + if ( behaviour ) dsp = behaviour->GetOrCreateDSP(); + } + #endif + + if (dsp == NULL) + continue; + + FMOD_RESULT result; + result = dsp->remove(); + FMOD_ASSERT(result); + result = GetAudioManager().GetChannelGroup_FX_IgnoreVolume()->addDSP(dsp, 0); + FMOD_ASSERT(result); + } +} +#endif + +void AudioListener::OnAddComponent() +{ +#if ENABLE_AUDIO_FMOD + ApplyFilters(); +#endif +} + +void AudioListener::InitializeClass () +{ + REGISTER_MESSAGE_VOID (AudioListener, kDidAddComponent, OnAddComponent); +} + +void AudioListener::CleanupClass() +{ +} + +template<class TransferFunc> +void AudioListener::Transfer (TransferFunc& transfer) { + Super::Transfer (transfer); +} + +IMPLEMENT_CLASS_HAS_INIT (AudioListener) +IMPLEMENT_OBJECT_SERIALIZE (AudioListener) + +#endif diff --git a/Runtime/Audio/AudioListener.h b/Runtime/Audio/AudioListener.h new file mode 100644 index 0000000..187ddb4 --- /dev/null +++ b/Runtime/Audio/AudioListener.h @@ -0,0 +1,62 @@ +#ifndef __AUDIOLISTENER_H__ +#define __AUDIOLISTENER_H__ + +#if ENABLE_AUDIO + +#include "Runtime/GameCode/Behaviour.h" +#include "Runtime/Math/Vector3.h" +#include "AudioBehaviour.h" + +class Transform; + +class AudioListener : public AudioBehaviour +{ +public: + + REGISTER_DERIVED_CLASS (AudioListener, AudioBehaviour) + DECLARE_OBJECT_SERIALIZE (AudioListener) + + AudioListener (MemLabelId label, ObjectCreationMode mode); + // virtual ~AudioListener (); declared-by-macro + + void AwakeFromLoad (AwakeFromLoadMode awakeMode); + int GetVelocityUpdateMode() const { return m_VelocityUpdateMode; } + void SetVelocityUpdateMode(int update) { m_VelocityUpdateMode=update; } + + // Behaviour + virtual void Update(); + virtual void FixedUpdate(); + + ListNode<AudioListener>& GetNode() { return m_Node; } + + const Vector3f& GetPosition() const { return m_LastPosition; } + + void OnAddComponent(); + + static void InitializeClass (); + static void CleanupClass(); + + void Cleanup(); + + void SetAlternativeTransform(Transform* t); + +private: + virtual void AddToManager (); + virtual void RemoveFromManager (); + void DoUpdate (); + void ApplyFilters(); + +private: + Vector3f m_LastPosition; + int m_VelocityUpdateMode; + + PPtr<Transform> m_AltTransform; + const Transform& GetCurrentTransform() const; + + ListNode<AudioListener> m_Node; + + friend class AudioManager; +}; + +#endif //ENABLE_AUDIO +#endif // __AUDIOLISTENER_H__ diff --git a/Runtime/Audio/AudioLowPassFilter.cpp b/Runtime/Audio/AudioLowPassFilter.cpp new file mode 100644 index 0000000..091405f --- /dev/null +++ b/Runtime/Audio/AudioLowPassFilter.cpp @@ -0,0 +1,111 @@ +#include "UnityPrefix.h" +#include "AudioLowPassFilter.h" +#include "Runtime/Utilities/Utility.h" + +#if ENABLE_AUDIO_FMOD + +AudioLowPassFilter::AudioLowPassFilter (MemLabelId label, ObjectCreationMode mode) : +Super(label, mode), +m_CutoffFrequency(5000.0f), +m_LowpassResonanceQ(1.0f), +m_NeedToNormalizeCurve(false) +{ + m_Type = FMOD_DSP_TYPE_LOWPASS; +} + +AudioLowPassFilter::~AudioLowPassFilter() +{} + +void AudioLowPassFilter::AwakeFromLoad (AwakeFromLoadMode awakeMode) +{ + Super::AwakeFromLoad (awakeMode); + + if (m_NeedToNormalizeCurve) + { + AudioSource* source = QueryComponent(AudioSource); + if (source) + ScaleCurveTime(m_LowpassLevelCustomCurve, 1 / source->GetMaxDistance()); + } +} + +void AudioLowPassFilter::Reset() +{ + Super::Reset(); + + m_CutoffFrequency = 5000.0f; + m_LowpassResonanceQ = 1.0f; + m_NeedToNormalizeCurve = false; + + // Curve initial values will be handled in CheckConsistency + m_LowpassLevelCustomCurve.ResizeUninitialized (0); + CheckConsistency (); +} + +void AudioLowPassFilter::AddToManager() +{ + Super::AddToManager(); +} + +void AudioLowPassFilter::CheckConsistency() +{ + Super::CheckConsistency(); + // @TODO get output freq from audiomanager + m_CutoffFrequency = clamp(m_CutoffFrequency, 10.0f, 22000.0f); + m_LowpassResonanceQ = clamp(m_LowpassResonanceQ, 1.0f, 10.0f); + + if (m_LowpassLevelCustomCurve.GetKeyCount() < 1) + m_LowpassLevelCustomCurve.AddKey(AnimationCurve::Keyframe(0.0f, 1 - m_CutoffFrequency / 22000.0f)); +} + +void AudioLowPassFilter::Update() +{ + if (m_DSP) + { + m_DSP->setParameter(FMOD_DSP_LOWPASS_CUTOFF, m_CutoffFrequency); + m_DSP->setParameter(FMOD_DSP_LOWPASS_RESONANCE, m_LowpassResonanceQ); + } +} +/// Set/Get spread curve +AnimationCurve& AudioLowPassFilter::GetCustomLowpassLevelCurve () +{ + return m_LowpassLevelCustomCurve; +} + + +const AnimationCurve& AudioLowPassFilter::GetCustomLowpassLevelCurve () const +{ + return m_LowpassLevelCustomCurve; +} + +void AudioLowPassFilter::SetCustomLowpassLevelCurve(const AnimationCurve& curve) +{ + m_LowpassLevelCustomCurve = curve; + SetDirty(); +} + + +template<class TransferFunc> +void AudioLowPassFilter::Transfer (TransferFunc& transfer) +{ + Super::Transfer (transfer); + + // 3.5 3: Normalized curve values + // <3.5 2: Non-Normalized curves + transfer.SetVersion (3); + + TRANSFER(m_CutoffFrequency); + TRANSFER(m_LowpassResonanceQ); + + transfer.Transfer(m_LowpassLevelCustomCurve, "lowpassLevelCustomCurve"); + + if (transfer.IsVersionSmallerOrEqual(2)) + { + m_NeedToNormalizeCurve = true; + } +} + + +IMPLEMENT_CLASS (AudioLowPassFilter) +IMPLEMENT_OBJECT_SERIALIZE (AudioLowPassFilter) + +#endif //ENABLE_AUDIO
\ No newline at end of file diff --git a/Runtime/Audio/AudioLowPassFilter.h b/Runtime/Audio/AudioLowPassFilter.h new file mode 100644 index 0000000..16e2d05 --- /dev/null +++ b/Runtime/Audio/AudioLowPassFilter.h @@ -0,0 +1,41 @@ +#ifndef __AUDIOLOWPASS_FILTER_H__ +#define __AUDIOLOWPASS_FILTER_H__ +#include "AudioSourceFilter.h" +#include "Runtime/Audio/AudioSource.h" +#include "Runtime/Animation/AnimationCurveUtility.h" +#include "Runtime/Math/AnimationCurve.h" + +#if ENABLE_AUDIO_FMOD + +class AudioLowPassFilter : public AudioFilter { +public: + REGISTER_DERIVED_CLASS (AudioLowPassFilter, AudioFilter) + DECLARE_OBJECT_SERIALIZE (AudioLowPassFilter) + AudioLowPassFilter (MemLabelId label, ObjectCreationMode mode); + + virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode); + + virtual void CheckConsistency (); + virtual void Update(); + virtual void AddToManager(); + + float GetCutoffFrequency() const { return m_CutoffFrequency; } + void SetCutoffFrequency(float value) { m_CutoffFrequency = value; Update(); SetDirty();} + float GetLowpassResonanceQ() const { return m_LowpassResonanceQ; } + void SetLowpassResonanceQ(float value) { m_LowpassResonanceQ = value; Update(); SetDirty();} + + AnimationCurve& GetCustomLowpassLevelCurve (); + const AnimationCurve& GetCustomLowpassLevelCurve () const; + void SetCustomLowpassLevelCurve(const AnimationCurve& curve); + + virtual void Reset(); +private: + AnimationCurve m_LowpassLevelCustomCurve; + float m_CutoffFrequency; + float m_LowpassResonanceQ; + bool m_NeedToNormalizeCurve; +}; + + +#endif // ENABLE_AUDIO +#endif // __AUDIOLOWPASS_FILTER_H__ diff --git a/Runtime/Audio/AudioManager.Callbacks.cpp b/Runtime/Audio/AudioManager.Callbacks.cpp new file mode 100644 index 0000000..6db2b9c --- /dev/null +++ b/Runtime/Audio/AudioManager.Callbacks.cpp @@ -0,0 +1,37 @@ +#include "UnityPrefix.h" +#if ENABLE_AUDIO_FMOD +#include "AudioManager.h" +#include "Runtime/Audio/correct_fmod_includer.h" + +FMOD_RESULT F_CALLBACK AudioManager::systemCallback(FMOD_SYSTEM* c_system, FMOD_SYSTEM_CALLBACKTYPE type, void* data1, void* data2) +{ + FMOD::System* system = (FMOD::System*)c_system; + FMOD_RESULT result = FMOD_OK; + + switch (type) + { + case FMOD_SYSTEM_CALLBACKTYPE_DEVICELISTCHANGED: + // Get available sound cards + // If no device is found fall back on the NOSOUND driver + // @TODO Enable user to choose driver + int numDrivers; + result = system->getNumDrivers(&numDrivers); + + if ((result == FMOD_OK) && (numDrivers != 0)) + { + // set driver to the new default driver + // and autodetect output + result = system->setDriver(0); + if (result != FMOD_OK) + { + ErrorString(Format("Default audio device was changed, but the audio system failed to initialize it (%s). This may be because the audio device that Unity was started on and the device switched to have different sampling rates or speaker configurations. To get sound back you can either adjust the sampling rate on the new device (on Windows using the control panel, on Mac via the Audio MIDI Setup application), switch back to the old device or restart Unity.",FMOD_ErrorString(result))); + return result; + } + } + break; + default: break; + } + + return result; +} +#endif //ENABLE_AUDIO
\ No newline at end of file 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 diff --git a/Runtime/Audio/AudioManager.h b/Runtime/Audio/AudioManager.h new file mode 100644 index 0000000..6e0d185 --- /dev/null +++ b/Runtime/Audio/AudioManager.h @@ -0,0 +1,296 @@ +#ifndef AUDIOMANAGER_H +#define AUDIOMANAGER_H + +#include "Configuration/UnityConfigure.h" +#include "Runtime/BaseClasses/GameManager.h" +#include "Runtime/Math/Vector3.h" +#include "Runtime/Utilities/LinkedList.h" +#include "Runtime/Profiler/ProfilerStats.h" +#include "Runtime/Misc/Player.h" +#include "Runtime/Audio/AudioTypes.h" +#include "Runtime/Mono/MonoIncludes.h" +#if ENABLE_AUDIO_FMOD +#include "Runtime/Audio/AudioScriptBufferManager.h" +#endif + +#if ENABLE_AUDIO + +#if UNITY_FLASH || UNITY_WEBGL +#include "PlatformDependent/FlashSupport/cpp/AudioChannel.h" +#endif + +#ifndef FMOD_ASSERT +#define FMOD_ASSERT(x) {Assert(x == FMOD_OK);\ +if (x != FMOD_OK){ ErrorString(FMOD_ErrorString(x));}} +#endif + + +#include "Runtime/Audio/correct_fmod_includer.h" // can't forward declare enums (@TODO use ints?) + +class AudioSource; +class AudioListener; +class AudioClip; +class WWW; +class AudioFilter; +class AudioReverbZone; +struct MonoDomain; +struct MonoThread; +#if ENABLE_AUDIO_FMOD +namespace FMOD { class System; class Sound; class Channel; class ChannelGroup; } +#endif +namespace Unity { class GameObject; } + + +enum{ + kVelocityUpdateModeAuto = 0, + kVelocityUpdateModeFixed = 1, + kVelocityUpdateModeDynamic = 2, +}; + + +class AudioManager : public GlobalGameManager +{ +public: + REGISTER_DERIVED_CLASS (AudioManager, GlobalGameManager) + DECLARE_OBJECT_SERIALIZE (AudioManager) + + AudioManager (MemLabelId label, ObjectCreationMode mode); + // virtual ~AudioManager (); declared-by-macro + +#if UNITY_WII + void UpdateOnDiskEject(); +#endif + void Update (); + void FixedUpdate (); + + void AddAudioSource (AudioSource* s, bool paused); + void RemoveAudioSource (AudioSource* s); + void StopSources(); + + void AddAudioListener (AudioListener* newListener); + void RemoveAudioListener (AudioListener* newListener); + AudioListener* GetAudioListener() const; + + + + void SetVolume (float volume); + float GetVolume () const; + void SetPause (bool pause); + bool GetPause () const { return m_IsPaused; } + int GetAutomaticUpdateMode(Unity::GameObject *go); + + // Manager implementation + void AwakeFromLoad (AwakeFromLoadMode awakeMode); + static void InitializeClass (); + static void CleanupClass (); + + void UpdateListener( + const Vector3f& position, + const Vector3f& velocity, + const Vector3f& up, + const Vector3f& forward); + + struct AudioScheduledSource : public ListElement + { + AudioScheduledSource(AudioSource* src) : source(src), time(0.0) {}; + AudioSource* source; + double time; + }; + +private: + + float m_DefaultVolume; + float m_Volume; + float m_Rolloffscale; + bool m_IsPaused; + + typedef List< ListNode<AudioSource> > TAudioSources; + typedef List< ListNode<AudioListener> > TAudioListeners; + typedef List<AudioScheduledSource> TScheduledSources; + typedef TAudioSources::iterator TAudioSourcesIterator; + typedef TAudioListeners::iterator TAudioListenersIterator; + typedef TScheduledSources::iterator TScheduledSourcesIterator; + + TAudioSources m_Sources; + TAudioSources m_PausedSources; + TAudioListeners m_Listeners; + TScheduledSources m_ScheduledSources; + + void ProcessScheduledSources(); +#if ENABLE_MICROPHONE + FMOD::Sound* CreateSound (int deviceID, int lengthSec, int frequency); +#endif // ENABLE_MICROPHONE + +public: + float GetRolloffScale() const { return m_Rolloffscale; } + bool IsAudioDisabled() const { return m_DisableAudio; } + + /// Schedule source to be played in sync. In this frame (if delay==0) or later + inline UInt64 GetAccumulatedPauseTicks() const { return m_accPausedTicks; } + void ScheduleSource(AudioSource* s, double time); + void UnScheduleSource(AudioSource* s); + double GetDSPTime() const; + +#if ENABLE_AUDIO_FMOD + void AddAudioReverbZone(AudioReverbZone* z); + void RemoveAudioReverbZone(AudioReverbZone* z); + + // FMOD + bool ValidateFMODResult(FMOD_RESULT result, const char* errmsg); + bool InitFMOD(); + void InitScriptBufferManager(); + void ReloadFMODSounds(); + + bool InitNormal(); +#ifdef SUPPORT_REPRODUCE_LOG + bool InitReproduction(); +#endif + + void CloseFMOD(); + FMOD::System* GetFMODSystem() const { return m_FMODSystem;} + static FMOD_RESULT F_CALLBACK systemCallback(FMOD_SYSTEM* c_system, FMOD_SYSTEM_CALLBACKTYPE type, void* data1, void* data2); + + // groups + FMOD::ChannelGroup* GetChannelGroup_FX_IgnoreVolume() const { return m_ChannelGroup_FX_IgnoreVolume; } + FMOD::ChannelGroup* GetChannelGroup_FX_UseVolume() const { return m_ChannelGroup_FX_UseVolume; } + FMOD::ChannelGroup* GetChannelGroup_NoFX_IgnoreVolume() const { return m_ChannelGroup_NoFX_IgnoreVolume; } + FMOD::ChannelGroup* GetChannelGroup_NoFX_UseVolume() const { return m_ChannelGroup_NoFX_UseVolume; } + + + // FMOD profiling + int GetMemoryAllocated() const; + float GetCPUUsage() const; + #if ENABLE_PROFILER + void GetProfilerData( AudioStats& audioStats ); + #endif + + + FMOD::Sound* CreateFMODSound(const void* buffer, FMOD_CREATESOUNDEXINFO exInfo, FMOD_MODE mode, bool useHardwareDecoder); + + #if UNITY_EDITOR + FMOD::Sound* CreateFMODSound(const std::string& path, bool threeD, + bool hardware, bool openOnly = false ); + #endif + + + FMOD::Sound* CreateFMODSoundFromWWW(WWW* webStream, + bool threeD, + FMOD_SOUND_TYPE suggestedtype, + FMOD_SOUND_FORMAT format, + unsigned freq, + unsigned channels, + bool stream); + + FMOD::Sound* CreateFMODSoundFromMovie(AudioClip* clip, bool threeD); + + FMOD::Channel* GetFreeFMODChannel(FMOD::Sound* sound, bool paused = true); + + unsigned GetPlayingChannelsForSound(FMOD::Sound* sound); + const std::string& GetLastError() const { return m_LastErrorString; } + + FMOD_SPEAKERMODE GetSpeakerMode() const { return m_speakerMode; } + FMOD_SPEAKERMODE GetSpeakerModeCaps() const { return m_speakerModeCaps; } + void SetSpeakerMode(FMOD_SPEAKERMODE speakerMode); + +public: + +#if ENABLE_MICROPHONE + // Microphone + const std::vector<std::string> GetRecordDevices() const; + bool EndRecord(int deviceID); + PPtr<AudioClip> StartRecord(int deviceID, bool loop, int lengthSec, int frequency); + bool IsRecording(int deviceID) const; + unsigned GetRecordPosition(int deviceID) const; + int GetMicrophoneDeviceIDFromName(const std::string& name) const; + void GetDeviceCaps(int deviceID, int *minFreq, int *maxFreq) const; + + mutable std::map<std::string, int> m_MicrophoneNameToIDMap; +#endif // ENABLE_MICROPHONE + + + +private: + float m_SpeedOfSound; + float m_DopplerFactor; + + typedef List< ListNode<AudioReverbZone> > TAudioReverbZones; + typedef TAudioReverbZones::iterator TAudioReverbZonesIterator; + TAudioReverbZones m_ReverbZones; + + // FMOD + FMOD::System* m_FMODSystem; + + /* +----------------+ + * | NoFX_UseVolume | + * /+----------------+ + * _+-------------------+/ + * / | NoFX_IgnoreVolume |<---EditorChannel plays directly into this group + * +-----------+/ +-------------------+ + * |FMOD Master| ............... + * +-----------+\ +------------------+ : AudioSource : + * ^ \_| FX_IgnoreVolume | __:__+-----+ : + * | +------------------+\ +--------------+ / : | Wet | : + * | \| FX_UseVolume |/ : +-----+ : + * | +--------------+\ : : + * Zone reverbs \__:__+-----+ : + * : | Dry | : + * : +-----+ : + * ............... + * The AudioSource's Wet and Dry groups can be attached to the Listener, IgnoreVolume, IgnoreVolNoFX, or ListenerNoFX groups depending on the combinations of the bypassEffects and bypassListenerEffects properties. + * Note that zone reverbs are applied by FMOD after the downmix of the master channel group, so in order to avoid reverb on previews the ignoreVolumeGroupNoFX uses FMOD's overrideReverbProperties to turn off the reverb. + */ + + FMOD::ChannelGroup* m_ChannelGroup_FMODMaster; + + FMOD::ChannelGroup* m_ChannelGroup_FX_IgnoreVolume; + FMOD::ChannelGroup* m_ChannelGroup_FX_UseVolume; + FMOD::ChannelGroup* m_ChannelGroup_NoFX_IgnoreVolume; + FMOD::ChannelGroup* m_ChannelGroup_NoFX_UseVolume; + + FMOD_SPEAKERMODE m_speakerMode; ///< TransferName{Default Speaker Mode} enum { Raw = 0, Mono = 1, Stereo = 2, Quad = 3, Surround = 4, Surround 5.1 = 5, Surround 7.1 = 6, Prologic DTS = 7 } + FMOD_SPEAKERMODE m_activeSpeakerMode; + FMOD_CAPS m_driverCaps; + FMOD_SPEAKERMODE m_speakerModeCaps; + + // Phone DSP buffer size + int m_DSPBufferSize; ///< enum { Default = 0, Best latency = 256, Good latency = 512, Best performance = 1024 } + + // error handling + std::string m_LastErrorString; + FMOD_RESULT m_LastFMODErrorResult; + +private: + AudioScriptBufferManager* m_ScriptBufferManager; + +public: + AudioScriptBufferManager& GetScriptBufferManager(); + AudioScriptBufferManager* GetScriptBufferManagerPtr(); + +#if UNITY_EDITOR +public: + void PlayClip(AudioClip& clip, int startSample = 0, bool loop = false, bool twoD = true); + void StopClip(const AudioClip& clip); + void PauseClip(const AudioClip& clip); + void ResumeClip(const AudioClip& clip); + bool IsClipPlaying(const AudioClip& clip); + void StopAllClips(); + float GetClipPosition(const AudioClip& clip); + unsigned int GetClipSamplePosition(const AudioClip& clip); + void SetClipSamplePosition(const AudioClip& clip, unsigned int iSamplePosition); + void LoopClip(const AudioClip& clip, bool loop); + void ListenerCheck(); + +private: + FMOD::Channel* m_EditorChannel; +#endif //UNITY_EDITOR +#endif //ENABLE_AUDIO_FMOD + UInt64 m_accPausedTicks; + UInt64 m_pauseStartTicks; + bool m_DisableAudio; // Completely disable audio (in standalone builds only) +}; + +AudioManager& GetAudioManager (); +AudioManager* GetAudioManagerPtr (); + +#endif // ENABLE_AUDIO +#endif // AUDIOMANAGER_H diff --git a/Runtime/Audio/AudioModule.cpp b/Runtime/Audio/AudioModule.cpp new file mode 100644 index 0000000..e71aa12 --- /dev/null +++ b/Runtime/Audio/AudioModule.cpp @@ -0,0 +1,142 @@ +#include "UnityPrefix.h" +#include "Runtime/Interfaces/IAudio.h" +#include "AudioManager.h" +#include "Runtime/Video/BaseVideoTexture.h" +#include "Runtime/Video/MovieTexture.h" +#include "Runtime/Audio/AudioClip.h" +#include "Runtime/Export/WWW.h" +#include "Runtime/Audio/AudioCustomFilter.h" + +class AudioModule : public IAudio +{ + virtual void SetPause( bool paused ) + { + GetAudioManager().SetPause(paused); + } + + virtual void FixedUpdate() + { + GetAudioManager().FixedUpdate(); + } + + virtual void Update() + { + GetAudioManager().Update(); + } + + virtual void StopVideoTextures() + { + BaseVideoTexture::StopVideoTextures(); + } + + virtual void PauseVideoTextures() + { + BaseVideoTexture::PauseVideoTextures(); + } + + virtual void UpdateVideoTextures() + { + BaseVideoTexture::UpdateVideoTextures(); + } +#if ENABLE_WWW +#if ENABLE_MOVIES + virtual MovieTexture* CreateMovieTextureFromWWW(WWW& www) + { + MovieTexture* tex = NEW_OBJECT(MovieTexture); + tex->Reset(); + tex->InitStream(&www); + return tex; + } +#endif + virtual AudioClip* CreateAudioClipFromWWW(WWW& www, bool threeD, bool stream, FMOD_SOUND_TYPE audioType) + { + AudioClip* clip = NEW_OBJECT(AudioClip); + + // only allow sample read if the security policy allows it + WWW::SecurityPolicy policy = www.GetSecurityPolicy(); + if (policy != WWW::kSecurityPolicyAllowAccess) + clip->SetReadAllowed(false); + + clip->Reset(); + clip->Set3D(threeD); + +#if !UNITY_FLASH + if (!clip->InitStream(&www, NULL, stream, audioType)) +#else + if (!clip->InitStream(&www, NULL, stream)) +#endif + + { + DestroySingleObject(clip); + return NULL; + } + return clip; + } +#endif + + virtual AudioClip* CreateAudioClipOGGFromWWW(WWW& www) + { +#if ENABLE_AUDIO_FMOD + AudioClip* clip = NEW_OBJECT (AudioClip); + clip->Reset(); + + clip->SetName(GetLastPathNameComponent(www.GetUrl()).c_str()); + + if (!clip->SetAudioData(www.GetData(), www.GetSize(), false, false,true, false, FMOD_SOUND_TYPE_OGGVORBIS, FMOD_SOUND_FORMAT_PCM16)) + { + DestroySingleObject(clip); + clip = NULL; + } + return clip; +#else + return 0; +#endif + } + + virtual bool IsFormatSupportedByPlatform(const char* type) + { +#if ENABLE_AUDIO_FMOD + return AudioClip::IsFormatSupportedByPlatform(type); +#endif + } + +#if ENABLE_AUDIO_FMOD + virtual FMOD::DSP* GetOrCreateDSPFromCustomFilter(AudioCustomFilter* filter) + { + return filter->GetOrCreateDSP(); + } + + virtual AudioCustomFilter* CreateAudioCustomFilter(MonoBehaviour* mb) + { + return new AudioCustomFilter(mb); + } + + virtual FMOD::DSP* GetDSPFromAudioCustomFilter(AudioCustomFilter* filter) + { + return filter->GetDSP(); + } + + virtual void SetBypassOnDSP(FMOD::DSP* dsp, bool state) + { + dsp->setBypass(state); + } +#endif + +#if ENABLE_PROFILER + virtual void GetProfilerStats(AudioStats& stats) + { + GetAudioManager().GetProfilerData(stats); + } +#endif + + virtual void AudioManagerAwakeFromLoad(AwakeFromLoadMode mode) + { + GetAudioManager().AwakeFromLoad(mode); + } +}; + + +IAudio* CreateAudioModule() +{ + return new AudioModule(); +}
\ No newline at end of file diff --git a/Runtime/Audio/AudioModule.jam b/Runtime/Audio/AudioModule.jam new file mode 100644 index 0000000..6c36e8d --- /dev/null +++ b/Runtime/Audio/AudioModule.jam @@ -0,0 +1,137 @@ +rule AudioModule_ReportCpp +{ + local audiosources = + AudioBehaviour.cpp + AudioBehaviour.h + AudioChorusFilter.cpp + AudioChorusFilter.h + AudioClip.Callbacks.cpp + AudioClip.cpp + AudioClip.h + AudioClip_Flash.h + AudioClip_FMOD.h + AudioClip_Flash.cpp + AudioDistortionFilter.cpp + AudioDistortionFilter.h + AudioEchoFilter.cpp + AudioEchoFilter.h + AudioHighPassFilter.cpp + AudioHighPassFilter.h + AudioListener.cpp + AudioListener.h + AudioLowPassFilter.cpp + AudioLowPassFilter.h + AudioManager.Callbacks.cpp + AudioManager.cpp + AudioManager.h + AudioParameters.h + AudioReverbFilter.cpp + AudioReverbFilter.h + AudioReverbZone.cpp + AudioReverbZone.h + AudioSource.Callbacks.cpp + AudioSource.cpp + AudioSource.h + AudioSourceFilter.cpp + AudioSourceFilter.h + OggReader.h + WavReader.h + Utilities/Conversion.h + AudioCustomFilter.cpp + AudioCustomFilter.h + AudioScriptBufferManager.h + AudioScriptBufferManager.cpp + + AudioModuleRegistration.cpp + AudioModule.cpp + ; + + local videosources = + MoviePlayback.cpp + MoviePlayback.h + MovieTexture.cpp + MovieTexture.h + BaseVideoTexture.h + BaseVideoTexture.cpp + VideoTexture.h + ; + + local modulesources = + Runtime/Audio/$(audiosources) + Runtime/Video/$(videosources) + ; + + if $(PLATFORM) in macosx32 macosx64 macosxppc + { + modulesources += PlatformDependent/OSX/VideoTexture.mm ; + } + + if $(PLATFORM) in macosx32 macosx64 macosxppc iphone ipad iphonesimulator + { + modulesources += PlatformDependent/OSX/DecodeCVImageBufferFrame.cpp ; + } + + # add other platforms here + + return $(modulesources) ; +} + +rule AudioModule_ReportTxt +{ + return + Runtime/Audio/ScriptBindings/AudioBindings.txt + Runtime/Video/ScriptBindings/MovieTextureBindings.txt + Runtime/Video/ScriptBindings/UnityEngineWebCamTexture.txt + ; +} + +rule AudioModule_ReportIncludes +{ + return + Projects/PrecompiledHeaders/ + External/Audio/FMOD/builds/macosx/include + External/Audio/common_ogg + ; +} + +rule AudioModule_ReportLibraries +{ + local libs = ; + if $(currentTarget) in MacEditor MacStandalonePlayer + { + libs += + External/Theora/libs/Release/libtheoradec.a + External/Audio/FMOD/builds/macosx/lib/libfmodex.a + ; + } + return $(libs) ; +} + +rule AudioModule_ReportLinkFlags +{ + local flags = ; + if $(currentTarget) in MacEditor MacStandalonePlayer iPhonePlayer iPhoneSimulatorPlayer + { + flags += + -framework CoreAudio + -framework CoreVideo + -framework QuickTime + -framework AudioUnit + -framework Carbon + -framework QTKit + ; + } + return $(flags) ; +} + + +rule AudioModule_Init +{ + OverrideModule Audio : GetModule_Cpp : byOverridingWithMethod : AudioModule_ReportCpp ; + OverrideModule Audio : GetModule_Txt : byOverridingWithMethod : AudioModule_ReportTxt ; + OverrideModule Audio : GetModule_Inc : byOverridingWithMethod : AudioModule_ReportIncludes ; + OverrideModule Audio : GetModule_Lib : byOverridingWithMethod : AudioModule_ReportLibraries ; + OverrideModule Audio : GetModule_Flags : byOverridingWithMethod : AudioModule_ReportLinkFlags ; +} + +#RegisterModule Audio ; diff --git a/Runtime/Audio/AudioModuleRegistration.cpp b/Runtime/Audio/AudioModuleRegistration.cpp new file mode 100644 index 0000000..e43d694 --- /dev/null +++ b/Runtime/Audio/AudioModuleRegistration.cpp @@ -0,0 +1,65 @@ +#include "UnityPrefix.h" + +#if ENABLE_AUDIO +#include "Runtime/BaseClasses/ClassRegistration.h" +#include "Runtime/Modules/ModuleRegistration.h" +#include "Runtime/Interfaces/IAudio.h" + +static void RegisterAudioClasses (ClassRegistrationContext& context) +{ + REGISTER_CLASS (AudioManager) + REGISTER_CLASS (AudioListener) + REGISTER_CLASS (AudioSource) + REGISTER_CLASS (AudioClip) + REGISTER_CLASS (AudioBehaviour) + +#if ENABLE_AUDIO_FMOD + REGISTER_CLASS (AudioReverbFilter) + REGISTER_CLASS (AudioHighPassFilter) + REGISTER_CLASS (AudioChorusFilter) + REGISTER_CLASS (AudioReverbZone) + REGISTER_CLASS (AudioEchoFilter) + REGISTER_CLASS (AudioLowPassFilter) + REGISTER_CLASS (AudioDistortionFilter) + REGISTER_CLASS (AudioFilter) +#endif + +#if ENABLE_MOVIES + REGISTER_CLASS (MovieTexture) +#endif + +#if ENABLE_WEBCAM + REGISTER_CLASS (WebCamTexture) +#endif + +} + +void ExportAudioBindings (); +void ExportUnityEngineWebCamTexture (); +void ExportMovieTextureBindings(); + +static void RegisterAudioICallModule () +{ +#if !INTERNAL_CALL_STRIPPING + ExportAudioBindings (); + #if ENABLE_WEBCAM + ExportUnityEngineWebCamTexture (); + #endif + #if ENABLE_MOVIES + ExportMovieTextureBindings(); + #endif +#endif +} + +extern "C" EXPORT_MODULE void RegisterModule_Audio () +{ + ModuleRegistrationInfo info; + info.registerClassesCallback = &RegisterAudioClasses; +#if ENABLE_MONO || UNITY_WINRT + info.registerIcallsCallback = &RegisterAudioICallModule; +#endif + RegisterModuleInfo (info); + + SetIAudio(CreateAudioModule()); +} +#endif
\ No newline at end of file diff --git a/Runtime/Audio/AudioParameters.h b/Runtime/Audio/AudioParameters.h new file mode 100644 index 0000000..e4b49be --- /dev/null +++ b/Runtime/Audio/AudioParameters.h @@ -0,0 +1,48 @@ +#ifndef ___AUDIOPARAMETERS_H__ +#define ___AUDIOPARAMETERS_H__ + +#include "Runtime/Math/Vector3.h" +#include "Runtime/Audio/correct_fmod_includer.h" +#include "Runtime/Math/AnimationCurve.h" + +enum RolloffMode { kRolloffLogarithmic=0, kRolloffLinear, kRolloffCustom }; + +struct AudioParameters +{ + // Animated props + AnimationCurve panLevelCustomCurve; + AnimationCurve spreadCustomCurve; + AnimationCurve rolloffCustomCurve; + + float insideConeAngle; + float outsideConeAngle; + float outsideConeVolume; + + int priority; + float dopplerLevel; + float minDistance; + float maxDistance; + float pan; + + float pitch; + float volume; + + // rolloff + RolloffMode rolloffMode; ///< enum { kRolloffLogarithmic=0, kRolloffLinear, kRolloffCustom } + + bool loop; // <-- this will be replaced by a loop node + bool mute; + +#if UNITY_WII + bool starving; // For streaming sounds, when data isn't coming due disk eject +#endif + + bool bypassEffects; // Bypass/ignore any applied effects from AudioSource + bool bypassListenerEffects; // Bypass/ignore any applied effects from AudioListener + bool bypassReverbZones; // Bypass/ignore any applied effects from reverb zones + bool ignoreListenerPause; +}; + + + +#endif // ___AUDIOPARAMETERS_H__ diff --git a/Runtime/Audio/AudioReverbFilter.cpp b/Runtime/Audio/AudioReverbFilter.cpp new file mode 100644 index 0000000..fe1e34d --- /dev/null +++ b/Runtime/Audio/AudioReverbFilter.cpp @@ -0,0 +1,194 @@ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" +#if ENABLE_AUDIO_FMOD +#include "AudioReverbFilter.h" +#include "Runtime/Utilities/Utility.h" +#include "AudioTypes.h" + +/* Inst Env Diffus Room RoomHF RmLF DecTm DecHF DecLF Refl RefDel Revb RevDel ModTm ModDp HFRef LFRef Diffus Densty FLAGS */ +#define FMOD_PRESET_DRUGGED { 0, 23, 0.50f, -1000, 0, 0, 8.39f, 1.39f, 1.0f, -115, 0.002f, 985, 0.030f, 0.250f, 0.00f, 5000.0f, 250.0f, 100.0f, 100.0f, 0x1f } +#define FMOD_PRESET_DIZZY { 0, 24, 0.60f, -1000, -400, 0, 17.23f, 0.56f, 1.0f, -1713, 0.020f, -613, 0.030f, 0.250f, 0.310f, 5000.0f, 250.0f, 100.0f, 100.0f, 0x1f } +#define FMOD_PRESET_PSYCHOTIC { 0, 25, 0.50f, -1000, -151, 0, 7.56f, 0.91f, 1.0f, -626, 0.020f, 774, 0.030f, 0.250f, 0.00f, 5000.0f, 250.0f, 100.0f, 100.0f, 0x1f } + + +FMOD_REVERB_PROPERTIES ReverbPresets [] = +{ + FMOD_PRESET_OFF, + FMOD_PRESET_GENERIC, + FMOD_PRESET_PADDEDCELL, + FMOD_PRESET_ROOM, + FMOD_PRESET_BATHROOM, + FMOD_PRESET_LIVINGROOM, + FMOD_PRESET_STONEROOM, + FMOD_PRESET_AUDITORIUM, + FMOD_PRESET_CONCERTHALL, + FMOD_PRESET_CAVE, + FMOD_PRESET_ARENA, + FMOD_PRESET_HANGAR, + FMOD_PRESET_CARPETTEDHALLWAY, + FMOD_PRESET_HALLWAY, + FMOD_PRESET_STONECORRIDOR, + FMOD_PRESET_ALLEY, + FMOD_PRESET_FOREST, + FMOD_PRESET_CITY, + FMOD_PRESET_MOUNTAINS, + FMOD_PRESET_QUARRY, + FMOD_PRESET_PLAIN, + FMOD_PRESET_PARKINGLOT, + FMOD_PRESET_SEWERPIPE, + FMOD_PRESET_UNDERWATER, + FMOD_PRESET_DRUGGED, + FMOD_PRESET_DIZZY, + FMOD_PRESET_PSYCHOTIC +}; + +AudioReverbFilter::AudioReverbFilter (MemLabelId label, ObjectCreationMode mode) : +Super(label, mode), +m_DryLevel(0.0f), // Dry Level : Mix level of dry signal in output in mB. Ranges from -10000.0 to 0.0. Default is 0. +m_Room(0.0f), // Room : Room effect level at low frequencies in mB. Ranges from -10000.0 to 0.0. Default is 0.0. +m_RoomHF(0.0f), // Room HF : Room effect high-frequency level re. low frequency level in mB. Ranges from -10000.0 to 0.0. Default is 0.0. +m_RoomRolloff(10.0f), // Room Rolloff : Like DS3D flRolloffFactor but for room effect. Ranges from 0.0 to 10.0. Default is 10.0 +m_DecayTime(1.0f), // Reverberation decay time at low-frequencies in seconds. Ranges from 0.1 to 20.0. Default is 1.0. +m_DecayHFRatio(0.5f), // Decay HF Ratio : High-frequency to low-frequency decay time ratio. Ranges from 0.1 to 2.0. Default is 0.5. +m_ReflectionsLevel(-10000.0f), // Early reflections level relative to room effect in mB. Ranges from -10000.0 to 1000.0. Default is -10000.0. +m_ReverbLevel(0.0f), // Reverb : Late reverberation level relative to room effect in mB. Ranges from -10000.0 to 2000.0. Default is 0.0. +m_ReverbDelay(0.04f), // Late reverberation delay time relative to first reflection in seconds. Ranges from 0.0 to 0.1. Default is 0.04. +m_Diffusion(100.0f), // Reverberation diffusion (echo density) in percent. Ranges from 0.0 to 100.0. Default is 100.0. +m_Density(100.0f), // Reverberation density (modal density) in percent. Ranges from 0.0 to 100.0. Default is 100.0. +m_HFReference(5000.0f), // HF Reference : Reference high frequency in Hz. Ranges from 20.0 to 20000.0. Default is 5000.0. +m_RoomLF(0.0f), // Room effect low-frequency level in mB. Ranges from -10000.0 to 0.0. Default is 0.0. +m_LFReference(250.0f), // Reference low-frequency in Hz. Ranges from 20.0 to 1000.0. Default is 250.0. +m_ReflectionsDelay(0.0f), // Late reverberation level relative to room effect in mB. Ranges from -10000.0 to 2000.0. Default is 0.0. +m_ReverbPreset(27) +{ + m_Type = FMOD_DSP_TYPE_SFXREVERB; +} + +AudioReverbFilter::~AudioReverbFilter() +{} + +void AudioReverbFilter::AddToManager() +{ + Super::AddToManager(); +} + +void AudioReverbFilter::CheckConsistency() +{ + Super::CheckConsistency(); + m_DryLevel = clamp(m_DryLevel,-10000.0f, 0.0f); + m_Room = clamp(m_Room,-10000.0f, 0.0f); + m_RoomHF = clamp(m_RoomHF,-10000.0f, 0.0f); + m_RoomRolloff = clamp(m_RoomRolloff,-10000.0f, 0.0f); + m_DecayTime = clamp(m_DecayTime,0.1f, 20.0f); + m_DecayHFRatio = clamp(m_DecayHFRatio, 0.1f, 2.0f); + m_ReflectionsLevel = clamp(m_ReflectionsLevel, -10000.0f, 1000.0f); + m_ReverbLevel = clamp(m_ReverbLevel, -10000.0f, 2000.0f); + m_ReverbDelay = clamp(m_ReverbDelay, 0.0f, 0.1f); + m_Diffusion = clamp(m_Diffusion, 0.0f, 100.0f); + m_Density = clamp(m_Density,0.0f, 100.0f); + m_HFReference = clamp(m_HFReference, 20.0f, 20000.0f); + m_RoomLF = clamp(m_RoomLF, -10000.0f, 0.0f); + m_LFReference = clamp(m_LFReference, 20.0f, 10000.0f); +} + +void AudioReverbFilter::Reset() +{ + Super::Reset(); + + m_DryLevel = 0.0f; + m_Room = 0.0f; + m_RoomHF = 0.0f; + m_RoomRolloff = 10.0f; + m_DecayTime = 1.0f; + m_DecayHFRatio = 0.5f; + m_ReflectionsLevel = -10000.0f; + m_ReverbLevel = 0.0f; + m_ReverbDelay = 0.04f; + m_Diffusion = 100.0f; + m_Density = 100.0f; + m_HFReference = 5000.0f; + m_RoomLF = 0.0f; + m_LFReference = 250.0f; + m_ReflectionsDelay = 0.0f; + m_ReverbPreset = 27; +} + +void AudioReverbFilter::Update() +{ + ChangeProperties(); + if (m_DSP) + { + m_DSP->setParameter(FMOD_DSP_SFXREVERB_DRYLEVEL, m_DryLevel); + m_DSP->setParameter(FMOD_DSP_SFXREVERB_ROOM, m_Room); + m_DSP->setParameter(FMOD_DSP_SFXREVERB_ROOMHF, m_RoomHF); + //m_DSP->setParameter(FMOD_DSP_SFXREVERB_ROOMROLLOFFFACTOR, m_RoomRolloff); + m_DSP->setParameter(FMOD_DSP_SFXREVERB_DECAYTIME, m_DecayTime); + m_DSP->setParameter(FMOD_DSP_SFXREVERB_DECAYHFRATIO, m_DecayHFRatio); + m_DSP->setParameter(FMOD_DSP_SFXREVERB_REFLECTIONSLEVEL, m_ReflectionsLevel); + m_DSP->setParameter(FMOD_DSP_SFXREVERB_REFLECTIONSDELAY, m_ReflectionsDelay); + m_DSP->setParameter(FMOD_DSP_SFXREVERB_REVERBLEVEL, m_ReverbLevel); + m_DSP->setParameter(FMOD_DSP_SFXREVERB_REVERBDELAY, m_ReverbDelay); + m_DSP->setParameter(FMOD_DSP_SFXREVERB_DIFFUSION, m_Diffusion); + m_DSP->setParameter(FMOD_DSP_SFXREVERB_DENSITY, m_Density); + m_DSP->setParameter(FMOD_DSP_SFXREVERB_HFREFERENCE, m_HFReference); + m_DSP->setParameter(FMOD_DSP_SFXREVERB_ROOMLF, m_RoomLF); + m_DSP->setParameter(FMOD_DSP_SFXREVERB_LFREFERENCE, m_LFReference); + } +} + +void AudioReverbFilter::ChangeProperties() +{ + if (m_ReverbPreset < 27) + { + FMOD_REVERB_PROPERTIES prop = ReverbPresets[m_ReverbPreset]; + m_Room = prop.Room; + m_RoomHF = prop.RoomHF; + //m_RoomRolloff = prop.RoomRolloffFactor; + m_DecayTime = prop.DecayTime; + m_DecayHFRatio = prop.DecayHFRatio; + m_ReflectionsLevel = prop.Reflections; + m_ReverbLevel = prop.Reverb; + m_ReverbDelay = prop.ReverbDelay; + m_Diffusion = prop.Diffusion; + m_Density = prop.Density; + m_HFReference = prop.HFReference; + m_RoomLF = prop.RoomLF; + m_LFReference = prop.LFReference; + SetDirty(); + } +} + +void AudioReverbFilter::SetReverbPreset(const int reverbPreset) +{ + m_ReverbPreset = reverbPreset; + ChangeProperties(); + Update(); +} + +template<class TransferFunc> +void AudioReverbFilter::Transfer (TransferFunc& transfer) +{ + Super::Transfer (transfer); + TRANSFER(m_DryLevel); + TRANSFER(m_Room); + TRANSFER(m_RoomHF); + TRANSFER(m_RoomRolloff); + TRANSFER(m_DecayTime); + TRANSFER(m_DecayHFRatio); + TRANSFER(m_ReflectionsLevel); + TRANSFER(m_ReverbLevel); + TRANSFER(m_ReverbDelay); + TRANSFER(m_Diffusion); + TRANSFER(m_Density); + TRANSFER(m_HFReference); + TRANSFER(m_RoomLF); + TRANSFER(m_LFReference); + TRANSFER(m_ReflectionsDelay); + TRANSFER(m_ReverbPreset); +} + + +IMPLEMENT_CLASS (AudioReverbFilter) +IMPLEMENT_OBJECT_SERIALIZE (AudioReverbFilter) + +#endif //ENABLE_AUDI diff --git a/Runtime/Audio/AudioReverbFilter.h b/Runtime/Audio/AudioReverbFilter.h new file mode 100644 index 0000000..df2a7ba --- /dev/null +++ b/Runtime/Audio/AudioReverbFilter.h @@ -0,0 +1,96 @@ +#ifndef __AUDIOREVERBFILTER_H__ +#define __AUDIOREVERBFILTER_H__ + +#include "Configuration/UnityConfigure.h" +#if ENABLE_AUDIO_FMOD +#include "AudioSourceFilter.h" +#include "Runtime/Utilities/Utility.h" + +/////@TODO: Why does this not have the same parameters as Reverb zone??? + +class AudioReverbFilter : public AudioFilter +{ +public: + REGISTER_DERIVED_CLASS (AudioReverbFilter, AudioFilter) + DECLARE_OBJECT_SERIALIZE (AudioReverbFilter) + + AudioReverbFilter (MemLabelId label, ObjectCreationMode mode); + + virtual void CheckConsistency (); + virtual void Update(); + virtual void AddToManager(); + virtual void Reset(); + + float GetDryLevel() const { return m_DryLevel; } + void SetDryLevel(const float drylevel) { m_DryLevel = drylevel; Update(); SetDirty();} + + float GetRoom() const { return m_Room; } + void SetRoom(const float room) { m_Room = room; Update(); SetDirty();} + + float GetRoomHF() const { return m_RoomHF; } + void SetRoomHF(const float roomhf) { m_RoomHF = roomhf; Update(); SetDirty();} + + float GetRoomLF() const { return m_RoomLF; } + void SetRoomLF(const float roomlf) { m_RoomLF = roomlf; Update(); SetDirty();} + + float GetRoomRolloff() const { return m_RoomRolloff; } + void SetRoomRolloff(const float rolloffscale) { m_RoomRolloff = rolloffscale; Update(); SetDirty();} + + float GetDecayTime() const { return m_DecayTime; } + void SetDecayTime(const float decayTime) { m_DecayTime = decayTime; Update(); SetDirty();} + + float GetDecayHFRatio() const { return m_DecayHFRatio; } + void SetDecayHFRatio(const float decayHFRatio) { m_DecayHFRatio = decayHFRatio; Update(); SetDirty(); } + + float GetReflectionsLevel() const { return m_ReflectionsLevel; } + void SetReflectionsLevel(const float reflectionsLevel) { m_ReflectionsLevel = reflectionsLevel; Update(); SetDirty(); } + + float GetReflectionsDelay() const { return m_ReflectionsDelay; } + void SetReflectionsDelay(const float reflectionsDelay) { m_ReflectionsDelay = reflectionsDelay; Update(); SetDirty();} + + float GetReverbLevel() const { return m_ReverbLevel; } + void SetReverbLevel(const float reverbLevel) { m_ReverbLevel = reverbLevel; Update(); SetDirty();} + + float GetReverbDelay() const { return m_ReverbDelay; } + void SetReverbDelay(const float reverbDelay) { m_ReverbDelay = reverbDelay; Update(); SetDirty();} + + float GetDiffusion() const { return m_Diffusion; } + void SetDiffusion(const float diffusion) { m_Diffusion = diffusion; Update(); SetDirty();} + + float GetDensity() const { return m_Density; } + void SetDensity(const float density) { m_Density = density; Update(); SetDirty();} + + float GetHFReference() const { return m_HFReference; } + void SetHFReference(const float hfReference) { m_HFReference = hfReference; Update(); SetDirty();} + + float GetLFReference() const { return m_LFReference; } + void SetLFReference(const float lfReference) { m_LFReference = lfReference; Update(); SetDirty();} + + int GetReverbPreset() const { return m_ReverbPreset; } + void SetReverbPreset(const int reverbPreset); + +private: + float m_DryLevel; // Dry Level : Mix level of dry signal in output in mB. Ranges from -10000.0 to 0.0. Default is 0. + float m_Room; // Room : Room effect level at low frequencies in mB. Ranges from -10000.0 to 0.0. Default is 0.0. + float m_RoomHF; // Room HF : Room effect high-frequency level re. low frequency level in mB. Ranges from -10000.0 to 0.0. Default is 0.0. + float m_RoomRolloff; // Room Rolloff : Like DS3D flRolloffFactor but for room effect. Ranges from 0.0 to 10.0. Default is 10.0 + float m_DecayTime; // Reverberation decay time at low-frequencies in seconds. Ranges from 0.1 to 20.0. Default is 1.0. + float m_DecayHFRatio; // Decay HF Ratio : High-frequency to low-frequency decay time ratio. Ranges from 0.1 to 2.0. Default is 0.5. + float m_ReflectionsLevel; // Early reflections level relative to room effect in mB. Ranges from -10000.0 to 1000.0. Default is -10000.0. + float m_ReflectionsDelay; // Late reverberation level relative to room effect in mB. Ranges from -10000.0 to 2000.0. Default is 0.0. + float m_ReverbLevel; // Reverb : Late reverberation level relative to room effect in mB. Ranges from -10000.0 to 2000.0. Default is 0.0. + float m_ReverbDelay; // Late reverberation delay time relative to first reflection in seconds. Ranges from 0.0 to 0.1. Default is 0.04. + float m_Diffusion; // Reverberation diffusion (echo density) in percent. Ranges from 0.0 to 100.0. Default is 100.0. + float m_Density; // Reverberation density (modal density) in percent. Ranges from 0.0 to 100.0. Default is 100.0. + float m_HFReference; // HF Reference : Reference high frequency in Hz. Ranges from 20.0 to 20000.0. Default is 5000.0. + float m_RoomLF; // Room effect low-frequency level in mB. Ranges from -10000.0 to 0.0. Default is 0.0. + float m_LFReference; // Reference low-frequency in Hz. Ranges from 20.0 to 1000.0. Default is 250.0. + int m_ReverbPreset; ///< enum { Off = 0, Generic = 1, PaddedCell = 2, Room = 3, Bathroom = 4, Livingroom = 5, Stoneroom = 6, Auditorium = 7, Concerthall = 8, Cave = 9, Arena = 10, Hangar = 11, CarpettedHallway = 12, Hallway = 13, StoneCorridor = 14, Alley = 15, Forest = 16, City = 17, Mountains = 18, Quarry = 19, Plain = 20, Parkinglot = 21, Sewerpipe = 22, Underwater = 23, Drugged = 24, Dizzy = 25, Psychotic = 26, User = 27 } + + void ChangeProperties(); +}; + +#endif //ENABLE_AUDIO +#endif // __AUDIOREVERBFILTER_H__ + + diff --git a/Runtime/Audio/AudioReverbZone.cpp b/Runtime/Audio/AudioReverbZone.cpp new file mode 100644 index 0000000..b76080d --- /dev/null +++ b/Runtime/Audio/AudioReverbZone.cpp @@ -0,0 +1,260 @@ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" +#if ENABLE_AUDIO_FMOD +#include "AudioReverbZone.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "AudioManager.h" +#include "Runtime/Graphics/Transform.h" +#include "AudioBehaviour.h" +#include "Runtime/Audio/correct_fmod_includer.h" +#include "Runtime/Utilities/Utility.h" +#include "AudioTypes.h" + +extern FMOD_REVERB_PROPERTIES ReverbPresets []; + +AudioReverbZone::AudioReverbZone (MemLabelId label, ObjectCreationMode mode) : + Super(label, mode), + m_MinDistance(10.0f), + m_MaxDistance(15.0f), + m_FMODReverb(NULL), + m_ReverbPreset(1), + m_Node(this) +{ + ChangeProperties(); +} + +AudioReverbZone::~AudioReverbZone () +{ + Cleanup(); + GetAudioManager().RemoveAudioReverbZone(this); +} + +void AudioReverbZone::Cleanup() +{ + if (m_FMODReverb) + { + m_FMODReverb->release(); + m_FMODReverb = NULL; + } +} + +void AudioReverbZone::VerifyValues() +{ + if (m_MinDistance < 0) + m_MinDistance = 0; + if (m_MaxDistance < m_MinDistance) + m_MaxDistance = m_MinDistance; + m_Room = clamp(m_Room, -10000, 0); + m_RoomHF = clamp(m_RoomHF, -10000, 0); + m_RoomLF = clamp(m_RoomLF, -10000, 0); + m_DecayTime = clamp(m_DecayTime, 0.1f, 20.0f); + m_DecayHFRatio = clamp(m_DecayHFRatio, 0.1f, 2.0f); + m_Reflections = clamp(m_Reflections, -10000, 1000); + m_ReflectionsDelay = clamp(m_ReflectionsDelay,0.0f, 0.3f); + m_Reverb = clamp(m_Reverb, -10000, 2000); + m_ReverbDelay = clamp(m_ReverbDelay, 0.0f, 0.1f); + m_HFReference = clamp(m_HFReference, 1000.0f, 20000.0f); + m_LFReference = clamp(m_LFReference, 20.0f, 1000.0f); + m_RoomRolloffFactor = clamp(m_RoomRolloffFactor, 0.0f, 10.0f); + m_Diffusion = clamp(m_Diffusion, 0.0f, 100.0f); + m_Density = clamp(m_Density, 0.0f, 100.0f); +} + + +void AudioReverbZone::Reset() +{ + Super::Reset(); + + m_MinDistance = 10.0f; + m_MaxDistance = 15.0f; + m_ReverbPreset = 1; + + ChangeProperties(); +} + +void AudioReverbZone::CheckConsistency () +{ + Super::CheckConsistency(); + VerifyValues(); +} + + +void AudioReverbZone::RemoveFromManager() +{ + if (m_FMODReverb) + m_FMODReverb->setActive(false); + GetAudioManager().RemoveAudioReverbZone(this); +} + + +void AudioReverbZone::AddToManager() +{ + Init(); + GetAudioManager().AddAudioReverbZone(this); +} + +void AudioReverbZone::Init() +{ + if (m_FMODReverb == NULL) + { + FMOD_RESULT result = GetAudioManager().GetFMODSystem()->createReverb( &m_FMODReverb ); + if (FMOD_OK != result) + ErrorString(FMOD_ErrorString(result)); + } + + m_FMODReverb->setActive(true); + + SetFMODValues(); + + // needed, otherwise sounds playing in the very first frame may not be connected to the master reverb dsp unit + GetAudioManager().GetFMODSystem()->update(); +} + +void AudioReverbZone::SetFMODValues () +{ + if (m_FMODReverb) + { + Vector3f p = GetComponent(Transform).GetPosition(); + FMOD_RESULT result; + result = m_FMODReverb->set3DAttributes(reinterpret_cast<FMOD_VECTOR*> (&p), m_MinDistance, m_MaxDistance); + Assert(FMOD_OK == result); + FMOD_REVERB_PROPERTIES prop = GetReverbProperty(m_ReverbPreset); + result = m_FMODReverb->setProperties(&prop); + Assert(FMOD_OK == result); + } +} + +void AudioReverbZone::ChangeProperties() +{ + FMOD_REVERB_PROPERTIES prop = GetReverbProperty(m_ReverbPreset); + // read back values + m_Room = prop.Room; + m_RoomHF = prop.RoomHF; + m_DecayTime = prop.DecayTime; + m_DecayHFRatio = prop.DecayHFRatio; + m_Reflections = prop.Reflections; + m_ReflectionsDelay = prop.ReflectionsDelay; + m_Reverb = prop.Reverb; + m_ReverbDelay = prop.ReverbDelay; + m_HFReference = prop.HFReference; + m_RoomRolloffFactor = 0.0F; + m_Diffusion = prop.Diffusion; + m_Density = prop.Density; + m_RoomLF = prop.RoomLF; + m_LFReference = prop.LFReference; + +} + +void AudioReverbZone::AwakeFromLoad(AwakeFromLoadMode mode) +{ + Super::AwakeFromLoad(mode); + ChangeProperties(); + SetFMODValues(); + SetDirty(); // ?? +} + +float AudioReverbZone::GetMinDistance() const +{ + return m_MinDistance; +} + +float AudioReverbZone::GetMaxDistance() const +{ + return m_MaxDistance; +} + +void AudioReverbZone::Update() +{ + Assert (m_FMODReverb); + + // Position + Vector3f p = GetComponent(Transform).GetPosition(); + FMOD_VECTOR& pos = UNITYVEC2FMODVEC(p); + m_FMODReverb->set3DAttributes(&pos, m_MinDistance, m_MaxDistance); +} + +void AudioReverbZone::SetMinDistance(float minDistance) +{ + m_MinDistance = minDistance; + VerifyValues(); + SetFMODValues(); + SetDirty(); +} + +void AudioReverbZone::SetMaxDistance(float maxDistance) +{ + m_MaxDistance = maxDistance; + VerifyValues(); + SetFMODValues(); + SetDirty(); +} + +void AudioReverbZone::SetReverbPreset(int reverbPreset) +{ + if (m_ReverbPreset != reverbPreset) + { + m_ReverbPreset = reverbPreset; + ChangeProperties(); + SetDirty(); + } +} + +FMOD_REVERB_PROPERTIES AudioReverbZone::GetReverbProperty(int preset) +{ + if (preset < 27) + return ReverbPresets [preset]; + else + { + FMOD_REVERB_PROPERTIES prop = FMOD_PRESET_OFF; + prop.Room = m_Room; // room effect level (at mid frequencies) + prop.RoomHF = m_RoomHF; // relative room effect level at high frequencies + prop.DecayTime = m_DecayTime; // reverberation decay time at mid frequencies + prop.DecayHFRatio = m_DecayHFRatio; // high-frequency to mid-frequency decay time ratio + prop.Reflections = m_Reflections; // early reflections level relative to room effect + prop.ReflectionsDelay = m_ReflectionsDelay; // initial reflection delay time + prop.Reverb = m_Reverb; // late reverberation level relative to room effect + prop.ReverbDelay = m_ReverbDelay; // late reverberation delay time relative to initial reflection + prop.HFReference = m_HFReference; // reference high frequency (hz) + //prop.RoomRolloffFactor = m_RoomRolloffFactor; // like rolloffscale in global settings, but for reverb room size effect + prop.Diffusion = m_Diffusion; // Value that controls the echo density in the late reverberation decay + prop.Density = m_Density; // Value that controls the modal density in the late reverberation decay + prop.RoomLF = m_RoomLF; + prop.LFReference = m_LFReference; // reference low frequency (hz) + return prop; + } +} + + +int AudioReverbZone::GetReverbPreset() const +{ + return m_ReverbPreset; +} + + +template<class TransferFunc> +void AudioReverbZone::Transfer (TransferFunc& transfer) +{ + Super::Transfer (transfer); + TRANSFER(m_MinDistance); + TRANSFER(m_MaxDistance); + TRANSFER(m_ReverbPreset); + TRANSFER(m_Room); // room effect level (at mid frequencies) + TRANSFER(m_RoomHF); // relative room effect level at high frequencies + TRANSFER(m_DecayTime); // reverberation decay time at mid frequencies + TRANSFER(m_DecayHFRatio); // high-frequency to mid-frequency decay time ratio + TRANSFER(m_Reflections); // early reflections level relative to room effect + TRANSFER(m_ReflectionsDelay); // initial reflection delay time + TRANSFER(m_Reverb); // late reverberation level relative to room effect + TRANSFER(m_ReverbDelay); // late reverberation delay time relative to initial reflection + TRANSFER(m_HFReference); // reference high frequency (hz) + TRANSFER(m_RoomRolloffFactor); // like rolloffscale in global settings, but for reverb room size effect + TRANSFER(m_Diffusion); // Value that controls the echo density in the late reverberation decay + TRANSFER(m_Density); // Value that controls the modal density in the late reverberation decay + TRANSFER(m_LFReference); // reference low frequency (hz) + TRANSFER(m_RoomLF); +} + + +IMPLEMENT_CLASS (AudioReverbZone) +IMPLEMENT_OBJECT_SERIALIZE (AudioReverbZone) +#endif //ENABLE_AUDIO diff --git a/Runtime/Audio/AudioReverbZone.h b/Runtime/Audio/AudioReverbZone.h new file mode 100644 index 0000000..8e2c084 --- /dev/null +++ b/Runtime/Audio/AudioReverbZone.h @@ -0,0 +1,109 @@ +#ifndef __AUDIOREVERBZONE_H__ +#define __AUDIOREVERBZONE_H__ + +#if ENABLE_AUDIO_FMOD +#include "Runtime/GameCode/Behaviour.h" +#include "Runtime/Audio/correct_fmod_includer.h" +#include "Runtime/Utilities/LinkedList.h" + + +class AudioReverbZone : public Behaviour +{ +public: + REGISTER_DERIVED_CLASS (AudioReverbZone, Behaviour) + DECLARE_OBJECT_SERIALIZE (AudioReverbZone) + + /** + * Construction/Destruction + **/ + AudioReverbZone (MemLabelId label, ObjectCreationMode mode); + // virtual ~AudioSource (); declared-by-macro + + virtual void CheckConsistency (); + virtual void Update(); + virtual void AwakeFromLoad(AwakeFromLoadMode mode); + virtual void AddToManager(); + virtual void RemoveFromManager(); + + float GetMinDistance() const; + void SetMinDistance(float minDistance); + + float GetMaxDistance() const; + void SetMaxDistance(float maxDistance); + + void SetReverbPreset(int preset); + int GetReverbPreset() const; + + void SetRoom(int room) { m_Room = room; VerifyValues(); SetFMODValues(); SetDirty(); } + int GetRoom() const { return m_Room; } + void SetRoomHF(int roomHF) { m_RoomHF = roomHF; VerifyValues(); SetFMODValues(); SetDirty(); } + int GetRoomHF() const { return m_RoomHF; } + void SetDecayTime(float decayTime) { m_DecayTime = decayTime; VerifyValues(); SetFMODValues(); SetDirty();} + float GetDecayTime() const { return m_DecayTime; } + void SetDecayHFRatio(float decayHFRatio) { m_DecayHFRatio = decayHFRatio; VerifyValues(); SetFMODValues(); SetDirty();} + float GetDecayHFRatio() const { return m_DecayHFRatio; } + void SetReflectionsDelay(float reflections) { m_ReflectionsDelay = (int)reflections; VerifyValues(); SetFMODValues(); SetDirty();} + void SetReflectionsDelay(int reflections) { m_ReflectionsDelay = reflections; VerifyValues(); SetFMODValues(); SetDirty();} + int GetReflectionsDelay() const { return (int)m_ReflectionsDelay; } + void SetReflections(int reflections) { m_Reflections = reflections; VerifyValues(); SetFMODValues(); SetDirty();} + int GetReflections() const { return m_Reflections; } + void SetReverb(int reverb) { m_Reverb = reverb; VerifyValues(); SetFMODValues(); SetDirty();} + int GetReverb() const { return m_Reverb; } + void SetReverbDelay(float reverbDelay) { m_ReverbDelay = reverbDelay; VerifyValues(); SetFMODValues(); SetDirty();} + int GetReverbDelay() const { return (int)m_ReverbDelay; } + void SetHFReference(float hfReference) { m_HFReference = hfReference; VerifyValues(); SetFMODValues(); SetDirty();} + float GetHFReference() const { return m_HFReference; } + void SetRoomRolloffFactor(float rolloffFactor) { m_RoomRolloffFactor = rolloffFactor; VerifyValues(); SetFMODValues(); SetDirty();} + float GetRoomRolloffFactor() const { return m_RoomRolloffFactor; } + void SetDiffusion(float diffusion) { m_Diffusion = diffusion; VerifyValues(); SetFMODValues(); SetDirty();} + float GetDiffusion() const { return m_Diffusion; } + void SetDensity(float density) { m_Density = density; VerifyValues(); SetFMODValues(); SetDirty();} + float GetDensity() const { return m_Density; } + void SetRoomLF(int roomLF) { m_RoomLF = roomLF; VerifyValues(); SetFMODValues(); SetDirty();} + int GetRoomLF() const { return m_RoomLF; } + void SetLFReference(float lfReference) { m_LFReference = lfReference; VerifyValues(); SetFMODValues(); SetDirty();} + float GetLFReference() const { return m_LFReference; } + + void Cleanup(); + void Init(); + + virtual void Reset(); + +private: + ListNode<AudioReverbZone> m_Node; + + void SetFMODValues (); + void VerifyValues(); + void ChangeProperties(); + + float m_MinDistance; + float m_MaxDistance; + int m_ReverbPreset; ///< enum { Off = 0, Generic = 1, PaddedCell = 2, Room = 3, Bathroom = 4, Livingroom = 5, Stoneroom = 6, Auditorium = 7, Concerthall = 8, Cave = 9, Arena = 10, Hangar = 11, CarpettedHallway = 12, Hallway = 13, StoneCorridor = 14, Alley = 15, Forest = 16, City = 17, Mountains = 18, Quarry = 19, Plain = 20, Parkinglot = 21, Sewerpipe = 22, Underwater = 23, Drugged = 24, Dizzy = 25, Psychotic = 26, User = 27 } + + int m_Room; // room effect level (at mid frequencies) + int m_RoomHF; // relative room effect level at high frequencies + int m_RoomLF; // relative room effect level at low frequencies + + float m_DecayTime; // reverberation decay time at mid frequencies + float m_DecayHFRatio; // high-frequency to mid-frequency decay time ratio + int m_Reflections; // early reflections level relative to room effect + float m_ReflectionsDelay; // initial reflection delay time + int m_Reverb; // late reverberation level relative to room effect + float m_ReverbDelay; // late reverberation delay time relative to initial reflection + float m_HFReference; // reference high frequency (hz) + float m_LFReference; // reference low frequency (hz) + float m_RoomRolloffFactor; // like rolloffscale in global settings, but for reverb room size effect + float m_Diffusion; // Value that controls the echo density in the late reverberation decay + float m_Density; // Value that controls the modal density in the late reverberation decay + + + + FMOD::Reverb* m_FMODReverb; + + FMOD_REVERB_PROPERTIES GetReverbProperty(int preset); + + friend class AudioManager; +}; + +#endif //ENABLE_AUDIO_FMOD +#endif // __AUDIOREVERBZONE_H__ diff --git a/Runtime/Audio/AudioScriptBufferManager.cpp b/Runtime/Audio/AudioScriptBufferManager.cpp new file mode 100644 index 0000000..4ec57da --- /dev/null +++ b/Runtime/Audio/AudioScriptBufferManager.cpp @@ -0,0 +1,138 @@ +/* + * AudioScriptBufferManager.cpp + * AllTargets.workspace + * + * Created by Søren Christiansen on 8/22/11. + * Copyright 2011 Unity Technologies. All rights reserved. + * + */ +#include "UnityPrefix.h" +#include "AudioScriptBufferManager.h" +#include "Runtime/Scripting/ScriptingExportUtility.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Mono/MonoManager.h" +#include "Runtime/Scripting/Backend/ScriptingBackendApi.h" +#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h" + +#if ENABLE_AUDIO_FMOD + +AudioScriptBufferManager::AudioScriptBufferManager(unsigned PCMArraySize, unsigned DSPFilterArraySize) : + m_PCMReadArray(SCRIPTING_NULL) + ,m_DSPFilterArray(SCRIPTING_NULL) + ,m_PCMReadArrayGCHandle(0) + ,m_DSPFilterArrayGCHandle(0) + ,m_DSPFilterArrayOrigLength(0) + ,m_PCMReadArrayOrigLength(0) +{ + Init(PCMArraySize, DSPFilterArraySize); +} + +AudioScriptBufferManager::~AudioScriptBufferManager() +{ +} + +void AudioScriptBufferManager::DidReloadDomain() +{ + Cleanup(); + Init(m_PCMReadArrayOrigLength, m_DSPFilterArrayOrigLength); +} + +void AudioScriptBufferManager::Init(unsigned PCMArraySize, unsigned DSPFilterArraySize) +{ + // Create shared MonoArray for callbacks and DSPs + m_PCMReadArrayOrigLength = PCMArraySize; + #if ENABLE_MONO + ScriptingClassPtr klass = mono_class_from_name (mono_get_corlib (), "System", "Single"); + m_PCMReadArray = mono_array_new (mono_domain_get (), klass, m_PCMReadArrayOrigLength); + m_PCMReadArrayGCHandle = mono_gchandle_new((MonoObject*)m_PCMReadArray, 1); + #else + ScriptingClassPtr klass = GetScriptingTypeRegistry().GetType("System", "Single"); + m_PCMReadArray = CreateScriptingArray<float>(klass, m_PCMReadArrayOrigLength); + m_PCMReadArrayGCHandle = scripting_gchandle_new(m_PCMReadArray); + #endif + + m_DSPFilterArrayOrigLength = DSPFilterArraySize; + #if ENABLE_MONO + m_DSPFilterArray = mono_array_new (mono_domain_get (), klass, m_DSPFilterArrayOrigLength ); + m_DSPFilterArrayGCHandle = mono_gchandle_new((MonoObject*)m_DSPFilterArray, 1); + #else + m_DSPFilterArray = CreateScriptingArray<float>(klass, m_DSPFilterArrayOrigLength); + m_DSPFilterArrayGCHandle = scripting_gchandle_new(m_DSPFilterArray); + #endif +} + +void AudioScriptBufferManager::Cleanup() +{ + #if ENABLE_SCRIPTING + // Cleanup mono arrays + if (m_PCMReadArray) + { + #if ENABLE_MONO + PatchLength(m_PCMReadArray, m_PCMReadArrayOrigLength); + #endif + scripting_gchandle_free(m_PCMReadArrayGCHandle); + m_PCMReadArray = SCRIPTING_NULL; + m_PCMReadArrayGCHandle = 0; + } + if (m_DSPFilterArray) + { + #if ENABLE_MONO + PatchLength(m_DSPFilterArray, m_DSPFilterArrayOrigLength); + #endif + scripting_gchandle_free(m_DSPFilterArrayGCHandle); + m_DSPFilterArray = SCRIPTING_NULL; + m_DSPFilterArrayGCHandle = 0; + } + #endif +} + +void AudioScriptBufferManager::GetPCMReadArray(unsigned length, ScriptingArrayPtr &array) +{ + #if ENABLE_SCRIPTING + Assert(length <= m_PCMReadArrayOrigLength); + unsigned curLength = GetScriptingArraySize(m_PCMReadArray); + if (length != curLength) + { + #if ENABLE_MONO + PatchLength(m_PCMReadArray, length); + #else + ScriptingClassPtr klass = GetMonoManager().GetCommonClasses().floatSingle; + array = CreateScriptingArray(Scripting::GetScriptingArrayStart<float>(m_PCMReadArray), length, klass); + return; + #endif + } + array = m_PCMReadArray; + #endif +} + +void AudioScriptBufferManager::GetDSPFilterArray(unsigned length, ScriptingArrayPtr &array) +{ + #if ENABLE_SCRIPTING + Assert(length <= m_DSPFilterArrayOrigLength); + unsigned curLength = GetScriptingArraySize(m_DSPFilterArray); + if (length != curLength) + { + #if ENABLE_MONO + PatchLength(m_DSPFilterArray, length); + #else + ScriptingClassPtr klass = GetMonoManager().GetCommonClasses().floatSingle; + array = CreateScriptingArray(Scripting::GetScriptingArrayStart<float>(m_DSPFilterArray), length, klass); + return; + #endif + } + array = m_DSPFilterArray; + #endif +} + + +#if ENABLE_MONO + +void AudioScriptBufferManager::PatchLength(ScriptingArrayPtr array, unsigned newlength) +{ + char* pos = sizeof(uintptr_t)*3 + (char*)array; + *((UInt32*)pos) = (UInt32)newlength; +} + +#endif + +#endif // ENABLE_AUDIO_FMOD diff --git a/Runtime/Audio/AudioScriptBufferManager.h b/Runtime/Audio/AudioScriptBufferManager.h new file mode 100644 index 0000000..12ef84d --- /dev/null +++ b/Runtime/Audio/AudioScriptBufferManager.h @@ -0,0 +1,48 @@ +/* + * AudioScriptBufferManager.h + * AllTargets.workspace + * + * Created by Søren Christiansen on 8/22/11. + * Copyright 2011 Unity Technologies. All rights reserved. + * + */ +#ifndef UNITY_AUDIOSCRIPTBUFFERMANAGER_H +#define UNITY_AUDIOSCRIPTBUFFERMANAGER_H + +#if ENABLE_AUDIO_FMOD + +#include "Runtime/Scripting/Backend/ScriptingTypes.h" + +struct MonoArray; + + +class AudioScriptBufferManager { +public: + AudioScriptBufferManager(unsigned PCMArraySize, unsigned DSPFilterArraySize); + ~AudioScriptBufferManager(); + + void Init(unsigned PCMArraySize, unsigned DSPFilterArraySize); + void Cleanup(); + void DidReloadDomain(); + + void GetDSPFilterArray(unsigned length, ScriptingArrayPtr &array); + void GetPCMReadArray(unsigned length, ScriptingArrayPtr &array); + +private: + ScriptingArrayPtr m_PCMReadArray; + int m_PCMReadArrayGCHandle; + unsigned m_PCMReadArrayOrigLength; + + ScriptingArrayPtr m_DSPFilterArray; + int m_DSPFilterArrayGCHandle; + unsigned m_DSPFilterArrayOrigLength; + + #if ENABLE_MONO + inline void PatchLength(ScriptingArrayPtr array, unsigned newlength); + #endif +}; + +#endif // ENABLE_AUDIO_FMOD + +#endif // UNITY_AUDIOSCRIPTBUFFERMANAGER_H + diff --git a/Runtime/Audio/AudioSource.Callbacks.cpp b/Runtime/Audio/AudioSource.Callbacks.cpp new file mode 100644 index 0000000..246450d --- /dev/null +++ b/Runtime/Audio/AudioSource.Callbacks.cpp @@ -0,0 +1,100 @@ +/* + * AudioSource.Callbacks.cpp + * Xcode + * + * Created by Søren Christiansen on 10/12/09. + * Copyright 2009 Unity Technologies. All rights reserved. + * + */ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" +#if ENABLE_AUDIO +#include "AudioSource.h" +#include "AudioManager.h" +#include "Runtime/Utilities/Utility.h" + +/** + * Do log, linear and custom rolloff calculation to avoid interpolating the (animation)curve + **/ +float AudioSource::CalculateVolumeModifierForDistance(float distance) +{ + float maxDistance, minDistance, rolloffScale; + RolloffMode rolloffMode; + + rolloffScale = GetAudioManager().GetRolloffScale(); + + maxDistance = GetMaxDistance(); + minDistance = GetMinDistance(); + rolloffMode = GetRolloffMode(); + + register float gain = 1.0f; + + switch (rolloffMode) { + case kRolloffLinear: + { + float range = maxDistance-minDistance; + if (range<=0) + gain=1.0f; + else + { + distance = maxDistance - distance; + gain = distance / range; + } + } + break; + case kRolloffLogarithmic: + { + if ((distance > minDistance) && (rolloffScale != 1.0f)) + { + distance -= minDistance; + distance *= rolloffScale; + distance += minDistance; + } + + if (distance < .000001f) + { + distance = .000001f; + } + gain = minDistance / distance; + } + break; + case kRolloffCustom: + { + //@TODO: maxDistance can be 0.0F in that case the audio will play back incorrectly because gain becomes nan + if (maxDistance > 0.0F) + { + const AnimationCurve& curve = GetCustomRolloffCurve(); + gain = curve.Evaluate(distance / maxDistance); + } + } + break; + default: + break; + } + + Assert(IsFinite(gain)); + + if (gain < 0.0f) + gain = 0.0f; + if (gain > 1.0f) + gain = 1.0f; + + return gain; +} + +#if ENABLE_AUDIO_FMOD +float F_CALLBACK AudioSource::rolloffCallback( + FMOD_CHANNEL * c_channel, + float distance) +{ + FMOD::Channel* channel = (FMOD::Channel*)c_channel; + AudioSource* audioSource = AudioSource::GetAudioSourceFromChannel(channel); + + if (audioSource == NULL) + return 0.0f; + + return audioSource->CalculateVolumeModifierForDistance(distance); +} + +#endif +#endif //ENABLE_AUDIO diff --git a/Runtime/Audio/AudioSource.cpp b/Runtime/Audio/AudioSource.cpp new file mode 100644 index 0000000..2128d43 --- /dev/null +++ b/Runtime/Audio/AudioSource.cpp @@ -0,0 +1,1738 @@ +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" +#if ENABLE_AUDIO +#include "AudioSource.h" +#include "AudioManager.h" +#include "Runtime/Graphics/Transform.h" +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "Runtime/BaseClasses/IsPlaying.h" +#include "Runtime/Utilities/Utility.h" +#include "Runtime/Profiler/Profiler.h" +#include "AudioClip.h" +#include "Runtime/Video/MoviePlayback.h" +#include "Runtime/Dynamics/RigidBody.h" +#include "Runtime/Animation/AnimationCurveUtility.h" +#include "AudioListener.h" +#include "AudioSourceFilter.h" +#include "AudioLowPassFilter.h" +#include "AudioEchoFilter.h" +#include "AudioChorusFilter.h" +#include "AudioCustomFilter.h" +#include "Runtime/BaseClasses/GameObject.h" +#include "Runtime/Input/TimeManager.h" +#include "Runtime/GameCode/CallDelayed.h" +#include "Runtime/Audio/correct_fmod_includer.h" +#include "Runtime/Misc/BuildSettings.h" +#include "Runtime/Mono/MonoBehaviour.h" +#include "Runtime/Animation/AnimationCurveUtility.h" +#include "Runtime/Interfaces/IPhysics.h" + +#if SUPPORT_REPRODUCE_LOG +#include "Runtime/Misc/ReproductionLog.h" +#include <fstream> +#endif + +#define Unity_HiWord(x) ((UInt32)((UInt64)(x) >> 32)) +#define Unity_LoWord(x) ((UInt32)(x)) + +inline void ReplaceOrAddSingleCurveValue(float value, AnimationCurve& curve) +{ + AnimationCurve::Keyframe key(0.0f,value); + curve.Assign(&key, &key + 1); +} + +// ------------------------------------------------------------------------ + + +#if ENABLE_PROFILER +int AudioSource::s_AudioSourceCount = 0; +#endif + + +AudioSource::AudioSource (MemLabelId label, ObjectCreationMode mode) : + Super(label, mode) + ,m_PlayOnAwake(true) + ,m_VelocityUpdateMode(kVelocityUpdateModeAuto) + ,m_LastUpdatePosition(Vector3f(0,0,0)) + ,m_Channel(NULL) + ,m_Node (this) + ,m_IgnoreListenerVolume(false) + ,m_samplePosition(0) + ,m_pause(true) + ,m_ScheduledSource(this) + ,m_HasScheduledStartDelay(false) + ,m_HasScheduledEndDelay(false) +#if ENABLE_AUDIO_FMOD + ,m_dryGroup(NULL) + ,m_wetGroup(NULL) + ,m_PlayingDSP(NULL) +#endif +{ +#if ENABLE_PROFILER + s_AudioSourceCount++; +#endif + + m_AudioParameters.pitch = 1.0; + m_AudioParameters.volume = 1.0f; + m_AudioParameters.priority = 128; + m_AudioParameters.loop = false; + m_AudioParameters.pan = 0.0f; + m_AudioParameters.dopplerLevel = 1.0f; + m_AudioParameters.minDistance = 1.0f; + m_AudioParameters.maxDistance = 500.0f; + m_AudioParameters.insideConeAngle = 360.0f; + m_AudioParameters.outsideConeAngle = 360.0f; + m_AudioParameters.outsideConeVolume = 1.0f; + m_AudioParameters.mute = false; + m_AudioParameters.rolloffMode = kRolloffLogarithmic; +#if UNITY_WII + m_AudioParameters.starving = false; +#endif + m_AudioParameters.bypassEffects = false; + m_AudioParameters.bypassListenerEffects = false; + m_AudioParameters.bypassReverbZones = false; + m_AudioParameters.ignoreListenerPause = false; + // curves + ReplaceOrAddSingleCurveValue(1.0f, m_AudioParameters.panLevelCustomCurve); + ReplaceOrAddSingleCurveValue(0.0f, m_AudioParameters.spreadCustomCurve); + + if(GetAudioManagerPtr() != NULL) + AssignProps(); +} + +void AudioSource::Reset() +{ + Super::Reset(); + + m_AudioParameters.pitch = 1.0; + m_AudioParameters.volume = 1.0f; + m_AudioParameters.priority = 128; + m_AudioParameters.loop = false; + m_AudioParameters.pan = 0.0f; + m_AudioParameters.dopplerLevel = 1.0f; + m_AudioParameters.minDistance = 1.0f; + m_AudioParameters.maxDistance = 500.0f; + m_AudioParameters.insideConeAngle = 360.0f; + m_AudioParameters.outsideConeAngle = 360.0f; + m_AudioParameters.outsideConeVolume = 1.0f; + m_AudioParameters.mute = false; + m_AudioParameters.rolloffMode = kRolloffLogarithmic; + m_AudioParameters.bypassEffects = false; + m_AudioParameters.bypassListenerEffects = false; + m_AudioParameters.bypassReverbZones = false; + m_AudioParameters.ignoreListenerPause = false; + + m_PlayOnAwake = true; + + // Curves initial values will be handled in CheckConsistency + m_AudioParameters.rolloffCustomCurve.ResizeUninitialized (0); + m_AudioParameters.panLevelCustomCurve.ResizeUninitialized (0); + m_AudioParameters.spreadCustomCurve.ResizeUninitialized (0); + CheckConsistency (); +} + +AudioSource::~AudioSource () +{ +#if ENABLE_PROFILER + s_AudioSourceCount--; +#endif + +#if ENABLE_AUDIO_FMOD + TearDownGroups(); + + Assert (m_dryGroup == NULL); + Assert (m_wetGroup == NULL); +#endif +} + +void AudioSource::AwakeFromLoad (AwakeFromLoadMode awakeMode) +{ + if (IsActive()) + SetupGroups(); + + AssignProps(); + + if (IsActive() && awakeMode & (kDidLoadFromDisk | kInstantiateOrCreateFromCodeAwakeFromLoad | kActivateAwakeFromLoad)) + m_LastUpdatePosition = GetComponent(Transform).GetPosition(); + + // This calls AddToManager which performs PlayOnAwake + // Thus we call it at the end after properties have been setup. + Super::AwakeFromLoad (awakeMode); +} + +void AudioSource::CheckConsistency () +{ + const float epsilon = 0.000001f; + + m_AudioParameters.volume = clamp01(m_AudioParameters.volume); + m_AudioParameters.priority = clamp(m_AudioParameters.priority,0,255); + m_AudioParameters.pitch = clamp(m_AudioParameters.pitch,-3.0f,3.0f); + m_AudioParameters.dopplerLevel = clamp(m_AudioParameters.dopplerLevel,0.0f,5.0f); + m_AudioParameters.minDistance = m_AudioParameters.minDistance < 0.0f?0.0f:m_AudioParameters.minDistance; + m_AudioParameters.maxDistance = m_AudioParameters.maxDistance < m_AudioParameters.minDistance + epsilon ? m_AudioParameters.minDistance + epsilon : m_AudioParameters.maxDistance; + + if (m_AudioParameters.rolloffCustomCurve.GetKeyCount() < 1) + { + m_AudioParameters.rolloffCustomCurve.AddKey(AnimationCurve::Keyframe(0.0f,1.0f)); + m_AudioParameters.rolloffCustomCurve.AddKey(AnimationCurve::Keyframe(1.0f,0.0f)); + } + if (m_AudioParameters.rolloffCustomCurve.GetKeyCount() == 1) + m_AudioParameters.rolloffCustomCurve.GetKey(0).value = clamp(m_AudioParameters.rolloffCustomCurve.GetKey(0).value, 0.0f, 1.0f); + + if (m_AudioParameters.panLevelCustomCurve.GetKeyCount() < 1) + ReplaceOrAddSingleCurveValue(1.0f, m_AudioParameters.panLevelCustomCurve); + if (m_AudioParameters.panLevelCustomCurve.GetKeyCount() == 1) + m_AudioParameters.panLevelCustomCurve.GetKey(0).value = clamp(m_AudioParameters.panLevelCustomCurve.GetKey(0).value, 0.0f, 1.0f); + + if (m_AudioParameters.spreadCustomCurve.GetKeyCount() < 1) + ReplaceOrAddSingleCurveValue(0.0f, m_AudioParameters.spreadCustomCurve); + if (m_AudioParameters.spreadCustomCurve.GetKeyCount() == 1) + m_AudioParameters.spreadCustomCurve.GetKey(0).value = clamp(m_AudioParameters.spreadCustomCurve.GetKey(0).value, 0.0f, 1.0f); +} + + +void AudioSource::AssignProps() +{ +#if ENABLE_AUDIO_FMOD + SetOutsideConeAngle(m_AudioParameters.outsideConeAngle); + SetInsideConeAngle(m_AudioParameters.insideConeAngle); + SetOutsideConeVolume(m_AudioParameters.outsideConeVolume); + SetupGroups(); + SetDopplerLevel(m_AudioParameters.dopplerLevel); + SetPitch(m_AudioParameters.pitch); +#endif + + SetPriority(m_AudioParameters.priority); + SetMinDistance(m_AudioParameters.minDistance); + SetMaxDistance(m_AudioParameters.maxDistance); + SetPan(m_AudioParameters.pan); + + SetVolume(m_AudioParameters.volume); + SetLoop(m_AudioParameters.loop); + SetMute(m_AudioParameters.mute); + + if (m_AudioClip && m_Channel) + m_Channel->setMode(m_AudioClip->Get3D()?FMOD_3D:FMOD_2D); + + SetRolloffMode(m_AudioParameters.rolloffMode); +} + +void AudioSource::CorrectScheduledTimeAfterUnpause(UInt64 delay) +{ +#if ENABLE_AUDIO_FMOD + if(m_Channel != NULL) + { + unsigned hiclock, loclock; + if(m_HasScheduledStartDelay) + { + m_Channel->getDelay(FMOD_DELAYTYPE_DSPCLOCK_START, &hiclock, &loclock); + FMOD_64BIT_ADD(hiclock, loclock, Unity_HiWord(delay), Unity_LoWord(delay)); + m_Channel->setDelay(FMOD_DELAYTYPE_DSPCLOCK_START, hiclock, loclock); + } + if(m_HasScheduledEndDelay) + { + m_Channel->getDelay(FMOD_DELAYTYPE_DSPCLOCK_END, &hiclock, &loclock); + FMOD_64BIT_ADD(hiclock, loclock, Unity_HiWord(delay), Unity_LoWord(delay)); + m_Channel->setDelay(FMOD_DELAYTYPE_DSPCLOCK_END, hiclock, loclock); + } + } +#endif +} + + +void AudioSource::SetAudioClip(AudioClip *clip) +{ + if(m_AudioClip == PPtr<AudioClip> (clip)) + return; + Stop(true); + m_AudioClip = clip; + SetDirty (); +} + +void AudioSource::Deactivate (DeactivateOperation operation) +{ + // We don't want audio to stop playing when in loading another level, + // so we just ignore the ignore deactivate completely + if (operation != kDeprecatedDeactivateToggleForLevelLoad) + { + Super::Deactivate (operation); + TearDownGroups (); + } +} + +/** + * Setup effect and non-effect groups + **/ +void AudioSource::SetupGroups() +{ +#if ENABLE_AUDIO_FMOD + Assert (GetAudioManagerPtr()); + + FMOD_RESULT result; + + if (!m_dryGroup) + { + result = GetAudioManager().GetFMODSystem()->createChannelGroup("Dry Group", &m_dryGroup); + FMOD_ASSERT(result); + + } + if (!m_wetGroup) + { + result = GetAudioManager().GetFMODSystem()->createChannelGroup("Wet Group", &m_wetGroup); + FMOD_ASSERT(result); + } + + Assert(m_wetGroup); + Assert(m_dryGroup); + + FMOD::ChannelGroup* newParentGroup; + if(m_AudioParameters.bypassListenerEffects) + newParentGroup = (m_IgnoreListenerVolume) ? GetAudioManager().GetChannelGroup_NoFX_IgnoreVolume() : GetAudioManager().GetChannelGroup_NoFX_UseVolume(); + else + newParentGroup = (m_IgnoreListenerVolume) ? GetAudioManager().GetChannelGroup_FX_IgnoreVolume() : GetAudioManager().GetChannelGroup_FX_UseVolume(); + + if(m_AudioParameters.bypassEffects) + { + // Connect dry group directly to new parent group (thus bypassing wet group) + FMOD_RESULT result; + FMOD::ChannelGroup* parentGroup; + result = m_dryGroup->getParentGroup(&parentGroup); FMOD_ASSERT(result); + if(parentGroup != newParentGroup) + { + result = newParentGroup->addGroup(m_dryGroup); FMOD_ASSERT(result); + } + FMOD::DSP* dsphead = NULL; + result = m_wetGroup->getDSPHead(&dsphead); FMOD_ASSERT(result); + result = dsphead->disconnectAll(false, true); FMOD_ASSERT(result); + } + else + { + // Connect dry group to wet group (where DSP effects are attached) and add wet group to new parent group + FMOD_RESULT result; + FMOD::ChannelGroup* parentGroup; + result = m_dryGroup->getParentGroup(&parentGroup); FMOD_ASSERT(result); + if(parentGroup != m_wetGroup) + { + result = m_wetGroup->addGroup(m_dryGroup); FMOD_ASSERT(result); + } + // addGroup must be called in any case since getParentGroup will still return old parent group even though the DSP is no longer connected (and is not visible in the FMOD profiler) + result = newParentGroup->addGroup(m_wetGroup); FMOD_ASSERT(result); + } + + FMOD_REVERB_CHANNELPROPERTIES rev; + memset(&rev, 0, sizeof(rev)); + if(m_AudioParameters.bypassReverbZones) + rev.Room = -10000; + if(m_Channel != NULL) + { + result = m_Channel->setReverbProperties(&rev); + //FMOD_ASSERT(result); + } + for(TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end(); ++it) + { + FMOD::Channel* channel = (*it)->channel; + if(channel != NULL) + { + result = channel->setReverbProperties(&rev); + //FMOD_ASSERT(result); + } + } + +#endif //ENABLE_AUDIO_FMOD +} + +/** + * tear down effect and non-effect groups + **/ +void AudioSource::TearDownGroups() +{ +#if ENABLE_AUDIO_FMOD + FMOD_RESULT result; + + if (m_dryGroup) + { + result = m_dryGroup->release(); + FMOD_ASSERT(result); + m_dryGroup = NULL; + } + if (m_wetGroup) + { + result = m_wetGroup->release(); + FMOD_ASSERT(result); + m_wetGroup = NULL; + } +#endif +} + +void AudioSource::Play(double time) +{ + if(GetAudioManager().IsAudioDisabled()) + return; + + if (! (GetEnabled() && IsActive()) ) + { + WarningStringObject("Can not play a disabled audio source", this); + if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1)) + return; + } + + SetupGroups(); + + /** + * Play/unpause main clip, if any + **/ + AudioClip* clip = m_AudioClip; + if (clip != NULL && !clip->ReadyToPlay()) + return; + + if (m_Channel) + { + FMOD_RESULT result; + +#if ENABLE_AUDIO_FMOD + result = m_Channel->setChannelGroup(m_dryGroup); +#endif + + bool paused; + result = m_Channel->getPaused(&paused); + if (result == FMOD_OK && paused) + { + AssignProps(); + paused = IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a3) && GetAudioManager().GetPause() && !m_AudioParameters.ignoreListenerPause; // overwrite to restart sound + m_Channel->setPaused(paused); + GetAudioManager ().AddAudioSource (this, paused); // to make sure source is put into active or paused sources lists + return; + } + else + Stop(!IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a1)); + } + + if (!m_Channel) + { + if (clip != NULL) + m_Channel = clip->CreateChannel(this); + else + { + #if ENABLE_AUDIO_FMOD + TFilters filters; + if ( GetFilterComponents(filters, true) ) + { + AudioCustomFilter* customFilter = NULL; + filters[0]->getUserData((void**)&customFilter); + if (customFilter) + { + customFilter->SetPlayingSource(this); + m_PlayingDSP = filters[0]; + m_PlayingDSP->remove(); + FMOD_RESULT result = GetAudioManager().GetFMODSystem()->playDSP(FMOD_CHANNEL_FREE, filters[0], true, &m_Channel); + FMOD_ASSERT( result ); + result = m_Channel->setMode ( FMOD_3D ); + FMOD_ASSERT ( result ); + } + else + WarningString(Format("Only custom filters can be played. Please add a custom filter or an audioclip to the audiosource (%s).", GetGameObjectPtr()?GetGameObjectPtr()->GetName():"")); + } + #endif + } + } + + if (m_Channel) + { + FMOD_RESULT result; + +#if ENABLE_AUDIO_FMOD + result = m_Channel->setChannelGroup(m_dryGroup); + ApplyFilters(); +#endif + m_Channel->setUserData(this); + AssignProps(); + UpdateParameters(m_Channel); + +#if ENABLE_MOVIES + // is this a movie playback - note movieplayback (this is *so* ugly) + if ( clip&&clip->GetMovie() ) + clip->GetMovie()->SetAudioChannel(m_Channel); +#endif + // set cached position + m_Channel->setPosition(m_samplePosition, FMOD_TIMEUNIT_PCM); + + // play in sync (this will add it to the manager as well) + GetAudioManager().ScheduleSource(this, time); + + m_pause = false; + bool paused = IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a3) && GetAudioManager().GetPause() && !m_AudioParameters.ignoreListenerPause; + m_Channel->setPaused(paused); + GetAudioManager ().AddAudioSource (this, paused); // to make sure source is put into active or paused sources lists + } +} + +void AudioSource::Pause() +{ + m_pause = true; + if (m_Channel) + { + m_Channel->setPaused(true); + } +} +void AudioSource::PauseOneShots () +{ + // pause one shots + TOneShots::iterator it = m_OneShots.begin(); + for (; it != m_OneShots.end();++it) + { + OneShot& os = **it; + os.channel->setPaused(true); + } +} + +void AudioSource::ResumeOneShots () +{ + /** + * play paused oneshots + **/ + // play one shots + TOneShots::iterator it = m_OneShots.begin(); + for (; it != m_OneShots.end();++it) + { + OneShot& os = **it; + os.channel->setPaused(IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a3) && GetAudioManager().GetPause() && !m_AudioParameters.ignoreListenerPause); + } +} + +void AudioSource::Cleanup() +{ + Stop(true); +#if ENABLE_AUDIO_FMOD + // cleanup filters + const GameObject* go = GetGameObjectPtr(); + + if (go) + { + for (int i=0;i<go->GetComponentCount();i++) + { + AudioFilter* filter = NULL; + filter = dynamic_pptr_cast<AudioFilter*> (&go->GetComponentAtIndex(i)); + if ( filter ) + filter->Cleanup(); + else + { + MonoBehaviour* behaviour = dynamic_pptr_cast<MonoBehaviour*> (&go->GetComponentAtIndex(i)); + if ( behaviour ) + { + AudioCustomFilter* filter = behaviour->GetAudioCustomFilter(); + if ( filter ) filter->Cleanup(); + } + } + } + } + + TearDownGroups(); + +#endif +} + + + +void AudioSource::Stop(bool stopOneShots) +{ + m_HasScheduledStartDelay = false; + m_HasScheduledEndDelay = false; + + if (m_Channel) + m_Channel->stop(); + + m_Channel = NULL; + + if(stopOneShots) + { + // stop one shots (and delete them from the list) + TOneShots::iterator it = m_OneShots.begin(); + for (; it != m_OneShots.end(); ++it) + { + OneShot* os = *it; + os->channel->stop(); + delete os; + } + m_OneShots.clear(); + } + +#if ENABLE_AUDIO_FMOD + if (m_PlayingDSP) + m_PlayingDSP->remove(); + + AudioCustomFilter* filter = NULL; + m_PlayingDSP->getUserData((void**)&filter); + if (filter) + filter->SetPlayingSource(NULL); + + m_PlayingDSP = NULL; +#endif + + if(m_OneShots.empty()) + { +#if ENABLE_AUDIO_FMOD + // remove filters + TFilters filters; + if (GetFilterComponents(filters, false)) + { + for (TFilters::const_iterator it=filters.begin();it!=filters.end();it++) + { + FMOD::DSP* dsp = *it; + FMOD_RESULT result; + result = dsp->remove(); + FMOD_ASSERT(result); + } + + filters.clear(); + } +#endif + + GetAudioManager ().RemoveAudioSource (this); + } +} + + +void AudioSource::Stop(AudioChannel* channel) +{ + if (m_Channel == channel) + { + m_HasScheduledStartDelay = false; + m_HasScheduledEndDelay = false; + + m_Channel->stop(); + m_Channel = NULL; + +#if ENABLE_AUDIO_FMOD + if (m_PlayingDSP) + m_PlayingDSP->remove(); + + AudioCustomFilter* filter = NULL; + m_PlayingDSP->getUserData((void**)&filter); + if (filter) + filter->SetPlayingSource(NULL); + m_PlayingDSP = NULL; +#endif + } + else + { + // stop one shots (and delete them from the list) + TOneShots::iterator it = m_OneShots.begin(); + for (; it != m_OneShots.end(); ++it) + { + OneShot* os = *it; + if(os->channel == channel) + { + m_OneShots.erase(it); + os->channel->stop(); + delete os; + break; + } + } + } + + if(m_Channel == NULL && m_OneShots.empty()) + { +#if ENABLE_AUDIO_FMOD + // remove filters + TFilters filters; + if (GetFilterComponents(filters, false)) + { + for (TFilters::const_iterator it=filters.begin();it!=filters.end();it++) + { + FMOD::DSP* dsp = *it; + FMOD_RESULT result; + result = dsp->remove(); + FMOD_ASSERT(result); + } + + filters.clear(); + } +#endif + GetAudioManager ().RemoveAudioSource (this); + } +} + + +bool AudioSource::IsPlayingScripting () +{ + bool res = IsPlaying (); +#if SUPPORT_REPRODUCE_LOG + if (GetReproduceOutStream()) + *GetReproduceOutStream() << "Audio_IsPlaying " << (int)res << std::endl; + else if (GetReproduceInStream() && GetReproduceVersion() >= 4) + { + if (!CheckReproduceTag("Audio_IsPlaying", *GetReproduceInStream())) + { + ErrorString("Grabbing Audio.IsPlaying but there are Audio_IsPlaying calls recorded"); + return res; + } + + int temp = 0; + *GetReproduceInStream() >> temp; + res = temp; + } +#endif + return res; +} + +bool AudioSource::IsPlaying () const +{ + if (m_Channel) + { + FMOD_RESULT result; + + bool isPlaying, isPaused; + + result = m_Channel->isPlaying(&isPlaying); + result = m_Channel->getPaused(&isPaused); + + return (result == FMOD_OK) && ((!m_pause) || ((isPlaying) && (!isPaused))); + + } + return false; +} + +bool AudioSource::IsPaused () const +{ + bool isPaused = false; + if (m_Channel) + m_Channel->getPaused(&isPaused); + return isPaused; +} + + +/** + * Update channel properties + * @param channel The channel to update + * @param oneshot Is this a oneshot? + **/ + +inline bool AudioSource::UpdateParameters(AudioChannel* channel, OneShot* oneshot /* = NULL */) +{ + // is this a valid channel? + bool isPlaying = false; + + FMOD_RESULT result = channel==NULL?FMOD_ERR_INVALID_HANDLE:channel->isPlaying(&isPlaying); + + if (result == FMOD_ERR_INVALID_HANDLE) + return false; + + // Position + Vector3f p = GetComponent(Transform).GetPosition(); + + // Velocity + // get velocity from rigidbody if present + Vector3f v; +#if ENABLE_PHYSICS + Rigidbody* rb = QueryComponent(Rigidbody); + if (rb) + v = GetIPhysics()->GetRigidBodyVelocity(*rb); + else +#endif + v = (p - m_LastUpdatePosition) * GetInvDeltaTime (); + + // Orientate cone + Vector3f orient = NormalizeSafe( QuaternionToEuler( GetComponent(Transform).GetRotation() ) ); + + channel->set3DConeOrientation(reinterpret_cast<FMOD_VECTOR*> (&orient)); + + +#if ENABLE_AUDIO_FMOD + FMOD_VECTOR& pos = UNITYVEC2FMODVEC(p); + FMOD_VECTOR& vel = UNITYVEC2FMODVEC(v); + // Update position and velocity + channel->set3DAttributes(&pos,&vel); +#endif + // update distance animated props + // distance to listener + // @TODO refactor when we support more than one listener + const AudioListener* listener = GetAudioManager().GetAudioListener(); + if (listener != NULL) + { + const Vector3f& position = listener->GetPosition(); + float distance = Magnitude(p - position); + + // Pan Level + const AnimationCurve& curve = m_AudioParameters.panLevelCustomCurve; + if (curve.GetKeyCount() != 1) + { + std::pair<float, float> curverange = curve.GetRange(); + float panLevel = curve.Evaluate(clamp(distance / m_AudioParameters.maxDistance, curverange.first, curverange.second)); + channel->set3DPanLevel(panLevel); + } + else + { + channel->set3DPanLevel(curve.GetKey(0).value); + } + +#if UNITY_FLASH || UNITY_WEBGL + if (GetAudioClip()->Get3D()) + channel->SetDistanceVolumeMultiplier(CalculateVolumeModifierForDistance(distance)); +#endif + + // Spread + if (!m_AudioClip.IsNull()) + { + + const AnimationCurve& curve = m_AudioParameters.spreadCustomCurve; + if (curve.GetKeyCount() != 1) + { + std::pair<float, float> curverange = curve.GetRange(); + float spread = curve.Evaluate(clamp(distance / m_AudioParameters.maxDistance, curverange.first, curverange.second)); + channel->set3DSpread(spread * 360.0f); + } + else + { + channel->set3DSpread(curve.GetKey(0).value * 360.0f); + } + } + +#if ENABLE_AUDIO_FMOD + // Low pass filter + AudioLowPassFilter* lowPassFilter = QueryComponent(AudioLowPassFilter); + if (lowPassFilter != NULL) + { + const AnimationCurve& curve = lowPassFilter->GetCustomLowpassLevelCurve(); + // Only use the curve if there's more than 1 key + if (curve.GetKeyCount() > 1) + { + std::pair<float, float> curverange = curve.GetRange(); + float lowpassLevel = curve.Evaluate(clamp(distance / m_AudioParameters.maxDistance, curverange.first, curverange.second)); + lowpassLevel = 22000.0f - (21990.0f*lowpassLevel); + lowPassFilter->SetCutoffFrequency(lowpassLevel); + } + } +#endif + } + + // update last position + m_LastUpdatePosition = p; + return true; +} + +void AudioSource::DoUpdate() +{ + if (! (GetEnabled() && IsActive() ) ) + return; + +#if UNITY_WII + // Check if we're not starving for data, starving can occur during disk eject + FMOD_OPENSTATE openState; + bool starving; + bool diskbusy; + m_AudioClip->GetSound()->getOpenState(&openState, NULL, &starving, &diskbusy); + if (starving != m_AudioParameters.starving) + { + m_AudioParameters.starving = starving; + if (starving) + { + // Mute all channels if we're starving for data, don't use AudioSource::SetMute because it will overwrite m_AudioParameters.mute + if (m_Channel) m_Channel->setMute(true); + for (TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end();++it) + (*it)->channel->setMute(true); + } + else + { + // Restore muteness + SetMute(m_AudioParameters.mute); + } + } +#endif + + /** + * One shots + **/ + TOneShots::iterator it = m_OneShots.begin(); + for (; it != m_OneShots.end();) + { + OneShot* os = *it; + bool playing = true; + os->channel->isPlaying(&playing); + + if (!playing) + { + os->channel->stop(); + delete os; + it = m_OneShots.erase(it); + } + else + { + if (!UpdateParameters(os->channel, os)) + { + os->channel->stop(); + delete os; + it = m_OneShots.erase(it); + } + else + ++it; + } + } + + if (!m_Channel) + return; + + //// why is this necessary??? + // check if channel is still valid and/or paused + bool isPaused = false; + + FMOD_RESULT result = m_Channel->getPaused(&isPaused); + + if (result == FMOD_ERR_INVALID_HANDLE) + { + m_HasScheduledStartDelay = false; + m_HasScheduledEndDelay = false; + m_Channel = NULL; + return; + } + + + // @FIX Should we update parameters even though its paused!? + if (isPaused) + return; + + /** + * Source + **/ + UpdateParameters(m_Channel); +} + +void AudioSource::AddToManager () +{ + if (m_PlayOnAwake && IsWorldPlaying() && !IsPlaying() && IsActive() && GetEnabled()) + Play(); +} + +void AudioSource::RemoveFromManager () +{ + Stop(true); +} + +PROFILER_INFORMATION(gAudioSourceUpdateProfile, "AudioSource.Update", kProfilerAudio) + +void AudioSource::Update() +{ + PROFILER_AUTO (gAudioSourceUpdateProfile, NULL); + + if(m_VelocityUpdateMode == kVelocityUpdateModeAuto) + m_VelocityUpdateMode = GetAudioManager().GetAutomaticUpdateMode( GetGameObjectPtr() ); + + if(m_VelocityUpdateMode==kVelocityUpdateModeDynamic) + DoUpdate(); +} + +void AudioSource::FixedUpdate() +{ + if(m_VelocityUpdateMode == kVelocityUpdateModeAuto) + m_VelocityUpdateMode = GetAudioManager().GetAutomaticUpdateMode( GetGameObjectPtr()); + + if(m_VelocityUpdateMode==kVelocityUpdateModeFixed) + DoUpdate(); +} + + +// PROPERTIES + +#define SetFMODParam(setFunction, value)\ +if(m_Channel)m_Channel->setFunction(value);\ +for (TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end();++it)\ + (*it)->channel->setFunction(value); + +#define SetFMODParam2(setFunction, value1, value2)\ +if(m_Channel)m_Channel->setFunction(value1, value2);\ +for (TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end();++it)\ + (*it)->channel->setFunction(value1, value2); + +#define SetFMODParam3(setFunction, value1, value2, value3)\ +if(m_Channel)m_Channel->setFunction(value1, value2, value2);\ +for (TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end();++it)\ + (*it)->channel->setFunction(value1, value2, value3); + + +/// Get/Set pitch of the sound +float AudioSource::GetPitch() const +{ + return m_AudioParameters.pitch; +} +void AudioSource::SetPitch(float pitch) +{ + AudioClip* clip = m_AudioClip; + if(!IsFinite(pitch)) + { + ErrorString("Attempt to set pitch to infinite value in AudioSource::SetPitch ignored!"); + return; + } + if(IsNAN(pitch)) + { + ErrorString("Attempt to set pitch to NaN value in AudioSource::SetPitch ignored!"); + return; + } +#if ENABLE_AUDIO_FMOD + if (clip&&clip->IsMovieAudio()) + pitch = clamp(pitch,0.0f,3.0f); +#endif + m_AudioParameters.pitch = pitch; + if (m_Channel&&clip) + { +#if ENABLE_AUDIO_FMOD + if(IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a1)) + { + FMOD::Sound* sound = NULL; + if(m_Channel->getCurrentSound(&sound) == FMOD_OK) + { + float frequency; + if(sound->getDefaults(&frequency, NULL, NULL, NULL) == FMOD_OK) + { + frequency *= m_AudioParameters.pitch; + Assert(IsFinite(frequency)); + Assert(!IsNAN(frequency)); + m_Channel->setFrequency(frequency); + } + } + } + else +#endif + { + float frequency = m_AudioParameters.pitch * clip->GetFrequency(); + Assert(IsFinite(frequency)); + Assert(!IsNAN(frequency)); + m_Channel->setFrequency(frequency); + } + } + for (TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end();++it) + { +#if ENABLE_AUDIO_FMOD + if(IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a1)) + { + FMOD::Channel* channel = (*it)->channel; + FMOD::Sound* sound = NULL; + if(channel->getCurrentSound(&sound) == FMOD_OK) + { + float frequency; + if(sound->getDefaults(&frequency, NULL, NULL, NULL) == FMOD_OK) + channel->setFrequency(m_AudioParameters.pitch * frequency); + } + } + else +#endif + (*it)->channel->setFrequency(m_AudioParameters.pitch * (*it)->clip->GetFrequency()); + } +} + +void AudioSource::SetVolume (float gain) +{ + m_AudioParameters.volume = clamp01(gain); + + if(m_Channel)m_Channel->setVolume(m_AudioParameters.volume); + for (TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end();++it) + (*it)->channel->setVolume(m_AudioParameters.volume * (*it)->volumeScale); +} + + +// Get/Set volume of the sound +float AudioSource::GetVolume() const +{ + return m_AudioParameters.volume; +} + +void AudioSource::SetPlayOnAwake(bool playOnAwake) +{ + m_PlayOnAwake = playOnAwake; + SetDirty(); +} + +void AudioSource::SetIgnoreListenerPause(bool ignoreListenerPause) +{ + m_AudioParameters.ignoreListenerPause = ignoreListenerPause; + SetDirty(); +} + +bool AudioSource::GetLoop () const +{ + return m_AudioParameters.loop; +} + +void AudioSource::SetLoop (bool loop) +{ + + if (m_Channel) + { + m_Channel->setMode( loop?FMOD_LOOP_NORMAL:FMOD_LOOP_OFF ); + } + // always turn looping off for oneshots + for (TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end();++it) + (*it)->channel->setMode( FMOD_LOOP_OFF ); + + m_AudioParameters.loop = loop; +} + +// Sets how much the 3d engine has an effect on the channel. +float AudioSource::GetPanLevel() const +{ + return m_AudioParameters.panLevelCustomCurve.GetKey(0).value; +} +void AudioSource::SetPanLevel(float level) +{ + // As panlevel is a curve + // setting level resets the curve to 1 key + level = clamp01(level); + ReplaceOrAddSingleCurveValue(level, m_AudioParameters.panLevelCustomCurve); +} + +// Sets the doppler scale for this AudioSource +float AudioSource::GetDopplerLevel() const +{ +#if ENABLE_AUDIO_FMOD + return m_AudioParameters.dopplerLevel; +#else + return 0; +#endif + +} +void AudioSource::SetDopplerLevel(float level) +{ +#if ENABLE_AUDIO_FMOD + m_AudioParameters.dopplerLevel = clamp(level, 0.0F, 5.0F); + SetFMODParam(set3DDopplerLevel, m_AudioParameters.dopplerLevel); +#endif +} + +// Sets the spread angle a 3d stereo ogr multichannel cound in speaker space. +// 0 = all sound channels are located at the same speaker location and is 'mono'. +// 360 = all subchannels are located at the opposite speaker location to the speaker location that it should be according to 3D position. Default = 0. +// Sets how much the 3d engine has an effect on the channel. +float AudioSource::GetSpread() const +{ + return m_AudioParameters.spreadCustomCurve.GetKey(0).value * 360.0f; +} +void AudioSource::SetSpread(float spread) +{ + // As spread is a curve + // setting level resets the curve to 1 key + spread = clamp(spread,0.0f, 360.0f); + ReplaceOrAddSingleCurveValue(spread / 360.0f, m_AudioParameters.spreadCustomCurve); +} + +// Sets the priority of the [[AudioSource]] +// Unity is virtualizing AudioSources, when there's more AudioSources playing than available hardware channels. +// The AudioSources with lowest priority (and audibility) is virtualized first. +// Priority is an integer between 0 and 256. 0=highest priority, 256=lowest priority +int AudioSource::GetPriority() const +{ + return m_AudioParameters.priority; +} + +void AudioSource::SetPriority(int priority) +{ + m_AudioParameters.priority = clamp(priority, 0, 255); + SetFMODParam(setPriority, m_AudioParameters.priority); +} + +// Un- / Mutes the AudioSource. Mute sets the volume=0, Un-Mute restore the original volume. +bool AudioSource::GetMute() const +{ + return m_AudioParameters.mute; +} +void AudioSource::SetMute(bool mute) +{ + m_AudioParameters.mute = mute; +#if UNITY_WII + // If we're starving for data, auto mute channels + mute |= m_AudioParameters.starving; +#endif + if (m_Channel) + m_Channel->setMute(mute); + + for (TOneShots::iterator it = m_OneShots.begin(); it != m_OneShots.end();++it) + (*it)->channel->setMute(mute); +} + +// Within the Min distance the AudioSource will cease to grow louder in volume. +// Outside the min distance the volume starts to attenuate. +float AudioSource::GetMinDistance() const +{ + return m_AudioParameters.minDistance; +} +void AudioSource::SetMinDistance(float minDistance) +{ + m_AudioParameters.minDistance = max(minDistance, 0.0F); + SetFMODParam2(set3DMinMaxDistance, m_AudioParameters.minDistance, m_AudioParameters.maxDistance); +} + +// (Logarithmic rolloff) MaxDistance is the distance a sound stops attenuating at. +// (Linear rolloff) MaxDistance is the distance where the sound is completely inaudible. +float AudioSource::GetMaxDistance() const +{ + return m_AudioParameters.maxDistance; +} +void AudioSource::SetMaxDistance(float maxDistance) +{ + m_AudioParameters.maxDistance = max(maxDistance, m_AudioParameters.minDistance); + SetFMODParam2(set3DMinMaxDistance, m_AudioParameters.minDistance, m_AudioParameters.maxDistance); +} + +#if ENABLE_AUDIO_FMOD +// Inside cone angle, in degrees. This is the angle within which the sound is at its normal volume. +// Must not be greater than outsideconeangle. Default = 360. +float AudioSource::GetInsideConeAngle() const +{ + return m_AudioParameters.insideConeAngle; +} +void AudioSource::SetInsideConeAngle(float angle) +{ + m_AudioParameters.insideConeAngle = angle; + SetFMODParam3(set3DConeSettings, m_AudioParameters.insideConeAngle, m_AudioParameters.outsideConeAngle, m_AudioParameters.outsideConeVolume); +} + +/// Outside cone angle, in degrees. This is the angle outside of which the sound is at its outside volume. +/// Must not be less than insideconeangle. Default = 360. +float AudioSource::GetOutsideConeAngle() const +{ + return m_AudioParameters.outsideConeAngle; +} +void AudioSource::SetOutsideConeAngle(float angle) +{ + m_AudioParameters.outsideConeAngle = angle; + + SetFMODParam3(set3DConeSettings, m_AudioParameters.insideConeAngle, m_AudioParameters.outsideConeAngle, m_AudioParameters.outsideConeVolume); +} + +/// Cone outside volume, from 0 to 1.0. Default = 1.0. +float AudioSource::GetOutsideConeVolume() const +{ + return m_AudioParameters.outsideConeVolume; +} +void AudioSource::SetOutsideConeVolume(float volume) +{ + m_AudioParameters.outsideConeVolume = clamp01(volume); + + SetFMODParam3(set3DConeSettings, m_AudioParameters.insideConeAngle, m_AudioParameters.outsideConeAngle, m_AudioParameters.outsideConeVolume); +} +#endif +/// Set/Get rolloff mode +RolloffMode AudioSource::GetRolloffMode() const +{ + return m_AudioParameters.rolloffMode; +} + +void AudioSource::SetRolloffMode(RolloffMode mode) +{ + m_AudioParameters.rolloffMode = mode; +} + +/// Set/Get Custom rolloff curve +AnimationCurve& AudioSource::GetCustomRolloffCurve() +{ + return m_AudioParameters.rolloffCustomCurve; +} + +const AnimationCurve& AudioSource::GetCustomRolloffCurve() const +{ + return m_AudioParameters.rolloffCustomCurve; +} + +void AudioSource::SetCustomRolloffCurve(const AnimationCurve& curve) +{ + m_AudioParameters.rolloffCustomCurve = curve; +} + +/// Set/Get Pan Level curve +AnimationCurve& AudioSource::GetCustomPanLevelCurve() +{ + return m_AudioParameters.panLevelCustomCurve; +} + +const AnimationCurve& AudioSource::GetCustomPanLevelCurve() const +{ + return m_AudioParameters.panLevelCustomCurve; +} + +void AudioSource::SetCustomPanLevelCurve(const AnimationCurve& curve) +{ + m_AudioParameters.panLevelCustomCurve = curve; +} + +/// Set/Get Pan Level curve +AnimationCurve& AudioSource::GetCustomSpreadCurve() +{ + return m_AudioParameters.spreadCustomCurve; +} + +const AnimationCurve& AudioSource::GetCustomSpreadCurve() const +{ + return m_AudioParameters.spreadCustomCurve; +} + +void AudioSource::SetCustomSpreadCurve(const AnimationCurve& curve) +{ + m_AudioParameters.spreadCustomCurve = curve; +} + +/// Sets a channels pan position linearly. Only works for 2D clips. +/// -1.0 to 1.0. -1.0 is full left. 0.0 is center. 1.0 is full right. +/// Only sounds that are mono or stereo can be panned. Multichannel sounds (ie >2 channels) cannot be panned. +float AudioSource::GetPan() const +{ + return m_AudioParameters.pan; +} +void AudioSource::SetPan(float pan) +{ + m_AudioParameters.pan = clamp(pan, -1.0f, 1.0f); + SetFMODParam(setPan, m_AudioParameters.pan); +} + +/// Bypass/ignore any applied effects on AudioSource +bool AudioSource::GetBypassEffects() const +{ + return m_AudioParameters.bypassEffects; +} + +void AudioSource::SetBypassEffects(bool bypassEffect) +{ + m_AudioParameters.bypassEffects = bypassEffect; + SetupGroups(); +} + +/// Bypass/ignore any applied effects on AudioSource +bool AudioSource::GetBypassListenerEffects() const +{ + return m_AudioParameters.bypassListenerEffects; +} + +void AudioSource::SetBypassListenerEffects(bool bypassListenerEffect) +{ + m_AudioParameters.bypassListenerEffects = bypassListenerEffect; + SetupGroups(); +} + +bool AudioSource::GetBypassReverbZones() const +{ + return m_AudioParameters.bypassReverbZones; +} + +/// Bypass effect of reverb zones on this AudioSource +void AudioSource::SetBypassReverbZones(bool bypassReverbZones) +{ + m_AudioParameters.bypassReverbZones = bypassReverbZones; + SetupGroups(); +} + +float AudioSource::GetSecPosition() const +{ + + float time = 0.0f; + if (m_Channel) + { + unsigned position = 0; + + m_Channel->getPosition(&position, FMOD_TIMEUNIT_MS); + time = (float)position / 1000.f; + } + else + time = m_AudioClip.IsNull()?0.0f:(float)m_samplePosition / (float)m_AudioClip->GetFrequency(); + + #if SUPPORT_REPRODUCE_LOG + if (GetReproduceOutStream()) + { + *GetReproduceOutStream() << "Audio_Position "; + WriteFloat(*GetReproduceOutStream(), time); + *GetReproduceOutStream() << std::endl; + } + else if (GetReproduceInStream() && GetReproduceVersion() >= 4) + { + if (!CheckReproduceTag("Audio_Position", *GetReproduceInStream())) + { + ErrorString("Grabbing Audio.IsPlaying but there are no realtime calls recorded"); + return time; + } + ReadFloat(*GetReproduceInStream(), time); + } + #endif + return time; + +} + + + +void AudioSource::SetSecPosition(float secPosition) +{ + if (m_Channel) + m_Channel->setPosition((unsigned int)(secPosition * 1000.f), FMOD_TIMEUNIT_MS); + m_samplePosition = (unsigned int)(m_AudioClip.IsNull() ? 0.0f : (float)(secPosition * m_AudioClip->GetFrequency())); +} + + + +UInt32 AudioSource::GetSamplePosition() const +{ + unsigned position = m_samplePosition; + if (m_Channel) + m_Channel->getPosition(&position, FMOD_TIMEUNIT_PCM); + return position; + +} + + + +void AudioSource::SetSamplePosition(UInt32 position) +{ + if (m_Channel) + m_Channel->setPosition(position, FMOD_TIMEUNIT_PCM); + m_samplePosition = position; +} + + +void AudioSource::SetScheduledStartTime(double time) +{ +#if ENABLE_AUDIO_FMOD + if (m_Channel) + { + m_HasScheduledStartDelay = true; + int sampleRate; + GetAudioManager().GetFMODSystem()->getSoftwareFormat(&sampleRate, NULL, NULL, NULL, NULL, NULL); + UInt64 sample = (UInt64)(time * sampleRate) + GetAudioManager().GetAccumulatedPauseTicks(); + unsigned hiclock = sample >> 32; + unsigned loclock = sample & 0xFFFFFFFF; + m_Channel->setDelay(FMOD_DELAYTYPE_DSPCLOCK_START, hiclock, loclock); + } +#endif +} + + +void AudioSource::SetScheduledEndTime(double time) +{ +#if ENABLE_AUDIO_FMOD + if (m_Channel) + { + m_HasScheduledEndDelay = true; + int sampleRate; + GetAudioManager().GetFMODSystem()->getSoftwareFormat(&sampleRate, NULL, NULL, NULL, NULL, NULL); + UInt64 sample = (UInt64)(time * sampleRate) + GetAudioManager().GetAccumulatedPauseTicks(); + unsigned hiclock = sample >> 32; + unsigned loclock = sample & 0xFFFFFFFF; + m_Channel->setDelay(FMOD_DELAYTYPE_DSPCLOCK_END, hiclock, loclock); + } +#endif +} + + +#if ENABLE_AUDIO_FMOD + +void AudioSource::GetOutputData(float* samples, int numSamples, int channelOffset) +{ + if (m_Channel) + { + m_Channel->getWaveData(samples, numSamples, channelOffset); + } +} + +/// Gets the current spectrum data +void AudioSource::GetSpectrumData(float* samples, int numSamples, int channelOffset, FMOD_DSP_FFT_WINDOW windowType) +{ + if (m_Channel) + { + m_Channel->getSpectrum(samples, numSamples, channelOffset, windowType); + } +} + +// Apply filters +void AudioSource::ApplyFilters() +{ + if (!m_wetGroup) + return; + + TFilters filters; + GetFilterComponents(filters, true); + + for (TFilters::const_iterator it=filters.begin();it!=filters.end();it++) + { + FMOD::DSP* dsp = *it; + + if (dsp == m_PlayingDSP) + continue; + + FMOD_RESULT result; + result = dsp->remove(); + FMOD_ASSERT(result); + result = m_wetGroup->addDSP(dsp, 0); + FMOD_ASSERT(result); + } +} + +#endif //ENABLE_AUDIO_FMOD + +void AudioSource::PlayOneShot( AudioClip& clip, float volumeScale ) +{ + if(GetAudioManager().IsAudioDisabled()) + return; + + if (! (GetEnabled() && IsActive()) ) + { + WarningStringObject("Can not play a disabled audio source", this); + if (IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_0_a1)) + return; + } + + SetupGroups(); + + OneShot* oneshot = new OneShot(); + oneshot->channel = clip.CreateChannel(this); + Assert(oneshot->channel); +#if ENABLE_AUDIO_FMOD + ApplyFilters(); +#endif + + uintptr_t ptr = reinterpret_cast<uintptr_t>(oneshot) | 1; + oneshot->channel->setUserData(reinterpret_cast<void*>(ptr)); + oneshot->clip = const_cast<AudioClip*>(&clip); + oneshot->volumeScale = volumeScale; + oneshot->audioSource = this; + + if (oneshot->channel) + { +#if ENABLE_AUDIO_FMOD + if(m_dryGroup != NULL) + oneshot->channel->setChannelGroup(m_dryGroup); + + Vector3f pos = GetComponent(Transform).GetPosition() ; + FMOD_VECTOR fpos = UNITYVEC2FMODVEC(pos); + oneshot->channel->set3DAttributes( + &fpos, + 0 ); +#endif + + // start! + m_OneShots.push_back(oneshot); + AssignProps(); // Apply all parameters + bool paused = IS_CONTENT_NEWER_OR_SAME(kUnityVersion4_1_a3) && GetAudioManager().GetPause() && !m_AudioParameters.ignoreListenerPause; + oneshot->channel->setPaused(paused); + GetAudioManager ().AddAudioSource (this, paused); // to make sure source is put into active or paused sources lists + } + else + { + delete oneshot; + } +} + +void AudioSource::SetIgnoreListenerVolume(bool ignore) +{ + if (m_IgnoreListenerVolume == ignore) + return; + + m_IgnoreListenerVolume = ignore; + SetupGroups(); +} + +float inline CalcOALGain(float rolloffFactor, float volume, float minVolume, float maxVolume, float distance) +{ + float gain = 1.0f; + if (1.0f + rolloffFactor * (distance - 1.0f) > 0.0f) + gain = 1.0f / ( 1.0f + rolloffFactor * (distance - 1.0f)); + + gain *= volume; + gain = std::min(gain, maxVolume); + gain = std::max(gain, minVolume); + return gain; +} + +AudioSource* AudioSource::GetAudioSourceFromChannel(AudioChannel* channel) +{ + Assert(channel); + + void* userData = NULL; + AudioSource* audioSource = NULL; + channel->getUserData(&userData); + + if (!userData) return NULL; + + uintptr_t ptr = reinterpret_cast<uintptr_t> (userData); + + // is it a OneShot? + if (ptr & 1) + { + ptr = ptr & (~1); + OneShot* oneshot = reinterpret_cast<OneShot*>(ptr); + Assert (oneshot->audioSource); + audioSource = oneshot->audioSource; + } + else // AudioSource + audioSource = (AudioSource*)userData; + + return audioSource; +} + + + +/** + * Create a custom rolloff curve from old <3.0 parameters + **/ + +void AudioSource::CreateOpenALRolloff(float rolloffFactor, float minVolume, float maxVolume) +{ + +#if ENABLE_AUDIO_FMOD + AnimationCurve& curve = m_AudioParameters.rolloffCustomCurve; + curve.RemoveKeys(curve.begin(), curve.end()); + + // insert first key + AnimationCurve::Keyframe key; + key.time = 0.0f; + key.value = CalcOALGain(rolloffFactor, m_AudioParameters.volume, minVolume, maxVolume, 0.0f); + curve.AddKey (key); + + for (float distance=0.1f;distance<m_AudioParameters.maxDistance;distance*=2) + { + AnimationCurve::Keyframe key; + key.time = distance; + key.value = CalcOALGain(rolloffFactor, m_AudioParameters.volume, minVolume, maxVolume, distance); + // add some sensible in/out slopes + float s = distance/10.0f; + key.inSlope = (key.value - CalcOALGain(rolloffFactor, m_AudioParameters.volume, minVolume, maxVolume, distance-s)) / s; + key.outSlope = (CalcOALGain(rolloffFactor, m_AudioParameters.volume, minVolume, maxVolume,distance+s) - key.value) / s; + curve.AddKey (key); + } + + key.time = m_AudioParameters.maxDistance; + key.value = CalcOALGain(rolloffFactor, m_AudioParameters.volume, minVolume, maxVolume, m_AudioParameters.maxDistance); + curve.AddKey (key); +#endif +} + +#if ENABLE_AUDIO_FMOD + +bool AudioSource::GetFilterComponents(TFilters& filters, bool create) const +{ + const GameObject* go = GetGameObjectPtr(); + + if (!go) + return false; + + for (int i=0;i<go->GetComponentCount();i++) + { + AudioFilter* filter = NULL; + FMOD::DSP* dsp = NULL; + filter = dynamic_pptr_cast<AudioFilter*> (&go->GetComponentAtIndex(i)); + // built-in filter are only available in PRO + if ( filter && GetBuildSettings().hasAdvancedVersion ) + dsp = filter->GetDSP(); + + #if ENABLE_SCRIPTING + if (!dsp) + { + MonoBehaviour* behaviour = dynamic_pptr_cast<MonoBehaviour*> (&go->GetComponentAtIndex(i)); + if ( behaviour ) + { + if (create) + dsp = behaviour->GetOrCreateDSP(); + else + dsp = behaviour->GetDSP(); + } + } + #endif + + if (dsp) + filters.push_back( dsp ); + } + + return !filters.empty(); +} + +#endif + +template<class TransferFunc> +void AudioSource::Transfer (TransferFunc& transfer) { + Super::Transfer (transfer); + + // 3.5 3: Normalized curve values + // 3.0 2: FMOD custom rolloff data + // <3.0 1: OpenAL rolloff data + transfer.SetVersion (3); + + if (transfer.IsOldVersion(2) || transfer.IsCurrentVersion ()) + { + transfer.Transfer (m_AudioClip, "m_audioClip"); + TRANSFER_SIMPLE (m_PlayOnAwake); + transfer.Align(); + + transfer.Transfer (m_AudioParameters.volume, "m_Volume"); + transfer.Transfer (m_AudioParameters.pitch, "m_Pitch"); + + transfer.Transfer (m_AudioParameters.loop, "Loop"); // repeat sound + transfer.Transfer(m_AudioParameters.mute, "Mute"); + transfer.Align(); + + transfer.Transfer(m_AudioParameters.priority, "Priority"); + transfer.Transfer(m_AudioParameters.dopplerLevel, "DopplerLevel"); + + transfer.Transfer(m_AudioParameters.minDistance, "MinDistance"); + transfer.Transfer(m_AudioParameters.maxDistance, "MaxDistance"); + + transfer.Transfer(m_AudioParameters.pan, "Pan2D"); + + SInt32 mode = (SInt32)m_AudioParameters.rolloffMode; + transfer.Transfer(mode, "rolloffMode"); + m_AudioParameters.rolloffMode = (RolloffMode)mode; + + transfer.Transfer(m_AudioParameters.bypassEffects, "BypassEffects"); + transfer.Transfer(m_AudioParameters.bypassListenerEffects, "BypassListenerEffects"); + transfer.Transfer(m_AudioParameters.bypassReverbZones, "BypassReverbZones"); + + transfer.Align(); + + // @TODO strip these from the player build if they're empty? + transfer.Transfer(m_AudioParameters.rolloffCustomCurve,"rolloffCustomCurve"); + transfer.Transfer(m_AudioParameters.panLevelCustomCurve, "panLevelCustomCurve"); + transfer.Transfer(m_AudioParameters.spreadCustomCurve, "spreadCustomCurve"); + + if (transfer.IsOldVersion(2)) + { + // Normalize curves so time goes from 0 to 1 + ScaleCurveTime (m_AudioParameters.rolloffCustomCurve, 1.0f / m_AudioParameters.maxDistance); + ScaleCurveTime (m_AudioParameters.panLevelCustomCurve, 1.0f / m_AudioParameters.maxDistance); + ScaleCurveTime (m_AudioParameters.spreadCustomCurve, 1.0f / m_AudioParameters.maxDistance); + } + } + else + { + transfer.Transfer (m_AudioClip, "m_audioClip"); + TRANSFER_SIMPLE (m_PlayOnAwake); + transfer.Align(); + + transfer.Transfer (m_AudioParameters.volume, "m_Volume"); + transfer.Transfer (m_AudioParameters.pitch, "m_Pitch"); + + float minVolume, maxVolume; + transfer.Transfer (minVolume,"m_MinVolume"); + transfer.Transfer (maxVolume,"m_MaxVolume"); + float rolloffFactor; + transfer.Transfer (rolloffFactor, "m_RolloffFactor"); + + transfer.Transfer (m_AudioParameters.loop, "Loop"); // repeat sound +#if ENABLE_AUDIO_FMOD + CreateOpenALRolloff(rolloffFactor, minVolume, maxVolume); +#endif + m_AudioParameters.rolloffMode = kRolloffCustom; + } +} + + +void AudioSource::OnAddComponent() +{ +#if ENABLE_AUDIO_FMOD + ApplyFilters(); +#endif +} + + +void AudioSource::InitializeClass () +{ + REGISTER_MESSAGE_VOID (AudioSource, kDidAddComponent, OnAddComponent); +} + +void AudioSource::CleanupClass() +{ +} + +IMPLEMENT_CLASS_HAS_INIT (AudioSource) +IMPLEMENT_OBJECT_SERIALIZE (AudioSource) + +#endif //ENABLE_AUDIO diff --git a/Runtime/Audio/AudioSource.h b/Runtime/Audio/AudioSource.h new file mode 100644 index 0000000..cd5d23d --- /dev/null +++ b/Runtime/Audio/AudioSource.h @@ -0,0 +1,351 @@ +#ifndef AUDIOSOURCE_H +#define AUDIOSOURCE_H + +#if ENABLE_AUDIO +#include "Runtime/GameCode/Behaviour.h" +#include "AudioParameters.h" +#include "AudioSourceFilter.h" +#include "Runtime/Math/Vector3.h" +#include <vector> +#include "AudioClip.h" +#include "AudioLowPassFilter.h" +#include "AudioBehaviour.h" +#if UNITY_FLASH +#include "PlatformDependent/FlashSupport/cpp/AudioChannel.h" +#endif + + +class AudioSource : public AudioBehaviour +{ +public: + + REGISTER_DERIVED_CLASS (AudioSource, AudioBehaviour) + DECLARE_OBJECT_SERIALIZE (AudioSource) + + /** + * Construction/Destruction + **/ + AudioSource (MemLabelId label, ObjectCreationMode mode); + // virtual ~AudioSource (); declared-by-macro + + + /** + * Transport + **/ + /// Plays a sound one time, then forgets about it (You cannot stop a one shot sound) + void PlayOneShot (AudioClip& clip, float volumeMultiplier = 1.0F); + /// Pause one shot sounds + void PauseOneShots (); + /// Resumes one shot sounds + void ResumeOneShots (); + /// Plays the active audioclip at (future) scheduled time. If time < 0 it specifies a delay + void Play(double time = 0.0); + /// Pauses the active audioclip + void Pause (); + /// Stops the active audio clip + void Stop (bool stopOneShots); + /// Stops a specific channel + void Stop(AudioChannel* channel); + /// Is the audio source currently playing? (Only looks at the main audio source, OneShots are ignored) + bool IsPlaying () const; + /// Is the audio source currently paused? (Only looks at the main audio source, OneShots are ignored) + bool IsPaused () const; + + bool IsPlayingScripting (); + + // positions + // seconds + float GetSecPosition() const; + void SetSecPosition(float secPosition); + + UInt32 GetSamplePosition() const; + void SetSamplePosition(UInt32 position); + void SetScheduledStartTime(double time); + void SetScheduledEndTime(double time); + void CorrectScheduledTimeAfterUnpause(UInt64 delay); + + // Get Length + float GetLength() const; + + /// Get/Set PlayOnAwake + bool GetPlayOnAwake() const { return m_PlayOnAwake; } + void SetPlayOnAwake(bool playOnAwake); + bool GetIgnoreListenerPause() const { return m_AudioParameters.ignoreListenerPause; } + void SetIgnoreListenerPause(bool ignoreListenerPause); + bool HasScheduledStartDelay() const { return m_HasScheduledStartDelay; } + bool HasScheduledEndDelay() const { return m_HasScheduledEndDelay; } + bool HasScheduledTime() const { return m_HasScheduledStartDelay | m_HasScheduledEndDelay; } + + /** + * Behaviour implementation + **/ + virtual void Deactivate (DeactivateOperation operation); + virtual void AwakeFromLoad (AwakeFromLoadMode awakeMode); + virtual void CheckConsistency (); + virtual void Update (); + virtual void FixedUpdate (); + + static void InitializeClass (); + static void CleanupClass(); + + /** + * + **/ + void AddToManager(); + void RemoveFromManager(); + + void OnAddComponent(); + + + virtual void Reset(); + + void Cleanup(); + +public: + // GET/SETTERS + bool GetLoop () const; + void SetLoop (bool loop); + + /// Get/Set pitch of the sound + float GetPitch() const; + void SetPitch(float pitch); + + // Get/Set volume of the sound + float GetVolume() const; + void SetVolume(float volume); + + // Sets how much the 3d engine has an effect on the channel. + float GetPanLevel() const; + void SetPanLevel(float level); + + // Sets the doppler scale for this AudioSource + float GetDopplerLevel() const; + void SetDopplerLevel(float level); + + // Sets the spread angle of a 3d stereo or multichannel sound in speaker space. + // 0 = all sound channels are located at the same speaker location and is 'mono'. + // 360 = all subchannels are located at the opposite speaker location to the speaker location that it should be according to 3D position. Default = 0. + float GetSpread() const; + void SetSpread(float spread); + + // Sets the priority of the [[AudioSource]] + // Unity is virtualizing AudioSources, when there's more AudioSources playing than available hardware channels. + // The AudioSources with lowest priority (and audibility) is virtualized first. + // Priority is an integer between 0 and 256. 0=highest priority, 256=lowest priority + int GetPriority() const; + void SetPriority(int priority); + + // Un- / Mutes the AudioSource. Mute sets the volume=0, Un-Mute restore the original volume. + bool GetMute() const; + void SetMute(bool mute); + + // Within the Min distance the AudioSource will cease to grow louder in volume. + // Outside the min distance the volume starts to attenuate. + float GetMinDistance() const; + void SetMinDistance(float minDistance); + + // (Logarithmic rolloff) MaxDistance is the distance a sound stops attenuating at. + // (Linear rolloff) MaxDistance is the distance where the sound is completely inaudible. + float GetMaxDistance() const; + void SetMaxDistance(float maxDistance); + + // Inside cone angle, in degrees. This is the angle within which the sound is at its normal volume. + // Must not be greater than outsideconeangle. Default = 360. + float GetInsideConeAngle() const; + void SetInsideConeAngle(float angle); + + /// Outside cone angle, in degrees. This is the angle outside of which the sound is at its outside volume. + /// Must not be less than insideconeangle. Default = 360. + float GetOutsideConeAngle() const; + void SetOutsideConeAngle(float angle); + + /// Cone outside volume, from 0 to 1.0. Default = 1.0. + float GetOutsideConeVolume() const; + void SetOutsideConeVolume(float volume); + + /// Set/Get rolloff mode + RolloffMode GetRolloffMode() const; + void SetRolloffMode(RolloffMode mode); + + /// Set/Get Custom rolloff curve + AnimationCurve& GetCustomRolloffCurve(); + const AnimationCurve& GetCustomRolloffCurve() const; + void SetCustomRolloffCurve(const AnimationCurve&); + + /// Set/Get PanLevel distance curve + AnimationCurve& GetCustomPanLevelCurve(); + const AnimationCurve& GetCustomPanLevelCurve() const; + void SetCustomPanLevelCurve(const AnimationCurve&); + + /// Set/Get spread distance curve + AnimationCurve& GetCustomSpreadCurve(); + const AnimationCurve& GetCustomSpreadCurve() const; + void SetCustomSpreadCurve(const AnimationCurve&); + + /// Sets a audiosource pan position linearly. Only works for 2D clips. + /// -1.0 to 1.0. -1.0 is full left. 0.0 is center. 1.0 is full right. + /// Only sounds that are mono or stereo can be panned. Multichannel sounds (ie >2 channels) cannot be panned. + float GetPan() const; + void SetPan(float pan); + + /// Bypass/ignore any applied effects on AudioSource + bool GetBypassEffects() const; + void SetBypassEffects(bool bypassEffect); + + /// Bypass/ignore any applied effects on listener + bool GetBypassListenerEffects() const; + void SetBypassListenerEffects(bool bypassListenerEffects); + + /// Bypass effect of reverb zones on this AudioSource + bool GetBypassReverbZones() const; + void SetBypassReverbZones(bool bypassReverbZones); + +#if ENABLE_AUDIO_FMOD + /// Gets the current output pcm data + void GetOutputData(float* samples, int numSamples, int channelOffset); + /// Gets the current spectrum data + void GetSpectrumData(float* samples, int numSamples, int channelOffset, FMOD_DSP_FFT_WINDOW windowType); +#endif + /// Sets the currently active audio clip + void SetAudioClip(AudioClip *clip); + AudioClip *GetAudioClip () const {return m_AudioClip; } + + int GetVelocityUpdateMode() const { return m_VelocityUpdateMode; } + void SetVelocityUpdateMode(int update) { m_VelocityUpdateMode=update; } + + bool GetIgnoreListenerVolume() const { return m_IgnoreListenerVolume; } + void SetIgnoreListenerVolume(bool ignore); + +private: +#if DOXYGEN + int Priority; ///< Sets the priority of the source. A sound with a lower priority will more likely be stolen by high priorities sounds. + float DopplerLevel; ///< Sets the specific doppler scale for the source. + float MinDistance; ///< Within the minDistance, the volume will stay at the loudest possible. Outside of this mindistance it begins to attenuate. + float MaxDistance; ///< MaxDistance is the distance a sound stops attenuating at. + float Pan2D; ///< Sets a source's pan position linearly. Only applicable on 2D sounds. + + float m_Pitch; ///< Sets the frequency of the sound. Use this to slow down or speed up the sound. + float m_Volume; ///< Sets the volume of the sound. + + // rolloff + RolloffMode rolloffMode; ///< enum { Logarithmic Rolloff=0, Linear Rolloff, Custom Rolloff } + + bool Loop; ///< Set the source to loop. If loop points are defined in the clip, these will be respected. + bool Mute; ///< Mutes the sound. + + bool BypassEffects; ///< Bypass/ignore any applied effects on AudioSource + bool BypassListenerEffects; ///< Bypass/ignore any applied effects from listener + bool BypassReverbZones; ///< Bypass/ignore any reverb zones + bool IgnoreListenerPause; ///< Allow source to play even though AudioListener is paused (for GUI sounds) + +#else + AudioParameters m_AudioParameters; +#endif + + +private: + /** + * OneShots + **/ + struct OneShot + { + AudioChannel* channel; + AudioClip* clip; + float volumeScale; + AudioSource* audioSource; + }; + + typedef std::vector<OneShot*> TOneShots; + + TOneShots m_OneShots; + + /** + * Update channel properties + * @param channel The channel to update + * @param oneshot Is this a oneshot? + * @return True if channel was update. False if the channel is invalid + **/ + inline bool UpdateParameters(AudioChannel* channel, OneShot* oneshot = NULL); + + /** + * Create a custom rolloff curve from old <3.0 parameters + **/ + void CreateOpenALRolloff(float rolloffFactor, float minVolume, float maxVolume); + + /** + * Setup effect and non-effect groups + **/ + void SetupGroups(); + void TearDownGroups(); + void SetChannelGroup(AudioChannel* channel); + +#if ENABLE_AUDIO_FMOD + /** + * Apply filters + **/ + void ApplyFilters(); +#endif + +private: + PPtr<AudioClip> m_AudioClip; + ListNode<AudioSource> m_Node; + AudioChannel* m_Channel; + + + AudioManager::AudioScheduledSource m_ScheduledSource; + +#if ENABLE_AUDIO_FMOD + // channel group, filter/non-filter group and for oneshot + FMOD::ChannelGroup* m_dryGroup; // No Effect unit + FMOD::ChannelGroup* m_wetGroup; // Effect unit +#endif + /** + * backward compatibility props + **/ + bool m_IgnoreListenerVolume; + + bool m_PlayOnAwake; ///<Play the sound when the scene loads. + bool m_HasScheduledStartDelay; + bool m_HasScheduledEndDelay; + int m_VelocityUpdateMode; + Vector3f m_LastUpdatePosition; + + // cached position + unsigned m_samplePosition; + // cache pause + bool m_pause; + + void DoUpdate(); + void UpdateQueue(); + void SetupQueue(); + void AssignProps(); + float CalculateVolumeModifierForDistance(float distance); + +#if ENABLE_AUDIO_FMOD + FMOD::DSP* m_PlayingDSP; + typedef std::vector<FMOD::DSP*> TFilters; + bool GetFilterComponents(TFilters &filters, bool create) const; +#endif + + friend class AudioManager; + +#if ENABLE_PROFILER + static int s_AudioSourceCount; +#endif + +#if ENABLE_AUDIO_FMOD +private: // callbacks + //static FMOD_RESULT F_CALLBACK channelCallback( + // FMOD_CHANNEL *channel, FMOD_CHANNEL_CALLBACKTYPE type, void *commanddata1, void *commanddata2); + static float F_CALLBACK rolloffCallback( + FMOD_CHANNEL * channel, + float distance + ); +#endif //ENABLE_AUDIO_FMOD + +public: // static helper functions + static AudioSource* GetAudioSourceFromChannel(AudioChannel* channel); +}; + +#endif //ENABLE_AUDIO +#endif diff --git a/Runtime/Audio/AudioSourceFilter.cpp b/Runtime/Audio/AudioSourceFilter.cpp new file mode 100644 index 0000000..21a072f --- /dev/null +++ b/Runtime/Audio/AudioSourceFilter.cpp @@ -0,0 +1,68 @@ +#include "UnityPrefix.h" +#if ENABLE_AUDIO_FMOD +#include "AudioSourceFilter.h" + +IMPLEMENT_CLASS(AudioFilter) +IMPLEMENT_OBJECT_SERIALIZE (AudioFilter) +INSTANTIATE_TEMPLATE_TRANSFER (AudioFilter) + +AudioFilter::~AudioFilter () +{ + Cleanup(); +} + +void AudioFilter::Cleanup() +{ + if (m_DSP) + { + m_DSP->release(); + m_DSP = NULL; + } +} + +void AudioFilter::AddToManager() +{ + if (!m_DSP) + Init(); + Update(); + Assert(m_DSP); + Assert(m_Type != FMOD_DSP_TYPE_UNKNOWN); + m_DSP->setBypass(false); +} + +void AudioFilter::RemoveFromManager() +{ + if (m_DSP) + m_DSP->setBypass(true); +} + +void AudioFilter::AwakeFromLoad(AwakeFromLoadMode mode) +{ + Super::AwakeFromLoad(mode); + Update(); +} + +void AudioFilter::Init() +{ + if (m_DSP == NULL && m_Type != FMOD_DSP_TYPE_FORCEINT) + { + FMOD_RESULT result = GetAudioManager().GetFMODSystem()->createDSPByType(m_Type, &m_DSP); + Assert (result == FMOD_OK); + result = m_DSP->setBypass(!GetEnabled()); + Assert (result == FMOD_OK); + } +} + +FMOD::DSP* AudioFilter::GetDSP() +{ + if (!m_DSP) + Init(); + return m_DSP; +} + +template<class TransferFunction> +void AudioFilter::Transfer (TransferFunction& transfer) +{ + Super::Transfer (transfer); +} +#endif //ENABLE_AUDIO
\ No newline at end of file diff --git a/Runtime/Audio/AudioSourceFilter.h b/Runtime/Audio/AudioSourceFilter.h new file mode 100644 index 0000000..72795b3 --- /dev/null +++ b/Runtime/Audio/AudioSourceFilter.h @@ -0,0 +1,36 @@ +#ifndef __AUDIOSOURCE_FILTER_H__ +#define __AUDIOSOURCE_FILTER_H__ + +#include "Runtime/Serialize/TransferFunctions/SerializeTransfer.h" +#include "AudioManager.h" +#include "Runtime/GameCode/Behaviour.h" +#include "Runtime/Audio/correct_fmod_includer.h" + +#if ENABLE_AUDIO_FMOD + +using namespace Unity; + +class AudioFilter : public Behaviour +{ +public: + REGISTER_DERIVED_ABSTRACT_CLASS (AudioFilter, Behaviour) + DECLARE_OBJECT_SERIALIZE (AudioFilter) + + AudioFilter(MemLabelId label, ObjectCreationMode mode) : Behaviour(label, mode), m_DSP(NULL), m_Type(FMOD_DSP_TYPE_UNKNOWN) {} + + FMOD::DSP* GetDSP(); + + virtual void RemoveFromManager(); + virtual void AddToManager(); + virtual void AwakeFromLoad(AwakeFromLoadMode mode); + + void Init(); + void Cleanup(); +protected: + FMOD_DSP_TYPE m_Type; + FMOD::DSP* m_DSP; + friend class AudioManager; +}; + +#endif //ENABLE_AUDIO +#endif // __AUDIOSOURCE_FILTER_H__ diff --git a/Runtime/Audio/AudioTypes.h b/Runtime/Audio/AudioTypes.h new file mode 100644 index 0000000..38bbc66 --- /dev/null +++ b/Runtime/Audio/AudioTypes.h @@ -0,0 +1,16 @@ +#pragma once + +#include "Runtime/Audio/correct_fmod_includer.h" // can't forward declare enums (@TODO use ints?) + +// macros/helpers +#if ENABLE_AUDIO_FMOD +#define UNITYVEC2FMODVEC(v) *(reinterpret_cast<FMOD_VECTOR*>(&v)) +#define UNITYVEC2FMODVECPTR(v) (reinterpret_cast<FMOD_VECTOR*>(&v)) + +#define FMOD_ASSERT(x) {Assert(x == FMOD_OK);\ +if (x != FMOD_OK){ ErrorString(FMOD_ErrorString(result));}} + +typedef FMOD::Channel AudioChannel; + + +#endif //ENABLE_AUDIO_FMOD diff --git a/Runtime/Audio/OggReader.h b/Runtime/Audio/OggReader.h new file mode 100644 index 0000000..fca185a --- /dev/null +++ b/Runtime/Audio/OggReader.h @@ -0,0 +1,51 @@ +#ifndef __OGG_READER_H__ +#define __OGG_READER_H__ + + + inline bool CheckOggVorbisFile (const UInt8* ogg_memory, size_t size, int* outChannels) + { + *outChannels = 0; + if (size < 40) return false; + /* + 0 1 2 3 + 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1| Byte + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | capture_pattern: Magic number for page start "OggS" | 0-3 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | version | header_type | granule_position | 4-7 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | 8-11 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | bitstream_serial_number | 12-15 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | page_sequence_number | 16-19 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | | CRC_checksum | 20-23 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | |page_segments | segment_table | 24-27 + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + | ... | 28- + +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + */ + + // capture pattern (OggS) + if (0x4f != ogg_memory[0]) return false; + if (0x67 != ogg_memory[1]) return false; + if (0x67 != ogg_memory[2]) return false; + if (0x53 != ogg_memory[3]) return false; + // version + if (0 != ogg_memory[4]) return false; + // ensure its the first page(2) (and not last(1) and not a continued page(4)) + const UInt8 page_flag = ogg_memory[5]; + if (!((2 & page_flag) && !(1 & page_flag) && !(4 & page_flag))) return false; + // vorbis packet id (must be #1) + if (1 != ogg_memory[28]) return false; + // vorbis header + if (memcmp((void*)&ogg_memory[29], "vorbis", 6) != 0) return false; + // vorbis version [35-38] + *outChannels = ogg_memory[39]; + + return true; +} + +#endif // __OGG_READER_H__ diff --git a/Runtime/Audio/ScriptBindings/AudioBindings.txt b/Runtime/Audio/ScriptBindings/AudioBindings.txt new file mode 100644 index 0000000..57ed241 --- /dev/null +++ b/Runtime/Audio/ScriptBindings/AudioBindings.txt @@ -0,0 +1,996 @@ +C++RAW + + +#include "UnityPrefix.h" +#include "Configuration/UnityConfigure.h" +#include "Runtime/Mono/MonoManager.h" +#include "Runtime/Graphics/Transform.h" +#include "Runtime/Utilities/PathNameUtility.h" +#include "Runtime/Profiler/ProfilerHistory.h" +#include "Runtime/Allocator/MemoryManager.h" +#include "Runtime/Audio/AudioClip.h" +#if ENABLE_AUDIO +#include "Runtime/Audio/AudioSource.h" +#include "Runtime/Audio/AudioListener.h" +#include "Runtime/Audio/AudioManager.h" +#include "Runtime/Audio/AudioReverbZone.h" +#include "Runtime/Audio/AudioReverbFilter.h" +#include "Runtime/Audio/AudioHighPassFilter.h" +#include "Runtime/Audio/AudioLowPassFilter.h" +#include "Runtime/Audio/AudioChorusFilter.h" +#include "Runtime/Audio/AudioDistortionFilter.h" +#include "Runtime/Audio/AudioEchoFilter.h" +#endif +#include "Runtime/Animation/Animation.h" +#include "Runtime/Utilities/PlayerPrefs.h" +#include "Runtime/Misc/BuildSettings.h" +#include "Runtime/Scripting/ScriptingUtility.h" +#include "Runtime/Scripting/ScriptingExportUtility.h" +#include "Runtime/Scripting/GetComponent.h" +#include "Runtime/Scripting/Backend/ScriptingBackendApi.h" +#include "Runtime/Scripting/Backend/ScriptingTypeRegistry.h" +#include "Runtime/Scripting/Scripting.h" + +using namespace Unity; + +/* + Mono defines a bool as either 1 or 2 bytes. + On windows a bool on the C++ side needs to be 2 bytes. + We use the typemap to map bool's to short's. + When using the C++ keyword and you want to export a bool value + to mono you have to use a short on the C++ side. +*/ + + +void PauseEditor (); +using namespace std; + +CSRAW +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Collections; +using System.Collections.Generic; +using UnityEngineInternal; + +namespace UnityEngine +{ + + + +// These are speaker types defined for use with [[AudioSettings.speakerMode]]. +CONDITIONAL ENABLE_AUDIO_FMOD +ENUM AudioSpeakerMode + // Channel count is unaffected. + Raw = 0, + // Channel count is set to 1. The speakers are monaural. + Mono = 1, + // Channel count is set to 2. The speakers are stereo. This is the editor default. + Stereo = 2, + // Channel count is set to 4. 4 speaker setup. This includes front left, front right, rear left, rear right. + Quad = 3, + // Channel count is set to 5. 5 speaker setup. This includes front left, front right, center, rear left, rear right. + Surround = 4, + // Channel count is set to 6. 5.1 speaker setup. This includes front left, front right, center, rear left, rear right and a subwoofer. + Mode5point1 = 5, + // Channel count is set to 8. 7.1 speaker setup. This includes front left, front right, center, rear left, rear right, side left, side right and a subwoofer. + Mode7point1 = 6, + // Channel count is set to 2. Stereo output, but data is encoded in a way that is picked up by a Prologic/Prologic2 decoder and split into a 5.1 speaker setup. + Prologic = 7 +END + +// Controls the global audio settings from script. +CONDITIONAL ENABLE_AUDIO_FMOD +CLASS AudioSettings + // Returns the speaker mode capability of the current audio driver. (RO) + CUSTOM_PROP static AudioSpeakerMode driverCaps + { + return GetAudioManager().GetSpeakerModeCaps(); + } + + // Sets or gets the current speaker mode. Default is 2 channel stereo. + CUSTOM_PROP static AudioSpeakerMode speakerMode + { + return GetAudioManager().GetSpeakerMode(); + } + { + GetAudioManager().SetSpeakerMode(value); + } + + // Returns the current time of the audio system. This is based on the number of samples the audio system processes and is therefore more exact than the time obtained via the Time.time property. + // It is constant while Unity is paused. + THREAD_SAFE CUSTOM_PROP static double dspTime + { + return GetAudioManager().GetDSPTime(); + } + + // Get and set the mixer's current output rate. + CUSTOM_PROP static int outputSampleRate + { + int sampleRate; + GetAudioManager().GetFMODSystem()->getSoftwareFormat( + &sampleRate, + NULL, + NULL, + NULL, + NULL, + NULL + ); + return sampleRate; + } + { + int currentSampleRate = AudioSettings_Get_Custom_PropOutputSampleRate(); + if (currentSampleRate != value) + { + GetAudioManager().CloseFMOD(); + FMOD_RESULT result = GetAudioManager().GetFMODSystem()->setSoftwareFormat( + value, + FMOD_SOUND_FORMAT_PCM16, + 0, + 8, + FMOD_DSP_RESAMPLER_LINEAR + ); + if (result != FMOD_OK) + { + ErrorString(Format("%dHz is an invalid output samplerate for this platform", value)); + } + + GetAudioManager().ReloadFMODSounds(); + } + } + + // Get or set the mixer's buffer size in samples. + CUSTOM static void SetDSPBufferSize(int bufferLength, int numBuffers) + { + bufferLength = clamp(bufferLength, 64, 4096); + + GetAudioManager().CloseFMOD(); + FMOD_RESULT result = GetAudioManager().GetFMODSystem()->setDSPBufferSize(bufferLength, numBuffers); + if (result != FMOD_OK) + { + ErrorString(Format("DSP ringbuffer of %d samples (x %d) is invalid for this platform", bufferLength, numBuffers)); + } + GetAudioManager().ReloadFMODSounds(); + } + + // Get or set the mixer's buffer size in samples. + CUSTOM static void GetDSPBufferSize(out int bufferLength, out int numBuffers) + { + FMOD_RESULT result = GetAudioManager().GetFMODSystem()->getDSPBufferSize((unsigned int*)bufferLength, numBuffers); + FMOD_ASSERT( result ); + } + + +END + +// Type of the imported(native) data +ENUM public AudioType + // 3rd party / unknown plugin format. + UNKNOWN = 0, + //acc - not supported + ACC = 1, /* [Unity] Not supported/used. But kept here to keep the order of the enum in sync. */ + //aiff + AIFF = 2, +// ASF = 3, /* Microsoft Advanced Systems Format (ie WMA/ASF/WMV). */ +// AT3 = 4, /* Sony ATRAC 3 format */ +// CDDA = 5, /* Digital CD audio. */ +// DLS = 6, /* Sound font / downloadable sound bank. */ +// FLAC = 7, /* FLAC lossless codec. */ +// FSB = 8, /* FMOD Sample Bank. */ + //game cube ADPCM + GCADPCM = 9, + //impulse tracker + IT = 10, +// MIDI = 11, /* MIDI. */ + //Protracker / Fasttracker MOD. + MOD = 12, + //MP2/MP3 MPEG. + MPEG = 13, + //ogg vorbis + OGGVORBIS = 14, +// PLAYLIST = 15, /* Information only from ASX/PLS/M3U/WAX playlists */ +// RAW = 16, /* Raw PCM data. */ + // ScreamTracker 3. + S3M = 17, +// SF2 = 18, /* Sound font 2 format. */ +// USER = 19, /* User created sound. */ + //Microsoft WAV. + WAV = 20, + // FastTracker 2 XM. + XM = 21, + // Xbox360 XMA + XMA = 22, +// VAG = 23, /* PlayStation 2 / PlayStation Portable adpcm VAG format. */ + //iPhone hardware decoder, supports AAC, ALAC and MP3. Extracodecdata is a pointer to an FMOD_AUDIOQUEUE_EXTRACODECDATA structure. + AUDIOQUEUE = 24, +// XWMA = 25, /* Xbox360 XWMA */ +// BCWAV = 26, /* 3DS BCWAV container format for DSP ADPCM and PCM */ +// AT9 = 27, /* NGP ATRAC 9 format */ +END + +// A container for audio data. +CONDITIONAL ENABLE_AUDIO +CLASS AudioClip : Object + + // Check if reading of the audioclip is allowed by crossdomain security and throw if not + C++RAW + static void CheckReadAllowedAndThrow(AudioClip *clip) + { +#if ENABLE_MONO && ENABLE_SECURITY && ENABLE_WWW + if ( clip&&!clip->GetReadAllowed() ) + Scripting::RaiseSecurityException("No read access to the audioclip data: %s", clip->GetName()); +#endif + } + + // The length of the audio clip in seconds (RO) + AUTO_PROP float length GetLengthSec + + // The length of the audio clip in samples (RO) + // Prints how many samples the attached audio source has + // + AUTO_PROP int samples GetSampleCount + + // Channels in audio clip (RO) + AUTO_PROP int channels GetChannelCount + + // Sample frequency (RO) + AUTO_PROP int frequency GetFrequency + + // Is a streamed audio clip ready to play? (RO) + AUTO_PROP bool isReadyToPlay ReadyToPlay + + CONDITIONAL ENABLE_AUDIO_FMOD + // Fills an array with sample data from the clip. The samples are floats ranging from -1.0f to 1.0f. The sample count is determined by the length of the float array. + CUSTOM void GetData(float[] data, int offsetSamples) + { + CheckReadAllowedAndThrow(self); + self->GetData(&Scripting::GetScriptingArrayElement<float>(data, 0), GetScriptingArraySize (data) / self->GetChannelCount(), offsetSamples); + } + + CONDITIONAL ENABLE_AUDIO_FMOD + // Set sample data in a clip. The samples should be floats ranging from 0.0f to 1.0f (exceeding these limits will lead to artifacts and undefined behaviour). + CUSTOM void SetData(float[] data, int offsetSamples) + { + self->SetData(&Scripting::GetScriptingArrayElement<float>(data, 0), GetScriptingArraySize (data) / self->GetChannelCount(), offsetSamples); + } + + CONDITIONAL ENABLE_AUDIO_FMOD + /// *listonly* + CSRAW public static AudioClip Create(string name, int lengthSamples, int channels, int frequency, bool _3D, bool stream) + { + AudioClip clip = Create (name, lengthSamples, channels, frequency, _3D, stream, null, null); + return clip; + } + + CONDITIONAL ENABLE_AUDIO_FMOD + /// *listonly* + CSRAW public static AudioClip Create(string name, int lengthSamples, int channels, int frequency, bool _3D, bool stream, PCMReaderCallback pcmreadercallback) + { + AudioClip clip = Create (name, lengthSamples, channels, frequency, _3D, stream, pcmreadercallback, null); + return clip; + } + + CONDITIONAL ENABLE_AUDIO_FMOD + // Creates a user AudioClip with a name and with the given length in samples, channels and frequency. + CSRAW public static AudioClip Create(string name, int lengthSamples, int channels, int frequency, bool _3D, bool stream, PCMReaderCallback pcmreadercallback, PCMSetPositionCallback pcmsetpositioncallback) + { + if(name == null) throw new NullReferenceException(); + + AudioClip clip = Construct_Internal(); + if ( pcmreadercallback != null) + clip.m_PCMReaderCallback += pcmreadercallback; + if ( pcmsetpositioncallback != null) + clip.m_PCMSetPositionCallback += pcmsetpositioncallback; + + clip.Init_Internal( name, lengthSamples, channels, frequency, _3D, stream ); + + return clip; + } + + /// *listonly* + CONDITIONAL ENABLE_AUDIO_FMOD + CSRAW public delegate void PCMReaderCallback(float[] data); + CONDITIONAL ENABLE_AUDIO_FMOD + CSRAW private event PCMReaderCallback m_PCMReaderCallback = null; + /// *listonly* + CONDITIONAL ENABLE_AUDIO_FMOD + CSRAW public delegate void PCMSetPositionCallback(int position); + CONDITIONAL ENABLE_AUDIO_FMOD + CSRAW private event PCMSetPositionCallback m_PCMSetPositionCallback = null; + + CONDITIONAL ENABLE_AUDIO_FMOD + CSRAW private void InvokePCMReaderCallback_Internal(float[] data) + { + if (m_PCMReaderCallback != null) + m_PCMReaderCallback( data ); + } + + CONDITIONAL ENABLE_AUDIO_FMOD + CSRAW private void InvokePCMSetPositionCallback_Internal(int position) + { + if (m_PCMSetPositionCallback != null) + m_PCMSetPositionCallback( position ); + } + + CONDITIONAL ENABLE_AUDIO_FMOD + CUSTOM private static AudioClip Construct_Internal() + { + AudioClip* clip = NEW_OBJECT(AudioClip); + return Scripting::ScriptingWrapperFor ( clip ); + } + + CONDITIONAL ENABLE_AUDIO_FMOD + CUSTOM private void Init_Internal(string name, int lengthSamples, int channels, int frequency, bool _3D, bool stream) + { + self->CreateUserSound( name, lengthSamples, channels, frequency, _3D, stream ); + } + +END + +// Describes when an [[AudioSource]] or [[AudioListener]] is updated. +ENUM AudioVelocityUpdateMode + // Updates the source or listener in the fixed update loop if it is attached to a [[Rigidbody]], dynamic otherwise. + Auto = 0, + // Updates the source or listener in the fixed update loop. + Fixed = 1, + // Updates the source or listener in the dynamic update loop. + Dynamic = 2, +END + + +// Representation of a listener in 3D space. +CONDITIONAL ENABLE_AUDIO +CLASS AudioListener : Behaviour + // Controls the game sound volume (0.0 to 1.0) + CUSTOM_PROP static float volume { return GetAudioManager ().GetVolume (); } { GetAudioManager ().SetVolume (value); } + + // The paused state of the audio. If set to True, the listener will not generate sound. + CUSTOM_PROP static bool pause { return GetAudioManager ().GetPause (); } { GetAudioManager ().SetPause (value); } + + // This lets you set whether the Audio Listener should be updated in the fixed or dynamic update. + + AUTO_PROP AudioVelocityUpdateMode velocityUpdateMode GetVelocityUpdateMode SetVelocityUpdateMode + + CONDITIONAL ENABLE_AUDIO_FMOD + CUSTOM static private void GetOutputDataHelper(float[] samples, int channel) + { + FMOD::ChannelGroup* channelGroup; + FMOD_RESULT result = GetAudioManager().GetFMODSystem()->getMasterChannelGroup(&channelGroup); + + if (result == FMOD_OK && channelGroup) + { + channelGroup->getWaveData(Scripting::GetScriptingArrayStart<float>(samples), GetScriptingArraySize(samples), channel); + } + } + + CONDITIONAL ENABLE_AUDIO_FMOD + CUSTOM static private void GetSpectrumDataHelper(float[] samples, int channel, FFTWindow window) + { + FMOD::ChannelGroup* channelGroup; + FMOD_RESULT result = GetAudioManager().GetFMODSystem()->getMasterChannelGroup(&channelGroup); + + if (result == FMOD_OK && channelGroup) + { + channelGroup->getSpectrum(Scripting::GetScriptingArrayStart<float>(samples), GetScriptingArraySize(samples), channel, (FMOD_DSP_FFT_WINDOW)window); + } + } + + // Returns a block of the listener (master)'s output data + CSRAW + CONDITIONAL ENABLE_AUDIO_FMOD + OBSOLETE warning GetOutputData returning a float[] is deprecated, use GetOutputData and pass a pre allocated array instead. + public static float[] GetOutputData(int numSamples, int channel) + { + float[] samples = new float[numSamples]; + GetOutputDataHelper(samples, channel); + return samples; + } + + // Returns a block of the listener (master)'s output data + CONDITIONAL ENABLE_AUDIO_FMOD + CSRAW static public void GetOutputData(float[] samples, int channel) + { + GetOutputDataHelper(samples, channel); + } + + // Returns a block of the listener (master)'s spectrum data + CSRAW + CONDITIONAL ENABLE_AUDIO_FMOD + OBSOLETE warning GetSpectrumData returning a float[] is deprecated, use GetOutputData and pass a pre allocated array instead. + public static float[] GetSpectrumData(int numSamples, int channel, FFTWindow window) + { + float[] samples = new float[numSamples]; + GetSpectrumDataHelper(samples, channel, window); + return samples; + } + + // Returns a block of the listener (master)'s spectrum data + CONDITIONAL ENABLE_AUDIO_FMOD + CSRAW static public void GetSpectrumData(float[] samples, int channel, FFTWindow window) + { + GetSpectrumDataHelper(samples, channel, window); + } + +END + + +// Spectrum analysis windowing types +CONDITIONAL ENABLE_AUDIO_FMOD +ENUM FFTWindow + // w[n] = 1.0 + Rectangular = 0, + // w[n] = TRI(2n/N) + Triangle = 1, + // w[n] = 0.54 - (0.46 * COS(n/N) ) + Hamming = 2, + // w[n] = 0.5 * (1.0 - COS(n/N) ) + Hanning = 3, + // w[n] = 0.42 - (0.5 * COS(n/N) ) + (0.08 * COS(2.0 * n/N) ) + Blackman = 4, + // w[n] = 0.35875 - (0.48829 * COS(1.0 * n/N)) + (0.14128 * COS(2.0 * n/N)) - (0.01168 * COS(3.0 * n/N)) + BlackmanHarris = 5 +END + +// Rolloff modes that a 3D sound can have in an audio source. +CONDITIONAL ENABLE_AUDIO +ENUM AudioRolloffMode + // Use this mode when you want a real-world rolloff. + Logarithmic = 0, + + + // Use this mode when you want to lower the volume of your sound over the distance + Linear = 1, + + + // Use this when you want to use a custom rolloff. + Custom = 2 +END + +// A representation of audio sources in 3D. +CONDITIONAL ENABLE_AUDIO +CLASS AudioSource : Behaviour + // The volume of the audio source (0.0 to 1.0) + AUTO_PROP float volume GetVolume SetVolume + + // The pitch of the audio source. + CUSTOM_PROP float pitch + { + return self->GetPitch(); + } + { + if(!IsFinite(value)) + { + WarningStringObject("Attempt to set pitch to infinite value from script ignored!", self); + return; + } + if(IsNAN(value)) + { + WarningStringObject("Attempt to set pitch to NaN value from script ignored!", self); + return; + } + self->SetPitch(value); + } + + // Playback position in seconds. + CONDITIONAL ENABLE_AUDIO + AUTO_PROP float time GetSecPosition SetSecPosition + + // Playback position in PCM samples. + CONDITIONAL ENABLE_AUDIO_FMOD + THREAD_SAFE AUTO_PROP int timeSamples GetSamplePosition SetSamplePosition + + // The default [[AudioClip]] to play + AUTO_PTR_PROP AudioClip clip GetAudioClip SetAudioClip + + // Plays the ::ref::clip with a certain delay (the optional delay argument is deprecated since 4.1a3) and the functionality has been replated by PlayDelayed. + + CUSTOM void Play (UInt64 delay=0) + { + if (delay > 0 && IS_CONTENT_NEWER_OR_SAME (kUnityVersion4_1_a3)) + { + WarningStringObject("Delayed playback via the optional argument of Play is deprecated. Use PlayDelayed instead!", self); + } + self->Play((double)delay * (const double)(-1.0 / 44100.0)); + } + + // Plays the ::ref::clip with a delay specified in seconds. Users are advised to use this function instead of the old Play(delay) function that took a delay specified in samples relative to a reference rate of 44.1 kHz as an argument. + CUSTOM void PlayDelayed (float delay) + { + self->Play((delay < 0.0f) ? 0.0 : -(double)delay); + } + + // Schedules the ::ref::clip to play at the specified absolute time. This is the preferred way to stitch AudioClips in music players because it is independent of the frame rate and gives the audio system enough time to prepare the playback of the sound to fetch it from media where the opening and buffering takes a lot of time (streams) without causing sudden performance peaks. + CUSTOM void PlayScheduled (double time) + { + self->Play((time < 0.0) ? 0.0 : time); + } + + // Changes the time at which a sound that has already been scheduled to play will start. Notice that depending on the timing not all rescheduling requests can be fulfilled. + CUSTOM void SetScheduledStartTime (double time) + { + self->SetScheduledStartTime(time); + } + + // Changes the time at which a sound that has already been scheduled to play will end. Notice that depending on the timing not all rescheduling requests can be fulfilled. + CUSTOM void SetScheduledEndTime (double time) + { + self->SetScheduledEndTime(time); + } + + // Stops playing the ::ref::clip. + CUSTOM void Stop() + { + self->Stop(true); + } + + // Pauses playing the ::ref::clip. + AUTO void Pause (); + + // Is the ::ref::clip playing right now (RO)? + AUTO_PROP bool isPlaying IsPlayingScripting + + // Plays an [[AudioClip]], and scales the [[AudioSource]] volume by volumeScale. + CONDITIONAL ENABLE_AUDIO + CUSTOM void PlayOneShot (AudioClip clip, float volumeScale = 1.0F) { if (clip) self->PlayOneShot (*clip, volumeScale); } + + // Plays the clip at position. Automatically cleans up the audio source after it has finished playing. + + CONDITIONAL ENABLE_AUDIO + CSRAW public static void PlayClipAtPoint (AudioClip clip, Vector3 position, float volume = 1.0F) + { + GameObject go = new GameObject ("One shot audio"); + go.transform.position = position; + AudioSource source = (AudioSource)go.AddComponent (typeof(AudioSource)); + source.clip = clip; + source.volume = volume; + source.Play (); + // Note: timeScale > 1 means that game time is accelerated. However, the sounds play at their normal speed, so we need to postpone the point in time, when the sound is stopped. + Destroy (go, clip.length * Time.timeScale); + } + + // Is the audio clip looping? + AUTO_PROP bool loop GetLoop SetLoop + + // This makes the audio source not take into account the volume of the audio listener. + CUSTOM_PROP bool ignoreListenerVolume + { + return self->GetIgnoreListenerVolume(); + } + { + self->SetIgnoreListenerVolume(value); + } + + // If set to true, the audio source will automatically start playing on awake + AUTO_PROP bool playOnAwake GetPlayOnAwake SetPlayOnAwake + + // If set to true, the audio source will be playable while the AudioListener is paused + AUTO_PROP bool ignoreListenerPause GetIgnoreListenerPause SetIgnoreListenerPause + + // Whether the Audio Source should be updated in the fixed or dynamic update. + AUTO_PROP AudioVelocityUpdateMode velocityUpdateMode GetVelocityUpdateMode SetVelocityUpdateMode + + + // Sets how much the 3d engine has an effect on the channel. + AUTO_PROP float panLevel GetPanLevel SetPanLevel + + // Bypass effects + CONDITIONAL ENABLE_AUDIO_FMOD + AUTO_PROP bool bypassEffects GetBypassEffects SetBypassEffects + + // Bypass listener effects + CONDITIONAL ENABLE_AUDIO_FMOD + AUTO_PROP bool bypassListenerEffects GetBypassListenerEffects SetBypassListenerEffects + // Bypass reverb zones + CONDITIONAL ENABLE_AUDIO_FMOD + AUTO_PROP bool bypassReverbZones GetBypassReverbZones SetBypassReverbZones + + // Sets the Doppler scale for this AudioSource + AUTO_PROP float dopplerLevel GetDopplerLevel SetDopplerLevel + + // Sets the spread angle a 3d stereo or multichannel sound in speaker space. + AUTO_PROP float spread GetSpread SetSpread + + // Sets the priority of the [[AudioSource]] + CONDITIONAL ENABLE_AUDIO_FMOD + AUTO_PROP int priority GetPriority SetPriority + + // Un- / Mutes the AudioSource. Mute sets the volume=0, Un-Mute restore the original volume. + AUTO_PROP bool mute GetMute SetMute + + // Within the Min distance the AudioSource will cease to grow louder in volume. + AUTO_PROP float minDistance GetMinDistance SetMinDistance + + // (Logarithmic rolloff) MaxDistance is the distance a sound stops attenuating at. + AUTO_PROP float maxDistance GetMaxDistance SetMaxDistance + + // Sets a channels pan position linearly. Only works for 2D clips. + AUTO_PROP float pan GetPan SetPan + + // Sets/Gets how the AudioSource attenuates over distance + + AUTO_PROP AudioRolloffMode rolloffMode GetRolloffMode SetRolloffMode + + CUSTOM private void GetOutputDataHelper(float[] samples, int channel) + { +#if ENABLE_AUDIO_FMOD + self->GetOutputData(Scripting::GetScriptingArrayStart<float>(samples), GetScriptingArraySize(samples), channel); +#endif + } + + // Returns a block of the currently playing source's output data + CSRAW + CONDITIONAL ENABLE_AUDIO_FMOD + OBSOLETE warning GetOutputData return a float[] is deprecated, use GetOutputData passing a pre allocated array instead. + public float[] GetOutputData(int numSamples, int channel) + { + float[] samples = new float[numSamples]; + GetOutputDataHelper(samples, channel); + return samples; + } + + // Returns a block of the currently playing source's output data + CONDITIONAL ENABLE_AUDIO_FMOD + CSRAW public void GetOutputData(float[] samples, int channel) + { + GetOutputDataHelper(samples, channel); + } + + CONDITIONAL ENABLE_AUDIO_FMOD + CUSTOM private void GetSpectrumDataHelper(float[] samples, int channel, FFTWindow window) + { + self->GetSpectrumData(Scripting::GetScriptingArrayStart<float>(samples), GetScriptingArraySize(samples), channel, (FMOD_DSP_FFT_WINDOW) window); + } + + // Returns a block of the currently playing source's spectrum data + CSRAW + CONDITIONAL ENABLE_AUDIO_FMOD + OBSOLETE warning GetSpectrumData returning a float[] is deprecated, use GetSpectrumData passing a pre allocated array instead. + public float[] GetSpectrumData(int numSamples, int channel, FFTWindow window) + { + float[] samples = new float[numSamples]; + GetSpectrumDataHelper(samples, channel, window); + return samples; + } + + // Returns a block of the currently playing source's spectrum data + CONDITIONAL ENABLE_AUDIO_FMOD + CSRAW public void GetSpectrumData(float[] samples, int channel, FFTWindow window) + { + GetSpectrumDataHelper(samples, channel, window); + } + + FLUSHCONDITIONS + + OBSOLETE error minVolume is not supported anymore. Use min-, maxDistance and rolloffMode instead. + CUSTOM_PROP float minVolume + { + ErrorString("minVolume is not supported anymore. Use min-, maxDistance and rolloffMode instead."); + return 0.0f; + } + { + ErrorString("minVolume is not supported anymore. Use min-, maxDistance and rolloffMode instead."); + } + OBSOLETE error maxVolume is not supported anymore. Use min-, maxDistance and rolloffMode instead. + CUSTOM_PROP float maxVolume + { + ErrorString("maxVolume is not supported anymore. Use min-, maxDistance and rolloffMode instead."); + return 0.0f; + } + { + ErrorString("maxVolume is not supported anymore. Use min-, maxDistance and rolloffMode instead."); + } + OBSOLETE error rolloffFactor is not supported anymore. Use min-, maxDistance and rolloffMode instead. + CUSTOM_PROP float rolloffFactor + { + ErrorString("rolloffFactor is not supported anymore. Use min-, maxDistance and rolloffMode instead."); + return 0.0f; + } + { + ErrorString("rolloffFactor is not supported anymore. Use min-, maxDistance and rolloffMode instead."); + } +END +// Reverb presets used by the Reverb Zone class and the audio reverb filter +ENUM AudioReverbPreset + // No reverb preset selected + Off = 0, + + // Generic preset. + Generic = 1, + + // Padded cell preset. + PaddedCell = 2, + + // Room preset. + Room = 3, + + // Bathroom preset. + Bathroom = 4, + + // Livingroom preset + Livingroom = 5, + + // Stoneroom preset + Stoneroom = 6, + + // Auditorium preset. + Auditorium = 7, + + // Concert hall preset. + Concerthall = 8, + + // Cave preset. + Cave = 9, + + // Arena preset. + Arena = 10, + + // Hangar preset. + Hangar = 11, + + // Carpeted hallway preset. + CarpetedHallway = 12, + + // Hallway preset. + Hallway = 13, + + // Stone corridor preset. + StoneCorridor = 14, + + // Alley preset. + Alley = 15, + + // Forest preset. + Forest = 16, + + // City preset. + City = 17, + + // Mountains preset. + Mountains = 18, + + // Quarry preset. + Quarry = 19, + + // Plain preset. + Plain = 20, + + // Parking Lot preset + ParkingLot = 21, + + // Sewer pipe preset. + SewerPipe = 22, + + // Underwater presset + Underwater = 23, + + // Drugged preset + Drugged = 24, + + // Dizzy preset. + Dizzy = 25, + + // Psychotic preset. + Psychotic = 26, + + // User defined preset. + User = 27 +END + +// Reverb Zones are used when you want to gradually change from a point +CONDITIONAL ENABLE_AUDIO_FMOD +CLASS AudioReverbZone : Behaviour + // The distance from the centerpoint that the reverb will have full effect at. Default = 10.0. + AUTO_PROP float minDistance GetMinDistance SetMinDistance + + // The distance from the centerpoint that the reverb will not have any effect. Default = 15.0. + AUTO_PROP float maxDistance GetMaxDistance SetMaxDistance + + // Set/Get reverb preset properties + AUTO_PROP AudioReverbPreset reverbPreset GetReverbPreset SetReverbPreset + + // room effect level (at mid frequencies) + AUTO_PROP int room GetRoom SetRoom + // relative room effect level at high frequencies + AUTO_PROP int roomHF GetRoomHF SetRoomHF + // relative room effect level at low frequencies + AUTO_PROP int roomLF GetRoomLF SetRoomLF + // reverberation decay time at mid frequencies + AUTO_PROP float decayTime GetDecayTime SetDecayTime + // high-frequency to mid-frequency decay time ratio + AUTO_PROP float decayHFRatio GetDecayHFRatio SetDecayHFRatio + // early reflections level relative to room effect + AUTO_PROP int reflections GetReflections SetReflections + // initial reflection delay time + AUTO_PROP float reflectionsDelay GetReflectionsDelay SetReflectionsDelay + // late reverberation level relative to room effect + AUTO_PROP int reverb GetReverb SetReverb + // late reverberation delay time relative to initial reflection + AUTO_PROP float reverbDelay GetReverbDelay SetReverbDelay + // reference high frequency (hz) + AUTO_PROP float HFReference GetHFReference SetHFReference + // reference low frequency (hz) + AUTO_PROP float LFReference GetLFReference SetLFReference + // like rolloffscale in global settings, but for reverb room size effect + AUTO_PROP float roomRolloffFactor GetRoomRolloffFactor SetRoomRolloffFactor + // Value that controls the echo density in the late reverberation decay + AUTO_PROP float diffusion GetDiffusion SetDiffusion + // Value that controls the modal density in the late reverberation decay + AUTO_PROP float density GetDensity SetDensity +END + + +// The Audio Low Pass Filter filter passes low frequencies of an +CONDITIONAL ENABLE_AUDIO_FMOD +CLASS AudioLowPassFilter : Behaviour + // Lowpass cutoff frequency in hz. 10.0 to 22000.0. Default = 5000.0. + AUTO_PROP float cutoffFrequency GetCutoffFrequency SetCutoffFrequency + + // Determines how much the filter's self-resonance is dampened. + AUTO_PROP float lowpassResonaceQ GetLowpassResonanceQ SetLowpassResonanceQ +END +// The Audio High Pass Filter passes high frequencies of an AudioSource and +CONDITIONAL ENABLE_AUDIO_FMOD +CLASS AudioHighPassFilter : Behaviour + // Highpass cutoff frequency in hz. 10.0 to 22000.0. Default = 5000.0. + AUTO_PROP float cutoffFrequency GetCutoffFrequency SetCutoffFrequency + + + // Determines how much the filter's self-resonance isdampened. + AUTO_PROP float highpassResonaceQ GetHighpassResonanceQ SetHighpassResonanceQ +END +// The Audio Distortion Filter distorts the sound from an AudioSource or +CONDITIONAL ENABLE_AUDIO_FMOD +CLASS AudioDistortionFilter : Behaviour + // Distortion value. 0.0 to 1.0. Default = 0.5. + AUTO_PROP float distortionLevel GetDistortionLevel SetDistortionLevel +END + +// The Audio Echo Filter repeats a sound after a given Delay, attenuating +CONDITIONAL ENABLE_AUDIO_FMOD +CLASS AudioEchoFilter : Behaviour + // Echo delay in ms. 10 to 5000. Default = 500. + AUTO_PROP float delay GetDelay SetDelay + + + // Echo decay per delay. 0 to 1. 1.0 = No decay, 0.0 = total decay (i.e. simple 1 line delay). Default = 0.5. + AUTO_PROP float decayRatio GetDecayRatio SetDecayRatio + + + // Volume of original signal to pass to output. 0.0 to 1.0. Default = 1.0. + AUTO_PROP float dryMix GetDryMix SetDryMix + + + // Volume of echo signal to pass to output. 0.0 to 1.0. Default = 1.0. + AUTO_PROP float wetMix GetWetMix SetWetMix +END + +// The Audio Chorus Filter takes an Audio Clip and processes it creating a chorus effect. +CONDITIONAL ENABLE_AUDIO_FMOD +CLASS AudioChorusFilter : Behaviour + // Volume of original signal to pass to output. 0.0 to 1.0. Default = 0.5. + AUTO_PROP float dryMix GetDryMix SetDryMix + + + // Volume of 1st chorus tap. 0.0 to 1.0. Default = 0.5. + AUTO_PROP float wetMix1 GetWetMix1 SetWetMix1 + + + // Volume of 2nd chorus tap. This tap is 90 degrees out of phase of the first tap. 0.0 to 1.0. Default = 0.5. + AUTO_PROP float wetMix2 GetWetMix2 SetWetMix2 + + + // Volume of 3rd chorus tap. This tap is 90 degrees out of phase of the second tap. 0.0 to 1.0. Default = 0.5. + AUTO_PROP float wetMix3 GetWetMix3 SetWetMix3 + + + // Chorus delay in ms. 0.1 to 100.0. Default = 40.0 ms. + AUTO_PROP float delay GetDelay SetDelay + + + // Chorus modulation rate in hz. 0.0 to 20.0. Default = 0.8 hz. + AUTO_PROP float rate GetRate SetRate + + + // Chorus modulation depth. 0.0 to 1.0. Default = 0.03. + AUTO_PROP float depth GetDepth SetDepth + + /// Chorus feedback. Controls how much of the wet signal gets fed back into the chorus buffer. 0.0 to 1.0. Default = 0.0. + OBSOLETE warning feedback is deprecated, this property does nothing. + CUSTOM_PROP float feedback + { + return 0.0f; + } + {} + +END +// The Audio Reverb Filter takes an Audio Clip and distortionates it in a +CONDITIONAL ENABLE_AUDIO_FMOD +CLASS AudioReverbFilter : Behaviour + // Set/Get reverb preset properties + AUTO_PROP AudioReverbPreset reverbPreset GetReverbPreset SetReverbPreset + // Mix level of dry signal in output in mB. Ranges from -10000.0 to 0.0. Default is 0. + AUTO_PROP float dryLevel GetDryLevel SetDryLevel + // Room effect level at low frequencies in mB. Ranges from -10000.0 to 0.0. Default is 0.0. + AUTO_PROP float room GetRoom SetRoom + // Room effect high-frequency level re. low frequency level in mB. Ranges from -10000.0 to 0.0. Default is 0.0. + AUTO_PROP float roomHF GetRoomHF SetRoomHF + // Rolloff factor for room effect. Ranges from 0.0 to 10.0. Default is 10.0 + AUTO_PROP float roomRolloff GetRoomRolloff SetRoomRolloff + // Reverberation decay time at low-frequencies in seconds. Ranges from 0.1 to 20.0. Default is 1.0. + AUTO_PROP float decayTime GetDecayTime SetDecayTime + // Decay HF Ratio : High-frequency to low-frequency decay time ratio. Ranges from 0.1 to 2.0. Default is 0.5. + AUTO_PROP float decayHFRatio GetDecayHFRatio SetDecayHFRatio + // Early reflections level relative to room effect in mB. Ranges from -10000.0 to 1000.0. Default is -10000.0. + AUTO_PROP float reflectionsLevel GetReflectionsLevel SetReflectionsLevel + // Late reverberation level relative to room effect in mB. Ranges from -10000.0 to 2000.0. Default is 0.0. + AUTO_PROP float reflectionsDelay GetReflectionsDelay SetReflectionsDelay + // Late reverberation level relative to room effect in mB. Ranges from -10000.0 to 2000.0. Default is 0.0. + AUTO_PROP float reverbLevel GetReverbLevel SetReverbLevel + // Late reverberation delay time relative to first reflection in seconds. Ranges from 0.0 to 0.1. Default is 0.04. + AUTO_PROP float reverbDelay GetReverbDelay SetReverbDelay + // Reverberation diffusion (echo density) in percent. Ranges from 0.0 to 100.0. Default is 100.0. + AUTO_PROP float diffusion GetDiffusion SetDiffusion + // Reverberation density (modal density) in percent. Ranges from 0.0 to 100.0. Default is 100.0. + AUTO_PROP float density GetDensity SetDensity + // Reference high frequency in Hz. Ranges from 20.0 to 20000.0. Default is 5000.0. + AUTO_PROP float hfReference GetHFReference SetHFReference + // Room effect low-frequency level in mB. Ranges from -10000.0 to 0.0. Default is 0.0. + AUTO_PROP float roomLF GetRoomLF SetRoomLF + // Reference low-frequency in Hz. Ranges from 20.0 to 1000.0. Default is 250.0. + AUTO_PROP float lFReference GetLFReference SetLFReference +END + + + +CONDITIONAL ENABLE_MICROPHONE +// Use this class to record to an [[AudioClip|audio clip]] using a connected microphone. +CLASS Microphone + // Start Recording with device + + CUSTOM static AudioClip Start(string deviceName, bool loop, int lengthSec, int frequency) + { + return Scripting::ScriptingWrapperFor( GetAudioManager().StartRecord( GetAudioManager().GetMicrophoneDeviceIDFromName ( deviceName ) , loop, lengthSec, frequency ) ); + } + + // Stops recording + CUSTOM static void End(string deviceName) + { + GetAudioManager().EndRecord( GetAudioManager().GetMicrophoneDeviceIDFromName ( deviceName ) ); + } + + // Gives you a list microphone devices, identified by name. + CUSTOM_PROP static string[] devices + { + std::vector<std::string> names; + names = GetAudioManager().GetRecordDevices(); + + ScriptingArrayPtr array = CreateScriptingArray<ScriptingStringPtr> (MONO_COMMON.string, names.size ()); + for (int i=0;i<names.size ();i++) + Scripting::SetScriptingArrayElement (array, i, scripting_string_new ( (const char*)names[i].c_str() )); + + return array; + } + + // Query if a device is currently recording. + CUSTOM static bool IsRecording(string deviceName) + { + return GetAudioManager().IsRecording( GetAudioManager().GetMicrophoneDeviceIDFromName ( deviceName ) ); + } + + // Get the position in samples of the recording. + THREAD_SAFE CUSTOM static int GetPosition(string deviceName) + { + return GetAudioManager().GetRecordPosition( GetAudioManager().GetMicrophoneDeviceIDFromName ( deviceName ) ); + } + + // Get the frequency capabilities of a device. + CUSTOM static void GetDeviceCaps(string deviceName, out int minFreq, out int maxFreq) + { + GetAudioManager().GetDeviceCaps( GetAudioManager().GetMicrophoneDeviceIDFromName ( deviceName ), minFreq, maxFreq ); + } + +END + + +CSRAW } + diff --git a/Runtime/Audio/Utilities/Conversion.h b/Runtime/Audio/Utilities/Conversion.h new file mode 100644 index 0000000..4bd8fad --- /dev/null +++ b/Runtime/Audio/Utilities/Conversion.h @@ -0,0 +1,291 @@ +/* + * AudioConversion.h + * audio-utils + * + * Created by Søren Christiansen on 2/9/11. + * Copyright 2011 Unity Technologies. All rights reserved. + * + */ +#pragma once + +#include <algorithm> +#include <functional> +#include <assert.h> +#include "Runtime/Audio/correct_fmod_includer.h" +#include "Runtime/Utilities/dynamic_array.h" + +using std::unary_function; +using std::transform; + +typedef union { + UInt8 int8[3]; +} SInt24; + +template<typename InType, typename OutType > +struct Reformat : public unary_function<InType, OutType> { + float operator()(const InType x) { assert("NO REFORMAT BETWEEN THESE TYPES"); return OutType(); } +}; + +// --> normalized float +template<typename InType > +struct Reformat<InType, float> : public unary_function<InType, float> { + float operator()(const InType x) { return ( (float) x ) / ( 1 << ( ( sizeof(InType) * 8) - 1 ) ) ; } +}; + +template< > +struct Reformat<SInt16, float> : public unary_function<SInt16, float> { + float operator()(const SInt16 x) { return ( ((float)x) / ( 1 << 15)) ; } +}; + +template< > +struct Reformat<float, float> : public unary_function<float, float> { + float operator()(const float x) { return x; } +}; + +template< > +struct Reformat<SInt24, float> : public unary_function<SInt24, float> { + float operator()(const SInt24 x) + { + int val; + val = ((unsigned int)x.int8[0] << 8); + val |= ((unsigned int)x.int8[1] << 16); + val |= ((unsigned int)x.int8[2] << 24); + val >>= 8; + return (float)(val) * (1.0f / (float)(1<<23)); + } +}; + +// --> 16 bit +template< > +struct Reformat<UInt8, SInt16> { + SInt16 operator()(const UInt8 x) { return (( ( UInt16 ) x << 8 )); } +}; + +template< > +struct Reformat<SInt24, SInt16> { + SInt16 operator()(const SInt24 x) { + SInt16 out = ( x.int8[2] << 8 ); + out |= ( x.int8[1] ); + return out; + } +}; + +template<typename T> +struct Reformat<float, T> { + T operator()(const float x) { return (T)(x * ( 1 << ( ( sizeof(T) * 8) - 1 ))); } +}; + +template<> +struct Reformat<float, SInt16> { + SInt16 operator()(const float x) { return SInt16(x * (1 << 15)); } +}; + +template<typename T> +struct NOP : public unary_function<T, T> { + T operator()(const T x) { return x; } +}; + +// --> helper functors +template <typename FO1, typename FO2> +class Composer : public unary_function<typename FO1::argument_type, typename FO2::result_type> { +private: + typedef typename FO2::result_type FO2Result; + typedef typename FO1::argument_type FO1Arg; + +public: + FO1 fo1; // first/inner function object to call + FO2 fo2; // second/outer function object to call + + // constructor: initialize function objects + Composer (FO1 f1, FO2 f2) + : fo1(f1), fo2(f2) { + } + + // ''function call'': nested call of function objects + FO2Result operator() (const FO1Arg v) { + return fo2(fo1(v)); + } +}; + +template <typename FO1, typename FO2> +inline +Composer<FO1,FO2> compose (FO1 f1, FO2 f2) { + return Composer<FO1,FO2> (f1, f2); +} + +template<typename _InputIterator, typename _Function> +_Function +for_each_channel(_InputIterator __first, _InputIterator __last, _Function __f, int channel, int channels) +{ + for ( __first = __first + channel; __first < __last - channel; __first = __first+channels) + __f(*__first); + return __f; +} + +// --> helper functions interface +typedef Reformat<SInt8, float> SInt8ToFloat; +typedef Reformat<SInt16, float> SInt16ToFloat; +typedef Reformat<SInt24, float> SInt24ToFloat; +typedef Reformat<SInt32, float> SInt32ToFloat; +typedef Reformat<float, float> FloatToFloat; +typedef Reformat<float, SInt8> FloatToSInt8; +typedef Reformat<float, SInt16> FloatToSInt16; +typedef Reformat<float, SInt24> FloatToSInt24; +typedef Reformat<float, SInt32> FloatToSInt32; + +template<typename _Function> +inline void ArrayToNormFloat(FMOD_SOUND_FORMAT inFormat, const void* beginIterator, const void* endIterator, dynamic_array<float>& v, _Function __f) +{ + if (inFormat == FMOD_SOUND_FORMAT_PCM8) + std::transform((const SInt8*) beginIterator, (const SInt8*) endIterator, std::back_inserter(v), compose( SInt8ToFloat(), __f )); + else if (inFormat == FMOD_SOUND_FORMAT_PCM16) + std::transform((const SInt16*) beginIterator, (const SInt16*) endIterator, std::back_inserter(v), compose ( SInt16ToFloat(), __f)); + else if (inFormat == FMOD_SOUND_FORMAT_PCM24) + std::transform((const SInt24*) beginIterator, (const SInt24*) endIterator, std::back_inserter(v), compose ( SInt24ToFloat(), __f)); + else if (inFormat == FMOD_SOUND_FORMAT_PCM32) + std::transform((const SInt32*) beginIterator, (const SInt32*) endIterator, std::back_inserter(v), compose ( SInt32ToFloat(), __f)); + else if (inFormat == FMOD_SOUND_FORMAT_PCMFLOAT) + std::transform((const float*) beginIterator, (const float*) endIterator, std::back_inserter(v), compose ( FloatToFloat(), __f)); +} + +inline void ArrayToNormFloat(FMOD_SOUND_FORMAT inFormat, const void* beginIterator, const void* endIterator, dynamic_array<float>& v) +{ + if (inFormat == FMOD_SOUND_FORMAT_PCM8) + std::transform((const SInt8*) beginIterator, (const SInt8*) endIterator, std::back_inserter(v), SInt8ToFloat()); + else if (inFormat == FMOD_SOUND_FORMAT_PCM16) + std::transform((const SInt16*) beginIterator, (const SInt16*) endIterator, std::back_inserter(v), SInt16ToFloat()); + else if (inFormat == FMOD_SOUND_FORMAT_PCM24) + std::transform((const SInt24*) beginIterator, (const SInt24*) endIterator, std::back_inserter(v), SInt24ToFloat()); + else if (inFormat == FMOD_SOUND_FORMAT_PCM32) + std::transform((const SInt32*) beginIterator, (const SInt32*) endIterator, std::back_inserter(v), SInt32ToFloat()); + else if (inFormat == FMOD_SOUND_FORMAT_PCMFLOAT) + std::transform((const float*) beginIterator, (const float*) endIterator, std::back_inserter(v), FloatToFloat()); +} + +template<typename T> +inline void ArrayFromNormFloat(FMOD_SOUND_FORMAT outFormat, const float* beginIterator, const float* endIterator, dynamic_array<T>& v) +{ + if (outFormat == FMOD_SOUND_FORMAT_PCM8) + std::transform(beginIterator, endIterator, std::back_inserter(v), FloatToSInt8()); + else if (outFormat == FMOD_SOUND_FORMAT_PCM16) + std::transform( beginIterator, endIterator, std::back_inserter(v), FloatToSInt16()); + //else if (outFormat == FMOD_SOUND_FORMAT_PCM24) + // std::transform( beginIterator, endIterator, std::back_inserter(v), FloatToSInt24()); + else if (outFormat == FMOD_SOUND_FORMAT_PCM32) + std::transform( beginIterator, endIterator, std::back_inserter(v), FloatToSInt32()); + else if (outFormat == FMOD_SOUND_FORMAT_PCMFLOAT) + std::transform( beginIterator, endIterator, std::back_inserter(v), FloatToFloat()); +} + +inline void ArrayFromNormFloat(FMOD_SOUND_FORMAT outFormat, const float* beginIterator, const float* endIterator, void* outbuffer) +{ + if (outFormat == FMOD_SOUND_FORMAT_PCM8) + { + SInt8* dstPtr = (SInt8*)outbuffer; + while (beginIterator != endIterator) + { + *dstPtr = FloatToSInt8()(*beginIterator); + beginIterator++; + dstPtr++; + } + } + else + if (outFormat == FMOD_SOUND_FORMAT_PCM16) + { + SInt16* dstPtr = (SInt16*)outbuffer; + while (beginIterator != endIterator) + { + *dstPtr = FloatToSInt16()(*beginIterator); + beginIterator++; + dstPtr++; + } + } + else + if (outFormat == FMOD_SOUND_FORMAT_PCM32) + { + SInt32* dstPtr = (SInt32*)outbuffer; + while (beginIterator != endIterator) + { + *dstPtr = FloatToSInt32()(*beginIterator); + beginIterator++; + dstPtr++; + } + } + else + if (outFormat == FMOD_SOUND_FORMAT_PCMFLOAT) + { + memcpy (outbuffer, beginIterator, (endIterator - beginIterator) * sizeof(float)); + } + else { + Assert("Conversion NOT supported"); + } + +} + + +inline void ArrayToNormFloat(FMOD_SOUND_FORMAT inFormat, const void* beginIterator, const void* endIterator, float* outbuffer) +{ + if (inFormat == FMOD_SOUND_FORMAT_PCM8) + { + SInt8* srcPtr = (SInt8*)beginIterator; + while (srcPtr != (SInt8*)endIterator) + { + *outbuffer = SInt8ToFloat()(*srcPtr); + srcPtr++; + outbuffer++; + } + } + else + if (inFormat == FMOD_SOUND_FORMAT_PCM16) + { + SInt16* srcPtr = (SInt16*)beginIterator; + while (srcPtr != (SInt16*)endIterator) + { + *outbuffer = SInt16ToFloat()(*srcPtr); + srcPtr++; + outbuffer++; + } + } + else + if (inFormat == FMOD_SOUND_FORMAT_PCM24) + { + SInt24* srcPtr = (SInt24*)beginIterator; + while (srcPtr != (SInt24*)endIterator) + { + *outbuffer = SInt24ToFloat()(*srcPtr); + srcPtr++; + outbuffer++; + } + } + else + if (inFormat == FMOD_SOUND_FORMAT_PCM32) + { + SInt32* srcPtr = (SInt32*)beginIterator; + while (srcPtr != (SInt32*)endIterator) + { + *outbuffer = SInt32ToFloat()(*srcPtr); + srcPtr++; + outbuffer++; + } + } + else + if (inFormat == FMOD_SOUND_FORMAT_PCMFLOAT) + { + memcpy (outbuffer, beginIterator, ((float*)endIterator - (float*)beginIterator) * sizeof(float)); + } + else { + ErrorString("Conversion from this format NOT supported"); + } +} + + + +template<typename InType, typename OutType> +inline void ReformatArray(const InType* inArray, const unsigned size, OutType* outArray) +{ + Reformat<InType, OutType> reformater; + std::transform( inArray, inArray + size, outArray, reformater ); +} + + + diff --git a/Runtime/Audio/WavReader.h b/Runtime/Audio/WavReader.h new file mode 100644 index 0000000..370ca4e --- /dev/null +++ b/Runtime/Audio/WavReader.h @@ -0,0 +1,182 @@ +#ifndef __WAVREADER_H__ +#define __WAVREADER_H__ + +typedef UInt32 FOURCC; + +struct RIFF_TAG { + FOURCC RIFF; + UInt32 size; +}; + +struct WAV_HEADER { + FOURCC RIFF; + UInt32 size; + FOURCC type; +}; + +struct WAV_FORMAT { + FOURCC ID; + UInt32 size; + UInt16 format; + UInt16 channels; + UInt32 samplerate; + UInt32 avgBytesSec; + UInt16 blockalign; + UInt16 bitsPerSample; +}; + +struct WAV_DATA { + FOURCC ID; + UInt32 size; +}; + +#if !UNITY_BIG_ENDIAN +#ifndef MAKEFOURCC +#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ + ((UInt32)(UInt8)(ch0) | ((UInt32)(UInt8)(ch1) << 8) | \ + ((UInt32)(UInt8)(ch2) << 16) | ((UInt32)(UInt8)(ch3) << 24 )) +#endif +#define btoll(x) (x) +#define btols(x) (x) +#else +#ifndef MAKEFOURCC +#define MAKEFOURCC(ch0, ch1, ch2, ch3) \ +((UInt32)(UInt8)(ch0) << 24 | ((UInt32)(UInt8)(ch1) << 16) | \ +((UInt32)(UInt8)(ch2) << 8) | ((UInt32)(UInt8)(ch3) )) +#endif +#define btoll(x) (((x) >> 24) | (((x)&0x00ff0000) >> 8) | (((x)&0x0000ff00) << 8) | ((x) << 24)) +#define btols(x) (((x) >> 8) | ((x&0xff) << 8)) +#endif + +#define RIFF_RIFF MAKEFOURCC('R','I','F','F') +#define RIFF_WAVE MAKEFOURCC('W','A','V','E') +#define RIFF_FORMAT MAKEFOURCC('f','m','t',' ') +#define RIFF_DATA MAKEFOURCC('d','a','t','a') + +static const UInt8* GetRIFFChunk ( FOURCC fourcc, const UInt8* wavRiff ) +{ + UInt8* p = const_cast<UInt8*>(wavRiff); + UInt32 next = *((UInt32*)p); + + if (next != RIFF_RIFF) // not a RIFF file + return NULL; + + UInt32 totalSize = (UInt32) *((UInt32*)(p + 4)); + + // next chunk + p += 12; + next = *((UInt32*)p); + + while (next != fourcc) + { + UInt32 size = (UInt32) *((UInt32*)(p + 4)); + + // next chunk + p += 8; + p += size; + next = *((UInt32*)p); + + if (p - wavRiff >= totalSize) + return NULL; + } + + return p; +} + +static bool IsWAV(const UInt8* wavRiff) +{ + return ((*((UInt32*)wavRiff) == RIFF_RIFF) && + (((WAV_HEADER*)(wavRiff))->type == RIFF_WAVE)); +} + +// @note return in little endian +static const WAV_HEADER* GetWAVHeader(const UInt8* wavRiff) +{ + return (WAV_HEADER*)wavRiff; +} + +// @note return in little endian +static const WAV_FORMAT* GetWAVFormat(const UInt8* wavRiff) +{ + return (WAV_FORMAT*)GetRIFFChunk(RIFF_FORMAT, wavRiff); +} + +// @note return in little endian +static const WAV_DATA* GetWAVData(const UInt8* wavRiff) +{ + return (WAV_DATA*)GetRIFFChunk(RIFF_DATA, wavRiff); +} + +// endian-safe +static bool IsNormalWAV(const UInt8* wavRiff) +{ + return IsWAV(wavRiff) && (GetWAVFormat(wavRiff)->format == 1); +} + +// endian-safe +static UInt32 GetWAVSize(const UInt8* wavRiff) +{ + return btoll(GetWAVHeader(wavRiff)->size); +} + +static void CreateRIFFTag(RIFF_TAG& tag, FOURCC fourCC, int size) +{ + tag.RIFF = fourCC; + tag.size = btoll(size); +} + + +static void CreateWAVHeader( WAV_HEADER &header, int size, int additionalTagSize = 0 ) +{ + header.RIFF = MAKEFOURCC('R','I','F','F'); + header.size = btoll ( sizeof (WAV_HEADER) + sizeof (WAV_FORMAT) + sizeof (WAV_DATA) + size + additionalTagSize ); + header.type = MAKEFOURCC('W','A','V','E'); +} + +static void CreateFMTTag( WAV_FORMAT &format, int channels, int frequency, int bitsPerSample ) +{ + format.ID = MAKEFOURCC('f','m','t',' ');; + format.size = btoll(16); + format.format = btols(1); + format.channels = btols(channels); + format.samplerate = btoll(frequency); + format.avgBytesSec = btoll((frequency * bitsPerSample) / 8); + format.blockalign = btols(bitsPerSample / 8); + format.bitsPerSample = btols(bitsPerSample); +} + + + +/** +* reconstructing a wav header + alloc size for data +* @param frequency Frequency +* @param size The size of the data +* @param channels channels +* @param bitsPerSamples Bits pr. sample +* @param ppData ptr to data chunk +* @return ptr to header + +**/ +static UInt8* CreateWAV(int frequency, int size, int channels, int bitsPerSample, UInt8** ppData) +{ + WAV_HEADER header; + WAV_FORMAT format; + WAV_DATA data; + + CreateWAVHeader(header, size); + CreateFMTTag(format, channels, frequency, bitsPerSample); + CreateRIFFTag((RIFF_TAG&)data, MAKEFOURCC('d','a','t','a'), size); + + UInt8* wav = new UInt8[ sizeof (WAV_HEADER) + sizeof (WAV_FORMAT) + sizeof (WAV_DATA) + size ]; + + memcpy(wav, &header, sizeof(WAV_HEADER)); + memcpy(wav + sizeof(WAV_HEADER), &format, sizeof(WAV_FORMAT)); + memcpy(wav + sizeof(WAV_HEADER) + sizeof(WAV_FORMAT), &data, sizeof(WAV_DATA)); + + *ppData = (UInt8*)(wav + sizeof(WAV_HEADER) + sizeof(WAV_FORMAT)) + 8; + + return wav; +} + + +#endif // __WAVREADER_H__ diff --git a/Runtime/Audio/correct_fmod_includer.h b/Runtime/Audio/correct_fmod_includer.h new file mode 100644 index 0000000..76c893d --- /dev/null +++ b/Runtime/Audio/correct_fmod_includer.h @@ -0,0 +1,39 @@ +#if ENABLE_AUDIO_FMOD + #if UNITY_WIN + #include "External/Audio/FMOD/builds/win32/include/fmod.hpp" + #include "External/Audio/FMOD/builds/win32/include/fmod_errors.h" + #include "External/Audio/FMOD/builds/win32/include/fmod_types.h" + #elif UNITY_LINUX + #if defined(__LP64__) || defined(_LP64) + #include "External/Audio/FMOD/builds/linux64/include/fmod.hpp" + #include "External/Audio/FMOD/builds/linux64/include/fmod_errors.h" + #include "External/Audio/FMOD/builds/linux64/include/fmod_types.h" + #else + #include "External/Audio/FMOD/builds/linux32/include/fmod.hpp" + #include "External/Audio/FMOD/builds/linux32/include/fmod_errors.h" + #include "External/Audio/FMOD/builds/linux32/include/fmod_types.h" + #endif + #elif UNITY_IPHONE + #include "External/Audio/FMOD/builds/iphone/include/fmod.hpp" + #include "External/Audio/FMOD/builds/iphone/include/fmod_errors.h" + #include "External/Audio/FMOD/builds/iphone/include/fmod_types.h" + #elif UNITY_PS3 + #include "External/Audio/FMOD/builds/ps3/include/fmod.hpp" + #include "External/Audio/FMOD/builds/ps3/include/fmod_errors.h" + #include "External/Audio/FMOD/builds/ps3/include/fmod_types.h" + #elif UNITY_BB10 + #include "External/Audio/FMOD/builds/bb10/include/fmod.hpp" + #include "External/Audio/FMOD/builds/bb10/include/fmod_errors.h" + #include "External/Audio/FMOD/builds/bb10/include/fmod_types.h" + #elif UNITY_TIZEN + #include "External/Audio/FMOD/builds/tizen/include/fmod.hpp" + #include "External/Audio/FMOD/builds/tizen/include/fmod_errors.h" + #include "External/Audio/FMOD/builds/tizen/include/fmod_types.h" + #else + #include <fmod.hpp> + #include <fmod_errors.h> + #include <fmod_types.h> + #endif +#elif UNITY_FLASH || UNITY_WEBGL + #include "External/Audio/FMOD/builds/win32/include/fmod.h" +#endif |