123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539 |
- /*
-
- Copyright (C) 2009-2011 id Software LLC, a ZeniMax Media company.
-
- This program is free software; you can redistribute it and/or
- modify it under the terms of the GNU General Public License
- as published by the Free Software Foundation; either version 2
- of the License, or (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- */
- //==================================================================================================
- // 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>
- extern "C" {
- #include "doomiphone.h"
- }
- enum {
- kSoundEngineErrUnitialized = 1,
- kSoundEngineErrInvalidID = 2,
- kSoundEngineErrFileNotFound = 3,
- kSoundEngineErrInvalidFileFormat = 4,
- kSoundEngineErrDeviceNotFound = 5
- };
- #define AssertNoError(inMessage, inHandler) \
- if(result != noErr) \
- { \
- printf("%s: %d\n", inMessage, (int)result); \
- goto inHandler; \
- }
- #define kNumberBuffers 3
- static Float32 gMasterVolumeGain = 0.5f;
- //==================================================================================================
- // Helper functions
- //==================================================================================================
- OSStatus LoadFileDataInfo(const char *inFilePath, AudioFileID &outAFID, AudioStreamBasicDescription &outFormat, UInt64 &outDataSize)
- {
- UInt32 thePropSize;
-
- CFURLRef theURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8*)inFilePath, strlen(inFilePath), false);
- if (theURL == NULL)
- return kSoundEngineErrFileNotFound;
- OSStatus result = AudioFileOpenURL(theURL, kAudioFileReadPermission, 0, &outAFID);
- CFRelease(theURL);
- AssertNoError("Error opening file", end);
-
- thePropSize = sizeof(outFormat);
- 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
- //==================================================================================================
- 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;
- class BackgroundTrackMgr
- {
- public:
- BackgroundTrackMgr();
- ~BackgroundTrackMgr();
-
- void Teardown();
-
- 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 SetVolume(Float32 inVolume);
- Float32 GetVolume() const;
-
- OSStatus Start();
- OSStatus Stop(Boolean inStopAtEnd);
-
- AudioQueueRef mQueue;
- AudioQueueBufferRef mBuffers[kNumberBuffers];
- UInt32 mBufferByteSize;
- SInt64 mCurrentPacket;
- UInt32 mNumPacketsToRead;
- Float32 mVolume;
- AudioStreamPacketDescription * mPacketDescs;
- static BG_FileInfo * CurFileInfo;
- Boolean mStopAtEnd;
- };
- BG_FileInfo *BackgroundTrackMgr::CurFileInfo;
- BackgroundTrackMgr::BackgroundTrackMgr()
- : mQueue(0),
- mBufferByteSize(0),
- mCurrentPacket(0),
- mNumPacketsToRead(0),
- mVolume(1.0f),
- mPacketDescs(NULL),
- mStopAtEnd(false)
- { }
- BackgroundTrackMgr::~BackgroundTrackMgr() {
- Teardown();
- }
- void BackgroundTrackMgr::Teardown() {
- if (mQueue) {
- AudioQueueDispose(mQueue, true);
- mQueue = NULL;
- }
- if ( CurFileInfo ) {
- AudioFileClose( CurFileInfo->mAFID);
- delete CurFileInfo;
- CurFileInfo = NULL;
- }
- if (mPacketDescs) {
- delete[] mPacketDescs;
- mPacketDescs = NULL;
- }
- }
- 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;
- 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->mStopAtEnd)) {
- nPackets = THIS->mNumPacketsToRead;
- } else {
- UInt32 numBytes;
- while (nPackets == 0) {
- // if loadAtOnce, get all packets in the file, otherwise ~.5 seconds of data
- nPackets = THIS->mNumPacketsToRead;
- 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;
-
- // we have gone through the playlist. if mStopAtEnd, stop the queue here
- if ( THIS->mStopAtEnd ) {
- result = AudioQueueStop(inAQ, false);
- AssertNoError("Error stopping queue", end);
- return;
- }
- }
- }
- }
-
- 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 err;
- OSStatus result = AudioQueueNewOutput(&inFileInfo->mFileFormat, QueueCallback, this,
- CFRunLoopGetMain() /* CFRunLoopGetCurrent() */, kCFRunLoopCommonModes, 0, &mQueue);
- AssertNoError("Error creating queue", end);
- #if 0
- // (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);
- }
- #endif
- // channel layout
- 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);
- }
-
- // volume
- result = SetVolume(mVolume);
-
- end:
- return result;
- }
- OSStatus BackgroundTrackMgr::SetupBuffers(BG_FileInfo *inFileInfo) {
- int numBuffersToQueue = kNumberBuffers;
- UInt32 maxPacketSize;
- UInt32 size = sizeof(maxPacketSize);
- bool isFormatVBR;
- // 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);
- 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) {
- // OSStatus result = LoadFileDataInfo(CurFileInfo->mFilePath.c_str(), CurFileInfo->mAFID, CurFileInfo->mFileFormat, CurFileInfo->mFileDataSize);
- // AssertNoError("Error getting file data info", fail);
- OSStatus result;
- UInt32 thePropSize;
-
- CFURLRef theURL = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (UInt8*)inFilePath, strlen(inFilePath), false);
- if (theURL == NULL)
- result = kSoundEngineErrFileNotFound;
- else
- result = 0;
- AssertNoError("Error opening URL", fail);
-
- CurFileInfo = new BG_FileInfo;
- CurFileInfo->mFilePath = inFilePath;
-
- result = AudioFileOpenURL(theURL, kAudioFileReadPermission, 0, &CurFileInfo->mAFID);
- CFRelease(theURL);
- AssertNoError("Error opening file", fail);
-
- thePropSize = sizeof(CurFileInfo->mFileFormat);
- result = AudioFileGetProperty(CurFileInfo->mAFID, kAudioFilePropertyDataFormat, &thePropSize, &CurFileInfo->mFileFormat);
- AssertNoError("Error getting file format", fail);
-
- thePropSize = sizeof(UInt64);
- result = AudioFileGetProperty(CurFileInfo->mAFID, kAudioFilePropertyAudioDataByteCount, &thePropSize, &CurFileInfo->mFileDataSize);
- AssertNoError("Error getting file data size", fail);
-
- CurFileInfo->mLoadAtOnce = inLoadAtOnce;
- CurFileInfo->mFileDataInQueue = false;
-
- result = SetupQueue(CurFileInfo);
- AssertNoError("Error setting up queue", fail);
-
- result = SetupBuffers(CurFileInfo);
- AssertNoError("Error setting up queue buffers", fail);
-
- return result;
-
- fail:
- if (CurFileInfo) {
- delete CurFileInfo;
- CurFileInfo = NULL;
- }
- return result;
- }
- OSStatus BackgroundTrackMgr::SetVolume(Float32 inVolume) {
- mVolume = inVolume;
- return AudioQueueSetParameter(mQueue, kAudioQueueParam_Volume, mVolume * gMasterVolumeGain);
- }
- Float32 BackgroundTrackMgr::GetVolume() const {
- return mVolume;
- }
- OSStatus BackgroundTrackMgr::Start() {
- OSStatus result = AudioQueuePrime(mQueue, 1, NULL);
- if (result) {
- printf("BackgroundTrackMgr: 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);
- }
- }
- static BackgroundTrackMgr sBackgroundTrackMgr;
- static char currentMusicName[1024];
- void iphonePauseMusic() {
- if( music ) {
- if ( music->value == 0 ) {
- // music is disabled
- return;
- }
- AudioQueuePause(sBackgroundTrackMgr.mQueue);
- }
- }
- void iphoneResumeMusic() {
- if ( music->value == 0 ) {
- // music is disabled
- return;
- }
- AudioQueueStart(sBackgroundTrackMgr.mQueue,NULL);
- }
- void iphoneStopMusic() {
- sBackgroundTrackMgr.Teardown();
- }
- void iphoneStartMusic() {
- if ( music->value == 0 ) {
- // music is disabled
- return;
- }
- char fullName[1024];
- sprintf( fullName, "%s/base/music/d_%s.mp3", SysIphoneGetAppDir(), currentMusicName );
-
- printf( "Starting music '%s'\n", fullName );
- iphoneStopMusic();
- sBackgroundTrackMgr.LoadTrack( fullName, false, true);
- sBackgroundTrackMgr.Start();
-
- if ( !strcmp( currentMusicName, "intro" ) ) {
- // stop the intro music at end, don't loop
- sBackgroundTrackMgr.mStopAtEnd = true;
- } else {
- sBackgroundTrackMgr.mStopAtEnd = false;
- }
- }
- void iphonePlayMusic( const char *name ) {
- strcpy( currentMusicName, name );
-
- iphoneStartMusic();
- }
|