📄 soundengine.cpp
字号:
result = SetupQueue(fileInfo); AssertNoError("Error setting up queue", fail); result = SetupBuffers(fileInfo); AssertNoError("Error setting up queue buffers", fail); } // if this is just part of the playlist, close the file for now else { result = AudioFileClose(fileInfo->mAFID); AssertNoError("Error closing file", fail); } return result; fail: if (fileInfo) delete fileInfo; return result; } OSStatus UpdateGain() { return SetVolume(mVolume); } OSStatus SetVolume(Float32 inVolume) { mVolume = inVolume; return AudioQueueSetParameter(mQueue, kAudioQueueParam_Volume, mVolume * gMasterVolumeGain); } OSStatus Start() { OSStatus result = AudioQueuePrime(mQueue, 1, NULL); if (result) { printf("Error priming queue"); return result; } return AudioQueueStart(mQueue, NULL); } OSStatus Stop(Boolean inStopAtEnd) { if (inStopAtEnd) { mStopAtEnd = true; return noErr; } else return AudioQueueStop(mQueue, true); } 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;};#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, 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) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -