1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813 |
- /*
- File: SoundEngine.cpp
- Abstract: These functions play background music tracks, multiple sound effects,
- and support stereo panning with a low-latency response.
- Version: 1.7
- Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
- ("Apple") in consideration of your agreement to the following terms, and your
- use, installation, modification or redistribution of this Apple software
- constitutes acceptance of these terms. If you do not agree with these terms,
- please do not use, install, modify or redistribute this Apple software.
- In consideration of your agreement to abide by the following terms, and subject
- to these terms, Apple grants you a personal, non-exclusive license, under
- Apple's copyrights in this original Apple software (the "Apple Software"), to
- use, reproduce, modify and redistribute the Apple Software, with or without
- modifications, in source and/or binary forms; provided that if you redistribute
- the Apple Software in its entirety and without modifications, you must retain
- this notice and the following text and disclaimers in all such redistributions
- of the Apple Software.
- Neither the name, trademarks, service marks or logos of Apple Inc. may be used
- to endorse or promote products derived from the Apple Software without specific
- prior written permission from Apple. Except as expressly stated in this notice,
- no other rights or licenses, express or implied, are granted by Apple herein,
- including but not limited to any patent rights that may be infringed by your
- derivative works or by other works in which the Apple Software may be
- incorporated.
- The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO
- WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
- WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
- COMBINATION WITH YOUR PRODUCTS.
- IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
- GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
- ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR
- DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF
- CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF
- APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- Copyright (C) 2008 Apple Inc. All Rights Reserved.
- */
- // Local Includes
- #include "SoundEngine.h"
- #ifndef WIN32
- /*==================================================================================================
- SoundEngine.cpp
- ==================================================================================================*/
- //==================================================================================================
- // Includes
- //==================================================================================================
- // System Includes
- #include <AudioToolbox/AudioToolbox.h>
- #include <CoreFoundation/CFURL.h>
- #include <OpenAL/al.h>
- #include <OpenAL/alc.h>
- #include <map>
- #include <vector>
- #include <pthread.h>
- #include <mach/mach.h>
- #include <string>
- #define AssertNoError(inMessage, inHandler) \
- if(result != noErr) \
- { \
- printf("%s: %d\n", inMessage, (int)result); \
- goto inHandler; \
- }
-
- #define AssertNoOALError(inMessage, inHandler) \
- if((result = alGetError()) != AL_NO_ERROR) \
- { \
- printf("%s: %x\n", inMessage, (int)result); \
- goto inHandler; \
- }
- #define kNumberBuffers 3
- class OpenALObject;
- class BackgroundTrackMgr;
- static OpenALObject *sOpenALObject = NULL;
- static BackgroundTrackMgr *sBackgroundTrackMgr = NULL;
- static Float32 gMasterVolumeGain = 1.0f;
- static bool isInitialized = false;
- static bool gInterrupted = false;
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- typedef ALvoid AL_APIENTRY (*alBufferDataStaticProcPtr) (const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq);
- ALvoid alBufferDataStaticProc(const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq)
- {
- static alBufferDataStaticProcPtr proc = NULL;
-
- if (proc == NULL) {
- proc = (alBufferDataStaticProcPtr) alcGetProcAddress(NULL, (const ALCchar*) "alBufferDataStatic");
- }
-
- if (proc)
- proc(bid, format, data, size, freq);
- return;
- }
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- typedef ALvoid AL_APIENTRY (*alcMacOSXMixerOutputRateProcPtr) (const ALdouble value);
- ALvoid alcMacOSXMixerOutputRateProc(const ALdouble value)
- {
- static alcMacOSXMixerOutputRateProcPtr proc = NULL;
-
- if (proc == NULL) {
- proc = (alcMacOSXMixerOutputRateProcPtr) alcGetProcAddress(NULL, (const ALCchar*) "alcMacOSXMixerOutputRate");
- }
-
- if (proc)
- proc(value);
- return;
- }
- #pragma mark ***** OpenALThread *****
- //==================================================================================================
- // Threading functions
- //==================================================================================================
- class OpenALThread
- {
- // returns the thread's priority as it was last set by the API
- #define OpenALThread_SET_PRIORITY 0
- // returns the thread's priority as it was last scheduled by the Kernel
- #define OpenALThread_SCHEDULED_PRIORITY 1
- // Types
- public:
- typedef void* (*ThreadRoutine)(void* inParameter);
- // Constants
- public:
- enum
- {
- kMinThreadPriority = 1,
- kMaxThreadPriority = 63,
- kDefaultThreadPriority = 31
- };
- // Construction/Destruction
- public:
- OpenALThread(ThreadRoutine inThreadRoutine, void* inParameter)
- : mPThread(0),
- mSpawningThreadPriority(getScheduledPriority(pthread_self(), OpenALThread_SET_PRIORITY)),
- mThreadRoutine(inThreadRoutine),
- mThreadParameter(inParameter),
- mPriority(kDefaultThreadPriority),
- mFixedPriority(false),
- mAutoDelete(true) { }
- ~OpenALThread() { }
- // Properties
- bool IsRunning() const { return 0 != mPThread; }
- void SetAutoDelete(bool b) { mAutoDelete = b; }
- void SetPriority(UInt32 inPriority, bool inFixedPriority)
- {
- OSStatus result = noErr;
- mPriority = inPriority;
- mFixedPriority = inFixedPriority;
- if(mPThread != 0)
- {
- if (mFixedPriority)
- {
- thread_extended_policy_data_t theFixedPolicy;
- theFixedPolicy.timeshare = false; // set to true for a non-fixed thread
- result = thread_policy_set(pthread_mach_thread_np(mPThread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT);
- if (result) {
- printf("OpenALThread::SetPriority: failed to set the fixed-priority policy");
- return;
- }
- }
- // We keep a reference to the spawning thread's priority around (initialized in the constructor),
- // and set the importance of the child thread relative to the spawning thread's priority.
- thread_precedence_policy_data_t thePrecedencePolicy;
-
- thePrecedencePolicy.importance = mPriority - mSpawningThreadPriority;
- result =thread_policy_set(pthread_mach_thread_np(mPThread), THREAD_PRECEDENCE_POLICY, (thread_policy_t)&thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT);
- if (result) {
- printf("OpenALThread::SetPriority: failed to set the precedence policy");
- return;
- }
- }
- }
- // Actions
- void Start()
- {
- if(mPThread != 0)
- {
- printf("OpenALThread::Start: can't start because the thread is already running\n");
- return;
- }
- OSStatus result;
- pthread_attr_t theThreadAttributes;
-
- result = pthread_attr_init(&theThreadAttributes);
- AssertNoError("Error initializing thread", end);
-
- result = pthread_attr_setdetachstate(&theThreadAttributes, PTHREAD_CREATE_DETACHED);
- AssertNoError("Error setting thread detach state", end);
-
- result = pthread_create(&mPThread, &theThreadAttributes, (ThreadRoutine)OpenALThread::Entry, this);
- AssertNoError("Error creating thread", end);
-
- pthread_attr_destroy(&theThreadAttributes);
- AssertNoError("Error destroying thread attributes", end);
- end:
- return;
- }
- // Implementation
- protected:
- static void* Entry(OpenALThread* inOpenALThread)
- {
- void* theAnswer = NULL;
- inOpenALThread->SetPriority(inOpenALThread->mPriority, inOpenALThread->mFixedPriority);
- if(inOpenALThread->mThreadRoutine != NULL)
- {
- theAnswer = inOpenALThread->mThreadRoutine(inOpenALThread->mThreadParameter);
- }
- inOpenALThread->mPThread = 0;
- if (inOpenALThread->mAutoDelete)
- delete inOpenALThread;
- return theAnswer;
- }
- static UInt32 getScheduledPriority(pthread_t inThread, int inPriorityKind)
- {
- thread_basic_info_data_t threadInfo;
- policy_info_data_t thePolicyInfo;
- unsigned int count;
- if (inThread == NULL)
- return 0;
-
- // get basic info
- count = THREAD_BASIC_INFO_COUNT;
- thread_info (pthread_mach_thread_np (inThread), THREAD_BASIC_INFO, (thread_info_t)&threadInfo, &count);
-
- switch (threadInfo.policy) {
- case POLICY_TIMESHARE:
- count = POLICY_TIMESHARE_INFO_COUNT;
- thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_TIMESHARE_INFO, (thread_info_t)&(thePolicyInfo.ts), &count);
- if (inPriorityKind == OpenALThread_SCHEDULED_PRIORITY) {
- return thePolicyInfo.ts.cur_priority;
- }
- return thePolicyInfo.ts.base_priority;
- break;
-
- case POLICY_FIFO:
- count = POLICY_FIFO_INFO_COUNT;
- thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_FIFO_INFO, (thread_info_t)&(thePolicyInfo.fifo), &count);
- if ( (thePolicyInfo.fifo.depressed) && (inPriorityKind == OpenALThread_SCHEDULED_PRIORITY) ) {
- return thePolicyInfo.fifo.depress_priority;
- }
- return thePolicyInfo.fifo.base_priority;
- break;
-
- case POLICY_RR:
- count = POLICY_RR_INFO_COUNT;
- thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_RR_INFO, (thread_info_t)&(thePolicyInfo.rr), &count);
- if ( (thePolicyInfo.rr.depressed) && (inPriorityKind == OpenALThread_SCHEDULED_PRIORITY) ) {
- return thePolicyInfo.rr.depress_priority;
- }
- return thePolicyInfo.rr.base_priority;
- break;
- }
-
- return 0;
- }
- pthread_t mPThread;
- UInt32 mSpawningThreadPriority;
- ThreadRoutine mThreadRoutine;
- void* mThreadParameter;
- SInt32 mPriority;
- bool mFixedPriority;
- bool mAutoDelete; // delete self when thread terminates
- };
- //==================================================================================================
- // Helper functions
- //==================================================================================================
- OSStatus OpenFile(const char *inFilePath, AudioFileID &outAFID)
- {
-
- CFURLRef theURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8*)inFilePath, strlen(inFilePath), false);
- if (theURL == NULL)
- return kSoundEngineErrFileNotFound;
- #if TARGET_OS_IPHONE
- OSStatus result = AudioFileOpenURL(theURL, kAudioFileReadPermission, 0, &outAFID);
- #else
- OSStatus result = AudioFileOpenURL(theURL, fsRdPerm, 0, &outAFID);
- #endif
- CFRelease(theURL);
- AssertNoError("Error opening file", end);
- end:
- return result;
- }
- OSStatus LoadFileDataInfo(const char *inFilePath, AudioFileID &outAFID, AudioStreamBasicDescription &outFormat, UInt64 &outDataSize)
- {
- UInt32 thePropSize = sizeof(outFormat);
- OSStatus result = OpenFile(inFilePath, outAFID);
- AssertNoError("Error opening file", end);
- result = AudioFileGetProperty(outAFID, kAudioFilePropertyDataFormat, &thePropSize, &outFormat);
- AssertNoError("Error getting file format", end);
-
- thePropSize = sizeof(UInt64);
- result = AudioFileGetProperty(outAFID, kAudioFilePropertyAudioDataByteCount, &thePropSize, &outDataSize);
- AssertNoError("Error getting file data size", end);
- end:
- return result;
- }
- void CalculateBytesForTime (AudioStreamBasicDescription & inDesc, UInt32 inMaxPacketSize, Float64 inSeconds, UInt32 *outBufferSize, UInt32 *outNumPackets)
- {
- static const UInt32 maxBufferSize = 0x10000; // limit size to 64K
- static const UInt32 minBufferSize = 0x4000; // limit size to 16K
- if (inDesc.mFramesPerPacket) {
- Float64 numPacketsForTime = inDesc.mSampleRate / inDesc.mFramesPerPacket * inSeconds;
- *outBufferSize = (long unsigned int)numPacketsForTime * inMaxPacketSize;
- } else {
- // if frames per packet is zero, then the codec has no predictable packet == time
- // so we can't tailor this (we don't know how many Packets represent a time period
- // we'll just return a default buffer size
- *outBufferSize = maxBufferSize > inMaxPacketSize ? maxBufferSize : inMaxPacketSize;
- }
-
- // we're going to limit our size to our default
- if (*outBufferSize > maxBufferSize && *outBufferSize > inMaxPacketSize)
- *outBufferSize = maxBufferSize;
- else {
- // also make sure we're not too small - we don't want to go the disk for too small chunks
- if (*outBufferSize < minBufferSize)
- *outBufferSize = minBufferSize;
- }
- *outNumPackets = *outBufferSize / inMaxPacketSize;
- }
- static Boolean MatchFormatFlags(const AudioStreamBasicDescription& x, const AudioStreamBasicDescription& y)
- {
- UInt32 xFlags = x.mFormatFlags;
- UInt32 yFlags = y.mFormatFlags;
-
- // match wildcards
- if (x.mFormatID == 0 || y.mFormatID == 0 || xFlags == 0 || yFlags == 0)
- return true;
-
- if (x.mFormatID == kAudioFormatLinearPCM)
- {
- // knock off the all clear flag
- xFlags = xFlags & ~kAudioFormatFlagsAreAllClear;
- yFlags = yFlags & ~kAudioFormatFlagsAreAllClear;
-
- // if both kAudioFormatFlagIsPacked bits are set, then we don't care about the kAudioFormatFlagIsAlignedHigh bit.
- if (xFlags & yFlags & kAudioFormatFlagIsPacked) {
- xFlags = xFlags & ~kAudioFormatFlagIsAlignedHigh;
- yFlags = yFlags & ~kAudioFormatFlagIsAlignedHigh;
- }
-
- // if both kAudioFormatFlagIsFloat bits are set, then we don't care about the kAudioFormatFlagIsSignedInteger bit.
- if (xFlags & yFlags & kAudioFormatFlagIsFloat) {
- xFlags = xFlags & ~kAudioFormatFlagIsSignedInteger;
- yFlags = yFlags & ~kAudioFormatFlagIsSignedInteger;
- }
-
- // if the bit depth is 8 bits or less and the format is packed, we don't care about endianness
- if((x.mBitsPerChannel <= 8) && ((xFlags & kAudioFormatFlagIsPacked) == kAudioFormatFlagIsPacked))
- {
- xFlags = xFlags & ~kAudioFormatFlagIsBigEndian;
- }
- if((y.mBitsPerChannel <= 8) && ((yFlags & kAudioFormatFlagIsPacked) == kAudioFormatFlagIsPacked))
- {
- yFlags = yFlags & ~kAudioFormatFlagIsBigEndian;
- }
-
- // if the number of channels is 0 or 1, we don't care about non-interleavedness
- if (x.mChannelsPerFrame <= 1 && y.mChannelsPerFrame <= 1) {
- xFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
- yFlags &= ~kLinearPCMFormatFlagIsNonInterleaved;
- }
- }
- return xFlags == yFlags;
- }
- Boolean FormatIsEqual(AudioStreamBasicDescription x, AudioStreamBasicDescription y)
- {
- // the semantics for equality are:
- // 1) Values must match exactly
- // 2) wildcard's are ignored in the comparison
-
- #define MATCH(name) ((x.name) == 0 || (y.name) == 0 || (x.name) == (y.name))
-
- return
- ((x.mSampleRate==0.) || (y.mSampleRate==0.) || (x.mSampleRate==y.mSampleRate))
- && MATCH(mFormatID)
- && MatchFormatFlags(x, y)
- && MATCH(mBytesPerPacket)
- && MATCH(mFramesPerPacket)
- && MATCH(mBytesPerFrame)
- && MATCH(mChannelsPerFrame)
- && MATCH(mBitsPerChannel) ;
- }
- #pragma mark ***** BackgroundTrackMgr *****
- //==================================================================================================
- // BackgroundTrackMgr class
- //==================================================================================================
- class BackgroundTrackMgr
- {
- #define CurFileInfo THIS->mBGFileInfo[THIS->mCurrentFileIndex]
- public:
- typedef struct BG_FileInfo {
- std::string mFilePath;
- AudioFileID mAFID;
- AudioStreamBasicDescription mFileFormat;
- UInt64 mFileDataSize;
- //UInt64 mFileNumPackets; // this is only used if loading file to memory
- Boolean mLoadAtOnce;
- Boolean mFileDataInQueue;
- } BackgroundMusicFileInfo;
-
- BackgroundTrackMgr();
- ~BackgroundTrackMgr();
-
- void Teardown();
- void ClearFileInfo();
-
- AudioStreamPacketDescription *GetPacketDescsPtr();
-
- UInt32 GetNumPacketsToRead(BackgroundTrackMgr::BG_FileInfo *inFileInfo);
-
- static OSStatus AttachNewCookie(AudioQueueRef inQueue, BackgroundTrackMgr::BG_FileInfo *inFileInfo);
- static void QueueStoppedProc( void * inUserData, AudioQueueRef inAQ, AudioQueuePropertyID inID );
- static Boolean DisposeBuffer(AudioQueueRef inAQ, std::vector<AudioQueueBufferRef> inDisposeBufferList, AudioQueueBufferRef inBufferToDispose);
-
- enum {
- kQueueState_DoNothing = 0,
- kQueueState_ResizeBuffer = 1,
- kQueueState_NeedNewCookie = 2,
- kQueueState_NeedNewBuffers = 3,
- kQueueState_NeedNewQueue = 4,
- };
-
- static SInt8 GetQueueStateForNextBuffer(BackgroundTrackMgr::BG_FileInfo *inFileInfo, BackgroundTrackMgr::BG_FileInfo *inNextFileInfo);
- static void QueueCallback( void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inCompleteAQBuffer);
-
- OSStatus SetupQueue(BG_FileInfo *inFileInfo);
- OSStatus SetupBuffers(BG_FileInfo *inFileInfo);
- OSStatus LoadTrack(const char* inFilePath, Boolean inAddToQueue, Boolean inLoadAtOnce);
-
- OSStatus UpdateGain();
- OSStatus SetVolume(Float32 inVolume);
- Float32 GetVolume() const;
-
- OSStatus Start();
- OSStatus Stop(Boolean inStopAtEnd);
-
- private:
- AudioQueueRef mQueue;
- AudioQueueBufferRef mBuffers[kNumberBuffers];
- UInt32 mBufferByteSize;
- SInt64 mCurrentPacket;
- UInt32 mNumPacketsToRead;
- Float32 mVolume;
- AudioStreamPacketDescription * mPacketDescs;
- std::vector<BG_FileInfo*> mBGFileInfo;
- UInt32 mCurrentFileIndex;
- Boolean mMakeNewQueueWhenStopped;
- Boolean mStopAtEnd;
- std::vector<AudioQueueBufferRef> mBuffersToDispose;
- };
- BackgroundTrackMgr::BackgroundTrackMgr()
- : mQueue(0),
- mBufferByteSize(0),
- mCurrentPacket(0),
- mNumPacketsToRead(0),
- mVolume(1.0f),
- mPacketDescs(NULL),
- mCurrentFileIndex(0),
- mMakeNewQueueWhenStopped(false),
- mStopAtEnd(false)
- { }
-
- BackgroundTrackMgr::~BackgroundTrackMgr() {
- Teardown();
- }
-
- void BackgroundTrackMgr::Teardown() {
- if (mQueue) {
- AudioQueueDispose(mQueue, true);
- }
- for (UInt32 i=0; i < mBGFileInfo.size(); i++) {
- if (mBGFileInfo[i]->mAFID) {
- AudioFileClose(mBGFileInfo[i]->mAFID);
- }
- }
-
- if (mPacketDescs) {
- delete mPacketDescs;
- }
-
- ClearFileInfo();
- }
-
- void BackgroundTrackMgr::ClearFileInfo() {
- std::vector< BG_FileInfo* >::iterator itr = mBGFileInfo.begin();
- std::vector< BG_FileInfo* >::iterator endItr = mBGFileInfo.end();
- for( ; itr != endItr; ++itr ) {
- delete *itr;
- *itr = NULL;
- }
- mBGFileInfo.clear();
- }
-
- AudioStreamPacketDescription *BackgroundTrackMgr::GetPacketDescsPtr() {
- return mPacketDescs;
- }
-
- UInt32 BackgroundTrackMgr::GetNumPacketsToRead(BackgroundTrackMgr::BG_FileInfo *inFileInfo) {
- (void)inFileInfo;
- return mNumPacketsToRead;
- }
-
- OSStatus BackgroundTrackMgr::AttachNewCookie(AudioQueueRef inQueue, BackgroundTrackMgr::BG_FileInfo *inFileInfo) {
- OSStatus result = noErr;
- UInt32 size = sizeof(UInt32);
- result = AudioFileGetPropertyInfo (inFileInfo->mAFID, kAudioFilePropertyMagicCookieData, &size, NULL);
- if (!result && size) {
- char* cookie = new char [size];
- result = AudioFileGetProperty (inFileInfo->mAFID, kAudioFilePropertyMagicCookieData, &size, cookie);
- AssertNoError("Error getting cookie data", end);
- result = AudioQueueSetProperty(inQueue, kAudioQueueProperty_MagicCookie, cookie, size);
- delete [] cookie;
- AssertNoError("Error setting cookie data for queue", end);
- }
- return noErr;
-
- end:
- return noErr;
- }
-
- void BackgroundTrackMgr::QueueStoppedProc( void * inUserData, AudioQueueRef inAQ, AudioQueuePropertyID inID ) {
- (void)inID;
- UInt32 isRunning;
- UInt32 propSize = sizeof(isRunning);
-
- BackgroundTrackMgr *THIS = (BackgroundTrackMgr*)inUserData;
- OSStatus result = AudioQueueGetProperty(inAQ, kAudioQueueProperty_IsRunning, &isRunning, &propSize);
-
- if ((!isRunning) && (THIS->mMakeNewQueueWhenStopped)) {
- result = AudioQueueDispose(inAQ, true);
- AssertNoError("Error disposing queue", end);
- result = THIS->SetupQueue(CurFileInfo);
- AssertNoError("Error setting up new queue", end);
- result = THIS->SetupBuffers(CurFileInfo);
- AssertNoError("Error setting up new queue buffers", end);
- result = THIS->Start();
- AssertNoError("Error starting queue", end);
- }
- end:
- return;
- }
-
- Boolean BackgroundTrackMgr::DisposeBuffer(AudioQueueRef inAQ, std::vector<AudioQueueBufferRef> inDisposeBufferList, AudioQueueBufferRef inBufferToDispose) {
- for (unsigned int i=0; i < inDisposeBufferList.size(); i++) {
- if (inBufferToDispose == inDisposeBufferList[i]) {
- OSStatus result = AudioQueueFreeBuffer(inAQ, inBufferToDispose);
- if (result == noErr) {
- inDisposeBufferList.pop_back();
- }
- return true;
- }
- }
- return false;
- }
-
- SInt8 BackgroundTrackMgr::GetQueueStateForNextBuffer(BackgroundTrackMgr::BG_FileInfo *inFileInfo, BackgroundTrackMgr::BG_FileInfo *inNextFileInfo) {
- inFileInfo->mFileDataInQueue = false;
-
- // unless the data formats are the same, we need a new queue
- if (!FormatIsEqual(inFileInfo->mFileFormat, inNextFileInfo->mFileFormat)) {
- return kQueueState_NeedNewQueue;
- }
-
- // if going from a load-at-once file to streaming or vice versa, we need new buffers
- if (inFileInfo->mLoadAtOnce != inNextFileInfo->mLoadAtOnce) {
- return kQueueState_NeedNewBuffers;
- }
-
- // if the next file is smaller than the current, we just need to resize
- if (inNextFileInfo->mLoadAtOnce) {
- return (inFileInfo->mFileDataSize >= inNextFileInfo->mFileDataSize) ? kQueueState_ResizeBuffer : kQueueState_NeedNewBuffers;
- }
-
- return kQueueState_NeedNewCookie;
- }
-
- void BackgroundTrackMgr::QueueCallback( void * inUserData, AudioQueueRef inAQ, AudioQueueBufferRef inCompleteAQBuffer ) {
- // dispose of the buffer if no longer in use
- OSStatus result = noErr;
- BackgroundTrackMgr *THIS = (BackgroundTrackMgr*)inUserData;
- if (DisposeBuffer(inAQ, THIS->mBuffersToDispose, inCompleteAQBuffer)) {
- return;
- }
- UInt32 nPackets = 0;
- // loop the current buffer if the following:
- // 1. file was loaded into the buffer previously
- // 2. only one file in the queue
- // 3. we have not been told to stop at playlist completion
- if ((CurFileInfo->mFileDataInQueue) && (THIS->mBGFileInfo.size() == 1) && (!THIS->mStopAtEnd)) {
- nPackets = THIS->GetNumPacketsToRead(CurFileInfo);
- } else {
- UInt32 numBytes;
- while (nPackets == 0) {
- // if loadAtOnce, get all packets in the file, otherwise ~.5 seconds of data
- nPackets = THIS->GetNumPacketsToRead(CurFileInfo);
- result = AudioFileReadPackets(CurFileInfo->mAFID, false, &numBytes, THIS->mPacketDescs, THIS->mCurrentPacket, &nPackets,
- inCompleteAQBuffer->mAudioData);
- AssertNoError("Error reading file data", end);
-
- inCompleteAQBuffer->mAudioDataByteSize = numBytes;
-
- if (nPackets == 0) { // no packets were read, this file has ended.
- if (CurFileInfo->mLoadAtOnce) {
- CurFileInfo->mFileDataInQueue = true;
- }
-
- THIS->mCurrentPacket = 0;
- UInt32 theNextFileIndex = (THIS->mCurrentFileIndex < THIS->mBGFileInfo.size()-1) ? THIS->mCurrentFileIndex+1 : 0;
-
- // we have gone through the playlist. if mStopAtEnd, stop the queue here
- if (theNextFileIndex == 0 && THIS->mStopAtEnd) {
- result = AudioQueueStop(inAQ, false);
- AssertNoError("Error stopping queue", end);
- return;
- }
-
- SInt8 theQueueState = GetQueueStateForNextBuffer(CurFileInfo, THIS->mBGFileInfo[theNextFileIndex]);
- if (theNextFileIndex != THIS->mCurrentFileIndex) {
- // if were are not looping the same file. Close the old one and open the new
- result = AudioFileClose(CurFileInfo->mAFID);
- AssertNoError("Error closing file", end);
- THIS->mCurrentFileIndex = theNextFileIndex;
-
- result = LoadFileDataInfo(CurFileInfo->mFilePath.c_str(), CurFileInfo->mAFID, CurFileInfo->mFileFormat, CurFileInfo->mFileDataSize);
- AssertNoError("Error opening file", end);
- }
-
- switch (theQueueState) {
- // if we need to resize the buffer, set the buffer's audio data size to the new file's size
- // we will also need to get the new file cookie
- case kQueueState_ResizeBuffer:
- inCompleteAQBuffer->mAudioDataByteSize = (UInt32)CurFileInfo->mFileDataSize;
- // if the data format is the same but we just need a new cookie, attach a new cookie
- case kQueueState_NeedNewCookie:
- result = AttachNewCookie(inAQ, CurFileInfo);
- AssertNoError("Error attaching new file cookie data to queue", end);
- break;
-
- // we can keep the same queue, but not the same buffer(s)
- case kQueueState_NeedNewBuffers:
- THIS->mBuffersToDispose.push_back(inCompleteAQBuffer);
- THIS->SetupBuffers(CurFileInfo);
- break;
-
- // if the data formats are not the same, we need to dispose the current queue and create a new one
- case kQueueState_NeedNewQueue:
- THIS->mMakeNewQueueWhenStopped = true;
- result = AudioQueueStop(inAQ, false);
- AssertNoError("Error stopping queue", end);
- return;
-
- default:
- break;
- }
- }
- }
- }
-
- result = AudioQueueEnqueueBuffer(inAQ, inCompleteAQBuffer, (THIS->mPacketDescs ? nPackets : 0), THIS->mPacketDescs);
- if(result != noErr) {
- result = AudioQueueFreeBuffer(inAQ, inCompleteAQBuffer);
- AssertNoError("Error freeing buffers that didn't enqueue", end);
- }
- AssertNoError("Error enqueuing new buffer", end);
- if (CurFileInfo->mLoadAtOnce) {
- CurFileInfo->mFileDataInQueue = true;
- }
-
- THIS->mCurrentPacket += nPackets;
-
- end:
- return;
- }
-
- OSStatus BackgroundTrackMgr::SetupQueue(BG_FileInfo *inFileInfo) {
- UInt32 size = 0;
- OSStatus result = AudioQueueNewOutput(&inFileInfo->mFileFormat, QueueCallback, this, CFRunLoopGetCurrent(), kCFRunLoopCommonModes, 0, &mQueue);
- AssertNoError("Error creating queue", end);
-
- // (2) If the file has a cookie, we should get it and set it on the AQ
- size = sizeof(UInt32);
- result = AudioFileGetPropertyInfo (inFileInfo->mAFID, kAudioFilePropertyMagicCookieData, &size, NULL);
-
- if (!result && size) {
- char* cookie = new char [size];
- result = AudioFileGetProperty (inFileInfo->mAFID, kAudioFilePropertyMagicCookieData, &size, cookie);
- AssertNoError("Error getting magic cookie", end);
- result = AudioQueueSetProperty(mQueue, kAudioQueueProperty_MagicCookie, cookie, size);
- delete [] cookie;
- AssertNoError("Error setting magic cookie", end);
- }
-
- // channel layout
- OSStatus err = AudioFileGetPropertyInfo(inFileInfo->mAFID, kAudioFilePropertyChannelLayout, &size, NULL);
- if (err == noErr && size > 0) {
- AudioChannelLayout *acl = (AudioChannelLayout *)malloc(size);
- result = AudioFileGetProperty(inFileInfo->mAFID, kAudioFilePropertyChannelLayout, &size, acl);
- AssertNoError("Error getting channel layout from file", end);
- result = AudioQueueSetProperty(mQueue, kAudioQueueProperty_ChannelLayout, acl, size);
- free(acl);
- AssertNoError("Error setting channel layout on queue", end);
- }
-
- // add a notification proc for when the queue stops
- result = AudioQueueAddPropertyListener(mQueue, kAudioQueueProperty_IsRunning, QueueStoppedProc, this);
- AssertNoError("Error adding isRunning property listener to queue", end);
-
- // we need to reset this variable so that if the queue is stopped mid buffer we don't dispose it
- mMakeNewQueueWhenStopped = false;
-
- // volume
- result = SetVolume(mVolume);
-
- end:
- return result;
- }
-
- OSStatus BackgroundTrackMgr::SetupBuffers(BG_FileInfo *inFileInfo) {
- int numBuffersToQueue = kNumberBuffers;
- UInt32 maxPacketSize;
- UInt32 size = sizeof(maxPacketSize);
- // we need to calculate how many packets we read at a time, and how big a buffer we need
- // we base this on the size of the packets in the file and an approximate duration for each buffer
-
- // first check to see what the max size of a packet is - if it is bigger
- // than our allocation default size, that needs to become larger
- OSStatus result = AudioFileGetProperty(inFileInfo->mAFID, kAudioFilePropertyPacketSizeUpperBound, &size, &maxPacketSize);
- AssertNoError("Error getting packet upper bound size", end);
- bool isFormatVBR = (inFileInfo->mFileFormat.mBytesPerPacket == 0 || inFileInfo->mFileFormat.mFramesPerPacket == 0);
-
- CalculateBytesForTime(inFileInfo->mFileFormat, maxPacketSize, 0.5/*seconds*/, &mBufferByteSize, &mNumPacketsToRead);
-
- // if the file is smaller than the capacity of all the buffer queues, always load it at once
- if ((mBufferByteSize * numBuffersToQueue) > inFileInfo->mFileDataSize) {
- inFileInfo->mLoadAtOnce = true;
- }
-
- if (inFileInfo->mLoadAtOnce) {
- UInt64 theFileNumPackets;
- size = sizeof(UInt64);
- result = AudioFileGetProperty(inFileInfo->mAFID, kAudioFilePropertyAudioDataPacketCount, &size, &theFileNumPackets);
- AssertNoError("Error getting packet count for file", end);
-
- mNumPacketsToRead = (UInt32)theFileNumPackets;
- mBufferByteSize = (UInt32)inFileInfo->mFileDataSize;
- numBuffersToQueue = 1;
- } else {
- mNumPacketsToRead = mBufferByteSize / maxPacketSize;
- }
-
- if (isFormatVBR) {
- mPacketDescs = new AudioStreamPacketDescription [mNumPacketsToRead];
- } else {
- mPacketDescs = NULL; // we don't provide packet descriptions for constant bit rate formats (like linear PCM)
- }
-
- // allocate the queue's buffers
- for (int i = 0; i < numBuffersToQueue; ++i) {
- result = AudioQueueAllocateBuffer(mQueue, mBufferByteSize, &mBuffers[i]);
- AssertNoError("Error allocating buffer for queue", end);
- QueueCallback (this, mQueue, mBuffers[i]);
- if (inFileInfo->mLoadAtOnce) {
- inFileInfo->mFileDataInQueue = true;
- }
- }
-
- end:
- return result;
- }
-
- OSStatus BackgroundTrackMgr::LoadTrack(const char* inFilePath, Boolean inAddToQueue, Boolean inLoadAtOnce) {
- BG_FileInfo *fileInfo = new BG_FileInfo;
- fileInfo->mFilePath = inFilePath;
- OSStatus result = LoadFileDataInfo(fileInfo->mFilePath.c_str(), fileInfo->mAFID, fileInfo->mFileFormat, fileInfo->mFileDataSize);
- AssertNoError("Error getting file data info", fail);
- fileInfo->mLoadAtOnce = inLoadAtOnce;
- fileInfo->mFileDataInQueue = false;
- // if not adding to the queue, clear the current file vector
- if (!inAddToQueue) {
- ClearFileInfo();
- }
-
- mBGFileInfo.push_back(fileInfo);
-
- // setup the queue if this is the first (or only) file
- if (mBGFileInfo.size() == 1) {
- result = SetupQueue(fileInfo);
- AssertNoError("Error setting up queue", fail);
- result = SetupBuffers(fileInfo);
- AssertNoError("Error setting up queue buffers", fail);
- } else { // if this is just part of the playlist, close the file for now
- result = AudioFileClose(fileInfo->mAFID);
- AssertNoError("Error closing file", fail);
- }
- return result;
-
- fail:
- if (fileInfo) {
- delete fileInfo;
- }
- return result;
- }
-
- OSStatus BackgroundTrackMgr::UpdateGain() {
- return SetVolume(mVolume);
- }
-
- OSStatus BackgroundTrackMgr::SetVolume(Float32 inVolume) {
- mVolume = inVolume;
- return AudioQueueSetParameter(mQueue, kAudioQueueParam_Volume, mVolume * gMasterVolumeGain);
- }
-
- Float32 BackgroundTrackMgr::GetVolume() const {
- return mVolume;
- }
-
- OSStatus BackgroundTrackMgr::Start() {
- if(gInterrupted) {
- printf("Start called, but interrupted so ignoring.\n");
- return noErr;
- }
-
- OSStatus result = AudioQueuePrime(mQueue, 1, NULL);
- if (result) {
- printf("Error priming queue: %d\n", (int)result);
- return result;
- }
- return AudioQueueStart(mQueue, NULL);
- }
-
- OSStatus BackgroundTrackMgr::Stop(Boolean inStopAtEnd) {
- if (inStopAtEnd) {
- mStopAtEnd = true;
- return noErr;
- } else {
- return AudioQueueStop(mQueue, true);
- }
- }
-
- #pragma mark ***** SoundEngineEffect *****
- //==================================================================================================
- // SoundEngineEffect class
- //==================================================================================================
- class SoundEngineEffect
- {
- public:
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- SoundEngineEffect(const char* inLoopPath, const char* inAttackPath, const char* inDecayPath, Boolean inDoLoop)
- : mSourceID(0),
- mAttackBufferID(0),
- mLoopBufferID(0),
- mDecayBufferID(0),
- mLoopPath(inLoopPath),
- mAttackPath(inAttackPath),
- mDecayPath(inDecayPath),
- mLoopData(NULL),
- mAttackData(NULL),
- mDecayData(NULL),
- mLoopDataSize(0),
- mAttackDataSize(0),
- mDecayDataSize(0),
- mIsLoopingEffect(inDoLoop),
- mPlayThread(NULL),
- mPlayThreadState(kPlayThreadState_Loop) { alGenSources(1, &mSourceID); }
-
- ~SoundEngineEffect()
- {
- alDeleteSources(1, &mSourceID);
-
- if (mLoopData)
- free(mLoopData);
- if (mAttackData)
- free(mAttackData);
- if (mDecayData)
- free(mDecayData);
- }
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Accessors
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- UInt32 GetEffectID() { return mSourceID; }
- UInt32 GetPlayThreadState() { return mPlayThreadState; }
- Boolean HasAttackBuffer() { return mAttackBufferID != 0; }
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Helper Functions
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- ALenum GetALFormat(AudioStreamBasicDescription inFileFormat)
- {
- if (inFileFormat.mFormatID != kAudioFormatLinearPCM)
- return kSoundEngineErrInvalidFileFormat;
-
- if ((inFileFormat.mChannelsPerFrame > 2) || (inFileFormat.mChannelsPerFrame < 1))
- return kSoundEngineErrInvalidFileFormat;
-
- if(inFileFormat.mBitsPerChannel == 8)
- return (inFileFormat.mChannelsPerFrame == 1) ? AL_FORMAT_MONO8 : AL_FORMAT_STEREO8;
- else if(inFileFormat.mBitsPerChannel == 16)
- return (inFileFormat.mChannelsPerFrame == 1) ? AL_FORMAT_MONO16 : AL_FORMAT_STEREO16;
- return kSoundEngineErrInvalidFileFormat;
- }
- OSStatus LoadFileData(const char *inFilePath, void* &outData, UInt32 &outDataSize, ALuint &outBufferID)
- {
- AudioFileID theAFID = 0;
- OSStatus result = noErr;
- UInt64 theFileSize = 0;
- AudioStreamBasicDescription theFileFormat;
-
- result = LoadFileDataInfo(inFilePath, theAFID, theFileFormat, theFileSize);
- outDataSize = (UInt32)theFileSize;
- AssertNoError("Error loading file info", fail)
- outData = malloc(outDataSize);
- result = AudioFileReadBytes(theAFID, false, 0, &outDataSize, outData);
- AssertNoError("Error reading file data", fail)
-
- if (!TestAudioFormatNativeEndian(theFileFormat) && (theFileFormat.mBitsPerChannel > 8))
- return kSoundEngineErrInvalidFileFormat;
-
- alGenBuffers(1, &outBufferID);
- AssertNoOALError("Error generating buffer\n", fail);
-
- alBufferDataStaticProc(outBufferID, GetALFormat(theFileFormat), outData, outDataSize, (ALsizei)theFileFormat.mSampleRate);
- AssertNoOALError("Error attaching data to buffer\n", fail);
- AudioFileClose(theAFID);
- return result;
-
- fail:
- if (theAFID)
- AudioFileClose(theAFID);
- if (outData)
- {
- free(outData);
- outData = NULL;
- }
- return result;
- }
-
- OSStatus AttachFilesToSource()
- {
- OSStatus result = AL_NO_ERROR;
- // first check for the attack file. That will be first in the queue if present
- if (mAttackPath)
- {
- result = LoadFileData(mAttackPath, mAttackData, mAttackDataSize, mAttackBufferID);
- AssertNoError("Error loading attack file info", end)
- }
-
- result = LoadFileData(mLoopPath, mLoopData, mLoopDataSize, mLoopBufferID);
- AssertNoError("Error loading looping file info", end)
-
- // if one-shot effect, attach the buffer to the source now
- if (!mIsLoopingEffect)
- {
- alSourcei(mSourceID, AL_BUFFER, mLoopBufferID);
- AssertNoOALError("Error attaching file data to effect", end)
- }
- if (mDecayPath)
- {
- result = LoadFileData(mDecayPath, mDecayData, mDecayDataSize, mDecayBufferID);
- AssertNoError("Error loading decay file info", end)
- }
- end:
- return result;
- }
-
- OSStatus ClearSourceBuffers()
- {
- OSStatus result = AL_NO_ERROR;
- ALint numQueuedBuffers = 0;
- ALuint *bufferIDs = (ALuint*)malloc(numQueuedBuffers * sizeof(ALint));
- alGetSourcei(mSourceID, AL_BUFFERS_QUEUED, &numQueuedBuffers);
- AssertNoOALError("Error getting OpenAL queued buffer size", end)
-
- alSourceUnqueueBuffers(mSourceID, numQueuedBuffers, bufferIDs);
- AssertNoOALError("Error unqueueing buffers from source", end)
-
- end:
- free(bufferIDs);
- return result;
- }
- static void* PlaybackProc(void *args)
- {
- OSStatus result = AL_NO_ERROR;
- SoundEngineEffect *THIS = (SoundEngineEffect*)args;
-
- alSourcePlay(THIS->GetEffectID());
- AssertNoOALError("Error starting effect playback", end)
-
- // if attack buffer is present, wait until it has completed, then turn looping on
- if (THIS->HasAttackBuffer())
- {
- ALint numBuffersProcessed = 0;
- while (numBuffersProcessed < 1)
- {
- alGetSourcei(THIS->GetEffectID(), AL_BUFFERS_PROCESSED, &numBuffersProcessed);
- AssertNoOALError("Error getting processed buffer number", end)
- }
-
- ALuint tmpBuffer = 0;
- alSourceUnqueueBuffers(THIS->GetEffectID(), 1, &tmpBuffer);
- AssertNoOALError("Error unqueueing buffers from source", end)
- }
- // now that we have processed the attack buffer, loop the main one
- THIS->SetLooping(true);
- end:
- return NULL;
- }
-
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- // Effect management
- // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- OSStatus Start()
- {
- OSStatus result = AL_NO_ERROR;
- alSourceStop(mSourceID);
- AssertNoOALError("Error stopping source", end)
- if (!mIsLoopingEffect)
- {
- // if we are just playing one-short effects, start playback here
- alSourcePlay(mSourceID);
- return alGetError();
- }
- // for loops we need to spawn a new thread
- mPlayThread = new OpenALThread(PlaybackProc, (void*)this);
- // we want this to delete upon thread completion
- mPlayThreadState = kPlayThreadState_Loop;
- // clean up remnants from any previous playback of the source
- result = ClearSourceBuffers();
- AssertNoError("Error clearing buffers", end)
-
- // if the effect has an attack sample, queue this first
- if (HasAttackBuffer())
- {
- alSourceQueueBuffers(mSourceID, 1, &mAttackBufferID);
- AssertNoOALError("Error queueing buffers for attack", end)
- // turn on looping after the attack buffer has been processed
- SetLooping(false);
- }
- alSourceQueueBuffers(mSourceID, 1, &mLoopBufferID);
- AssertNoOALError("Error queueing looping buffer", end)
- mPlayThread->Start();
- end:
- return result;
- }
-
- OSStatus StartDecay()
- {
- // turn off looping, and queue the decay buffer
- OSStatus result = AL_NO_ERROR;
- alSourcei(mSourceID, AL_LOOPING, 0);
- AssertNoOALError("Error turning off looping", end)
- alSourceQueueBuffers(mSourceID, 1, &mDecayBufferID);
- AssertNoOALError("Error queueing decay file", end)
- end:
- return result;
- }
-
- OSStatus Stop(Boolean inDoDecay)
- {
- OSStatus result = AL_NO_ERROR;
- // for non looped effects and loops with no decay sample
- if ((mDecayBufferID == 0) || !inDoDecay)
- {
- // if no decay to play, just stop the source
- alSourceStop(mSourceID);
- AssertNoOALError("Error stopping source", end)
- }
- else
- return StartDecay();
- end:
- return result;
- }
- OSStatus SetPitch(Float32 inValue)
- {
- alSourcef(mSourceID, AL_PITCH, inValue);
- return alGetError();
- }
-
- OSStatus SetLooping(Boolean inDoLoop)
- {
- ALint doLoop = inDoLoop ? 1 : 0;
- alSourcei(mSourceID, AL_LOOPING, doLoop);
- return alGetError();
- }
-
- OSStatus SetPosition(Float32 inX, Float32 inY, Float32 inZ)
- {
- alSource3f(mSourceID, AL_POSITION, inX, inY, inZ);
- return alGetError();
- }
- OSStatus SetMaxDistance(Float32 inValue)
- {
- alSourcef(mSourceID, AL_MAX_DISTANCE, inValue);
- return alGetError();
- }
- OSStatus SetReferenceDistance(Float32 inValue)
- {
- alSourcef(mSourceID, AL_REFERENCE_DISTANCE, inValue);
- return alGetError();
- }
-
- OSStatus SetLevel(Float32 inValue)
- {
- alSourcef(mSourceID, AL_GAIN, inValue * gMasterVolumeGain);
- return alGetError();
- }
-
- enum {
- kPlayThreadState_Loop = 0,
- kPlayThreadState_Decay = 1,
- kPlayThreadState_End = 2
- };
-
- private:
- ALuint mSourceID;
- ALuint mAttackBufferID;
- ALuint mLoopBufferID;
- ALuint mDecayBufferID;
- UInt32 mNumberBuffers;
- const char* mLoopPath;
- const char* mAttackPath;
- const char* mDecayPath;
- void* mLoopData;
- void* mAttackData;
- void* mDecayData;
- UInt32 mLoopDataSize;
- UInt32 mAttackDataSize;
- UInt32 mDecayDataSize;
- Boolean mIsLoopingEffect;
- OpenALThread* mPlayThread;
- UInt32 mPlayThreadState;
- };
- #pragma mark ***** SoundEngineEffectMap *****
- //==================================================================================================
- // SoundEngineEffectMap class
- //==================================================================================================
- class SoundEngineEffectMap
- : std::multimap<UInt32, SoundEngineEffect*, std::less<ALuint> >
- {
- public:
- // add a new context to the map
- void Add (const ALuint inEffectToken, SoundEngineEffect **inEffect)
- {
- iterator it = upper_bound(inEffectToken);
- insert(it, value_type (inEffectToken, *inEffect));
- }
- SoundEngineEffect* Get(ALuint inEffectToken)
- {
- iterator it = find(inEffectToken);
- if (it != end())
- return ((*it).second);
- return (NULL);
- }
- void Remove (const ALuint inSourceToken) {
- iterator it = find(inSourceToken);
- if (it != end())
- erase(it);
- }
-
- SoundEngineEffect* GetEffectByIndex(UInt32 inIndex) {
- iterator it = begin();
- for (UInt32 i = 0; i < inIndex; i++) {
- if (it != end())
- ++it;
- else
- i = inIndex;
- }
-
- if (it != end())
- return ((*it).second);
- return (NULL);
- }
- iterator GetIterator() { return begin(); }
-
- UInt32 Size () const { return size(); }
- bool Empty () const { return empty(); }
- };
- #pragma mark ***** OpenALObject *****
- //==================================================================================================
- // OpenALObject class
- //==================================================================================================
- class OpenALObject
- {
- public:
- OpenALObject(Float32 inMixerOutputRate)
- : mOutputRate(inMixerOutputRate),
- mGain(1.0f),
- mContext(NULL),
- mDevice(NULL),
- mEffectsMap(NULL)
- {
- mEffectsMap = new SoundEngineEffectMap();
- }
-
- ~OpenALObject() { Teardown(); }
- OSStatus Initialize()
- {
- OSStatus result = noErr;
- mDevice = alcOpenDevice(NULL);
- AssertNoOALError("Error opening output device", end)
- if(mDevice == NULL) { return kSoundEngineErrDeviceNotFound; }
-
- // if a mixer output rate was specified, set it here
- // must be done before the alcCreateContext() call
- if (mOutputRate)
- alcMacOSXMixerOutputRateProc(mOutputRate);
-
- // Create an OpenAL Context
- mContext = alcCreateContext(mDevice, NULL);
- AssertNoOALError("Error creating OpenAL context", end)
-
- alcMakeContextCurrent(mContext);
- AssertNoOALError("Error setting current OpenAL context", end)
-
- end:
- return result;
- }
-
- void Teardown()
- {
- if (mEffectsMap)
- {
- for (UInt32 i = 0; i < mEffectsMap->Size(); i++)
- {
- SoundEngineEffect *theEffect = mEffectsMap->GetEffectByIndex(0);
- if (theEffect)
- {
- mEffectsMap->Remove(theEffect->GetEffectID());
- delete theEffect;
- }
- }
- delete mEffectsMap;
- }
-
- if (mContext) alcDestroyContext(mContext);
-
- if (mDevice) alcCloseDevice(mDevice);
- }
- OSStatus SetListenerPosition(Float32 inX, Float32 inY, Float32 inZ)
- {
- alListener3f(AL_POSITION, inX, inY, inZ);
- return alGetError();
- }
-
- OSStatus SetListenerDirection(Float32 inX, Float32 inY, Float32 inZ)
- {
- alListener3f(AL_DIRECTION, inX, inY, inZ);
- return alGetError();
- }
- OSStatus SetListenerGain(Float32 inValue)
- {
- alListenerf(AL_GAIN, inValue);
- return alGetError();
- }
-
- OSStatus SetMaxDistance(Float32 inValue)
- {
- OSStatus result = 0;
- for (UInt32 i=0; i < mEffectsMap->Size(); i++)
- {
- SoundEngineEffect *theEffect = mEffectsMap->GetEffectByIndex(i);
- if ((result = theEffect->SetMaxDistance(inValue)) != AL_NO_ERROR)
- return result;
- }
- return result;
- }
- OSStatus SetReferenceDistance(Float32 inValue)
- {
- OSStatus result = 0;
- for (UInt32 i=0; i < mEffectsMap->Size(); i++)
- {
- SoundEngineEffect *theEffect = mEffectsMap->GetEffectByIndex(i);
- if ((result = theEffect->SetReferenceDistance(inValue)) != AL_NO_ERROR)
- return result;
- }
- return result;
- }
-
- OSStatus SetEffectReferenceDistance(UInt32 inEffectID, Float32 inValue)
- {
- SoundEngineEffect *theEffect = mEffectsMap->Get(inEffectID);
- return (theEffect) ? theEffect->SetReferenceDistance(inValue) : kSoundEngineErrInvalidID;
- }
- OSStatus SetEffectsVolume(Float32 inValue)
- {
- OSStatus result = 0;
- for (UInt32 i=0; i < mEffectsMap->Size(); i++)
- {
- SoundEngineEffect *theEffect = mEffectsMap->GetEffectByIndex(i);
- if ((result = theEffect->SetLevel(inValue)) != AL_NO_ERROR)
- return result;
- }
- mGain = inValue;
- return result;
- }
-
- Float32 GetEffectsVolume() const
- {
- return mGain;
- }
- OSStatus UpdateGain()
- {
- return SetEffectsVolume(mGain);
- }
-
- OSStatus LoadEffect(const char *inFilePath, UInt32 *outEffectID)
- {
- SoundEngineEffect *theEffect = new SoundEngineEffect(inFilePath, NULL, NULL, false);
- OSStatus result = theEffect->AttachFilesToSource();
- if (result == noErr)
- {
- *outEffectID = theEffect->GetEffectID();
- mEffectsMap->Add(*outEffectID, &theEffect);
- }
- return result;
- }
- OSStatus LoadLoopingEffect(const char *inLoopFilePath, const char *inAttackFilePath, const char *inDecayFilePath, UInt32 *outEffectID)
- {
- SoundEngineEffect *theEffect = new SoundEngineEffect(inLoopFilePath, inAttackFilePath, inDecayFilePath, true);
- OSStatus result = theEffect->AttachFilesToSource();
- if (result == noErr)
- {
- *outEffectID = theEffect->GetEffectID();
- mEffectsMap->Add(*outEffectID, &theEffect);
- }
- return result;
- }
-
- OSStatus UnloadEffect(UInt32 inEffectID)
- {
- SoundEngineEffect *theEffect = mEffectsMap->Get(inEffectID);
- if (theEffect)
- {
- mEffectsMap->Remove(inEffectID);
- delete theEffect;
- }
- return 0;
- }
- OSStatus StartEffect(UInt32 inEffectID)
- {
- SoundEngineEffect *theEffect = mEffectsMap->Get(inEffectID);
- return (theEffect) ? theEffect->Start() : kSoundEngineErrInvalidID;
- }
-
- OSStatus StopEffect(UInt32 inEffectID, Boolean inDoDecay)
- {
- SoundEngineEffect *theEffect = mEffectsMap->Get(inEffectID);
- return (theEffect) ? theEffect->Stop(inDoDecay) : kSoundEngineErrInvalidID;
- }
-
- OSStatus SetEffectPitch(UInt32 inEffectID, Float32 inValue)
- {
- SoundEngineEffect *theEffect = mEffectsMap->Get(inEffectID);
- return (theEffect) ? theEffect->SetPitch(inValue) : kSoundEngineErrInvalidID;
- }
- OSStatus SetEffectVolume(UInt32 inEffectID, Float32 inValue)
- {
- SoundEngineEffect *theEffect = mEffectsMap->Get(inEffectID);
- return (theEffect) ? theEffect->SetLevel(inValue * mGain) : kSoundEngineErrInvalidID;
- }
-
- OSStatus SetEffectPosition(UInt32 inEffectID, Float32 inX, Float32 inY, Float32 inZ)
- {
- SoundEngineEffect *theEffect = mEffectsMap->Get(inEffectID);
- return (theEffect) ? theEffect->SetPosition(inX, inY, inZ) : kSoundEngineErrInvalidID;
- }
-
- private:
- Float32 mOutputRate;
- Float32 mGain;
- ALCcontext* mContext;
- ALCdevice* mDevice;
- SoundEngineEffectMap* mEffectsMap;
- };
- #pragma mark ***** API *****
- //==================================================================================================
- // Sound Engine
- //==================================================================================================
- extern "C"
- static void interruptionCallback(void* arg, UInt32 interruptionState)
- {
- printf("Excuse this interruption...\n");
- switch(interruptionState)
- {
- case kAudioSessionBeginInterruption:
- printf("begin interruption\n");
- SoundEngine_Teardown();
- gInterrupted = true;
- break;
-
- // SCD: TODO Revisit when building with 2.2 where this callback is actually supposed to be made...
- // case kAudioSessionEndInterruption:
- // printf("end interruption.\n");
- // gInterrupted = false;
- // break;
- }
- }
- #endif // WIN32
- extern "C"
- OSStatus SoundEngine_Reactivate()
- {
- #ifndef WIN32
- gInterrupted = false;
- return noErr;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_Initialize(Float32 inMixerOutputRate)
- {
- #ifndef WIN32
- if(gInterrupted)
- return noErr;
- if( !isInitialized )
- {
- AudioSessionInitialize( NULL, NULL, interruptionCallback, NULL );
- // UInt32 sessionCategory = kAudioSessionCategory_AmbientSound;
- // AudioSessionSetProperty( kAudioSessionProperty_AudioCategory, sizeof( sessionCategory ), &sessionCategory );
- isInitialized = true;
- }
-
- if (sOpenALObject)
- delete sOpenALObject;
- if (sBackgroundTrackMgr)
- delete sBackgroundTrackMgr;
- sOpenALObject = new OpenALObject(inMixerOutputRate);
- sBackgroundTrackMgr = new BackgroundTrackMgr();
-
- return sOpenALObject->Initialize();
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_Teardown()
- {
- #ifndef WIN32
- if (sOpenALObject)
- {
- delete sOpenALObject;
- sOpenALObject = NULL;
- }
-
- if (sBackgroundTrackMgr)
- {
- delete sBackgroundTrackMgr;
- sBackgroundTrackMgr = NULL;
- }
-
- return 0;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_SetMasterVolume(Float32 inValue)
- {
- #ifndef WIN32
- OSStatus result = noErr;
- gMasterVolumeGain = inValue;
- if (sBackgroundTrackMgr)
- result = sBackgroundTrackMgr->UpdateGain();
-
- if (result) return result;
-
- if (sOpenALObject)
- return sOpenALObject->UpdateGain();
-
- return result;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_SetListenerPosition(Float32 inX, Float32 inY, Float32 inZ)
- {
- #ifndef WIN32
- return (sOpenALObject) ? sOpenALObject->SetListenerPosition(inX, inY, inZ) : kSoundEngineErrUnitialized;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_SetListenerDirection(Float32 inX, Float32 inY, Float32 inZ)
- {
- #ifndef WIN32
- return (sOpenALObject) ? sOpenALObject->SetListenerDirection(inX, inY, inZ) : kSoundEngineErrUnitialized;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_SetListenerGain(Float32 inValue)
- {
- #ifndef WIN32
- return (sOpenALObject) ? sOpenALObject->SetListenerGain(inValue) : kSoundEngineErrUnitialized;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_LoadBackgroundMusicTrack(const char* inPath, Boolean inAddToQueue, Boolean inLoadAtOnce)
- {
- #ifndef WIN32
- if(gInterrupted)
- return noErr;
- if (sBackgroundTrackMgr == NULL)
- sBackgroundTrackMgr = new BackgroundTrackMgr();
- return sBackgroundTrackMgr->LoadTrack(inPath, inAddToQueue, inLoadAtOnce);
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_UnloadBackgroundMusicTrack()
- {
- #ifndef WIN32
- if (sBackgroundTrackMgr)
- {
- delete sBackgroundTrackMgr;
- sBackgroundTrackMgr = NULL;
- }
-
- return 0;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_StartBackgroundMusic()
- {
- #ifndef WIN32
- return (sBackgroundTrackMgr) ? sBackgroundTrackMgr->Start() : kSoundEngineErrUnitialized;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_StopBackgroundMusic(Boolean stopAtEnd)
- {
- #ifndef WIN32
- return (sBackgroundTrackMgr) ? sBackgroundTrackMgr->Stop(stopAtEnd) : kSoundEngineErrUnitialized;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_SetBackgroundMusicVolume(Float32 inValue)
- {
- #ifndef WIN32
- return (sBackgroundTrackMgr) ? sBackgroundTrackMgr->SetVolume(inValue) : kSoundEngineErrUnitialized;
- #else
- return 0;
- #endif
- }
- extern "C"
- Float32 SoundEngine_GetBackgroundMusicVolume()
- {
- #ifndef WIN32
- return (sBackgroundTrackMgr) ? sBackgroundTrackMgr->GetVolume() : 0.0f;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_LoadEffect(const char* inPath, UInt32* outEffectID)
- {
- #ifndef WIN32
- OSStatus result = noErr;
- if (sOpenALObject == NULL)
- {
- sOpenALObject = new OpenALObject(0.0f);
- result = sOpenALObject->Initialize();
- }
- return (result) ? result : sOpenALObject->LoadEffect(inPath, outEffectID);
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_LoadLoopingEffect(const char* inLoopFilePath, const char* inAttackFilePath, const char* inDecayFilePath, UInt32* outEffectID)
- {
- #ifndef WIN32
- OSStatus result = noErr;
- if (sOpenALObject == NULL)
- {
- sOpenALObject = new OpenALObject(0.0f);
- result = sOpenALObject->Initialize();
- }
- return (result) ? result : sOpenALObject->LoadLoopingEffect(inLoopFilePath, inAttackFilePath, inDecayFilePath, outEffectID);
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_UnloadEffect(UInt32 inEffectID)
- {
- #ifndef WIN32
- return (sOpenALObject) ? sOpenALObject->UnloadEffect(inEffectID) : kSoundEngineErrUnitialized;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_StartEffect(UInt32 inEffectID)
- {
- #ifndef WIN32
- return (sOpenALObject) ? sOpenALObject->StartEffect(inEffectID) : kSoundEngineErrUnitialized;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_StopEffect(UInt32 inEffectID, Boolean inDoDecay)
- {
- #ifndef WIN32
- return (sOpenALObject) ? sOpenALObject->StopEffect(inEffectID, inDoDecay) : kSoundEngineErrUnitialized;
- #else
- return 0;
- #endif
- }
-
- extern "C"
- OSStatus SoundEngine_SetEffectPitch(UInt32 inEffectID, Float32 inValue)
- {
- #ifndef WIN32
- return (sOpenALObject) ? sOpenALObject->SetEffectPitch(inEffectID, inValue) : kSoundEngineErrUnitialized;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_SetEffectLevel(UInt32 inEffectID, Float32 inValue)
- {
- #ifndef WIN32
- return (sOpenALObject) ? sOpenALObject->SetEffectVolume(inEffectID, inValue) : kSoundEngineErrUnitialized;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_SetEffectPosition(UInt32 inEffectID, Float32 inX, Float32 inY, Float32 inZ)
- {
- #ifndef WIN32
- return (sOpenALObject) ? sOpenALObject->SetEffectPosition(inEffectID, inX, inY, inZ) : kSoundEngineErrUnitialized;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_SetEffectsVolume(Float32 inValue)
- {
- #ifndef WIN32
- return (sOpenALObject) ? sOpenALObject->SetEffectsVolume(inValue) : kSoundEngineErrUnitialized;
- #else
- return 0;
- #endif
- }
- extern "C"
- Float32 SoundEngine_GetEffectsVolume()
- {
- #ifndef WIN32
- return (sOpenALObject) ? sOpenALObject->GetEffectsVolume() : 0.0f;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_SetMaxDistance(Float32 inValue)
- {
- #ifndef WIN32
- return (sOpenALObject) ? sOpenALObject->SetMaxDistance(inValue) : kSoundEngineErrUnitialized;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_SetReferenceDistance(Float32 inValue)
- {
- #ifndef WIN32
- return (sOpenALObject) ? sOpenALObject->SetReferenceDistance(inValue) : kSoundEngineErrUnitialized;
- #else
- return 0;
- #endif
- }
- extern "C"
- OSStatus SoundEngine_SetEffectReferenceDistance(UInt32 inEffectID, Float32 inValue)
- {
- #ifndef WIN32
- return (sOpenALObject) ? sOpenALObject->SetEffectReferenceDistance(inEffectID, inValue) : kSoundEngineErrUnitialized;
- #else
- return 0;
- #endif
- }
|