/*============================================================================= oalSource.h al_osx Created by Bob Aron on Mon Oct 13 2003. Copyright (c) 2003 Apple Computer Inc. All rights reserved. $Log: oalSource.h,v $ Revision 1.29 2005/01/18 20:40:06 baron Use OpenAL doppler formula instead of the 3DMixer doppler feature. [3917843] Revision 1.28 2004/09/27 21:40:24 baron Make the OALSource object thread safe by using two bufferQ lists and mutexes. Don't dereference NULL ptr. [3791105, 3791120] Revision 1.27 2004/09/02 20:20:08 baron Add workaround for DistanceParameter problem in the 2.0 3DMixer. Allow Doppler to be turned ON. Allow OAL Distance Model to be AL_NONE. Revision 1.26 2004/03/19 22:52:55 baron Add BSD License and clean up comments before submitting to Open Source (Creative). Revision 1.25 2004/03/16 22:21:13 baron Remove DoBuffersUseThisAC() buffer queue list. Revision 1.24 2004/03/11 19:42:23 baron Add extension to clamp rendering to stereo (ALC_RENDER_CHANNEL_COUNT). Merge the OAL3DGraph and OALDevice classes. General clean up. Revision 1.23 2004/03/04 01:31:55 baron Support 1.3 version of the 3DMixer with added SRC code. Revision 1.22 2004/02/25 20:51:55 baron Check version of 3DMixer component and fail if pre-Panther version. Use playback rate parameter to adjust sample rates if 3DMixer is not 2.0 or >, else use stream format property to change sample rates on the mixer busses. Revision 1.21 2004/02/24 19:05:16 baron Stop using the playback rate parameter to adjust for sample rate of data. Use the stream format property instead. Revision 1.20 2004/02/13 17:42:00 baron Removed mResetGain member from the OALSource class. Revision 1.19 2004/02/13 02:35:03 baron Check for NANs. Add 3DMixer Distance Params support. Partial Min/Max Gain changes. Everything builds and works, some commented code needs to be clean up still. Revision 1.18 2004/01/29 22:08:06 baron Misc. cleanup. Revision 1.17 2004/01/26 18:44:26 baron Separate the FileRenderProc() into a notify and input proc. Revision 1.16 2004/01/20 19:45:08 baron Reinstate the should fade code. Set bus stream format in the GetAvailableMono/Stereo methods instead of the Source Play() method. cleanup. Revision 1.15 2004/01/20 02:12:00 baron Doppler support changes. Revision 1.14 2004/01/09 18:35:06 baron Remove calloc from Render Proc and other potential blocking code. Add several build flags and logging code. Revision 1.13 2003/12/23 01:44:32 baron Remove duplicate method for finding QLength.. Revision 1.12 2003/12/19 22:22:30 baron Only reset specific bus settings (pitch, gain, distance, doppler) when necessary from the source's render proc. Revision 1.11 2003/12/17 18:28:20 baron Add mMinDistance member for distance calculations. Revision 1.10 2003/12/11 23:03:32 baron Add the ability to bypass the AudioConverters when the data is already in the mixer's format. Add stereo pass through support. Revision 1.9 2003/12/10 17:57:24 baron Remove an infinite loop in ProcessedBufferCount(). Revision 1.8 2003/12/09 01:26:22 baron more cleanup. Revision 1.7 2003/12/06 00:11:20 baron Clean up RenderProc and ACFillProc. Don't count samples pulled for anymore. Revision 1.6 2003/12/03 23:17:09 baron Add some doppler support and clean up comments Revision 1.5 2003/12/02 18:59:50 baron Add check-in comment logging. $NoKeywords: $ =============================================================================*/ /********************************************************************************************************************************** * * OpenAL cross platform audio library * Copyright © 2004, Apple Computer, Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted provided * that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided with the distribution. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * **********************************************************************************************************************************/ #ifndef __OAL_SOURCE__ #define __OAL_SOURCE__ #include "oalImp.h" #include "oalDevice.h" #include "oalBuffer.h" #include "oalContext.h" #include "altypes.h" #include #include #include #include "CAStreamBasicDescription.h" #include "CAGuard.h" #include "CAMutex.h" // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // buffer info state constants enum { kPendingProcessing = 0, kInProgress = 1, kProcessed = 2 }; #define OALSourceError_CallConverterAgain 'agan' #define kSourceNeedsBus -1 // do not change kDistanceScalar from 10.0 - it is used to compensate for a reverb related problem in the 3DMixer #define kDistanceScalar 10.0 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Source - Buffer Queue // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #pragma mark _____BufferQueue_____ // This struct is used by OAL source to store info about the buffers in it's queue struct BufferInfo { UInt32 mBufferToken; // the buffer name that the user was returned when the buffer was created OALBuffer *mBuffer; // buffer object UInt32 mOffset; // current read position offset of this data UInt32 mProcessedState; // mark as true when data of this buffer is finished playing and looping is off UInt32 mACToken; // use this AC from the ACMap for converting the data to the mixer format, when // set to zero, then NO AC is needed, the data has already been converted }; class BufferQueue : std::list { public: BufferInfo* Get(short index) { iterator it = begin(); std::advance(it, index); if (it != end()) return(&(*it)); return (NULL); } void AppendBuffer(UInt32 inBufferToken, OALBuffer *inBuffer, UInt32 inACToken) { BufferInfo newBuffer; inBuffer->mAttachedCount++; newBuffer.mBufferToken = inBufferToken; newBuffer.mBuffer = inBuffer; newBuffer.mOffset = 0; newBuffer.mProcessedState = kPendingProcessing; newBuffer.mACToken = inACToken; push_back(value_type (newBuffer)); } bool IsBufferProcessed(UInt32 inIndex) { iterator it = begin(); bool returnValue = false; std::advance(it, inIndex); if (it != end()) returnValue = (it->mProcessedState == kProcessed) ? true : false; return (returnValue); } void SetBufferAsProcessed(UInt32 inIndex) { iterator it = begin(); std::advance(it, inIndex); if (it != end()) it->mProcessedState = kProcessed; } UInt32 GetBufferTokenByIndex(UInt32 inIndex) { iterator it = begin(); std::advance(it, inIndex); if (it != end()) return (it->mBufferToken); return (0); } // return the bufferToken for the buffer removed from the queue UInt32 RemoveQueueEntryByIndex(UInt32 inIndex) { iterator it = begin(); UInt32 outBufferToken = 0; std::advance(it, inIndex); if (it != end()) { outBufferToken = it->mBufferToken; it->mBuffer->mAttachedCount--; erase(it); } return (outBufferToken); } // mark all the buffers in the queue as unprocessed and offset 0 void ResetBuffers() { iterator it = begin(); while (it != end()) { it->mProcessedState = kPendingProcessing; it->mOffset = 0; it++; } } // count the buffers that have been played UInt32 ProcessedBufferCount() { iterator it = begin(); UInt32 count = 0; while (it != end()) { if (it->mProcessedState == kProcessed) { count++; } it++; } return (count); } UInt32 Size () const { return size(); } bool Empty () const { return empty(); } }; typedef BufferQueue BufferQueue; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ACMap - map the AudioConverters for the sources queue // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #pragma mark _____ACMap_____ // This struct is used by OAL source to store info about the ACs used by it's queue struct ACInfo { AudioConverterRef mConverter; CAStreamBasicDescription mInputFormat; }; class ACMap : std::multimap > { public: // add a new context to the map void Add (const UInt32 inACToken, ACInfo *inACInfo) { iterator it = upper_bound(inACToken); insert(it, value_type (inACToken, *inACInfo)); } AudioConverterRef Get(UInt32 inACToken) { iterator it = find(inACToken); iterator theEnd = end(); if (it != theEnd) return ((*it).second.mConverter); return (NULL); } void GetACForFormat (CAStreamBasicDescription &inFormat, UInt32 &outToken) { iterator it = begin(); outToken = 0; // 0 means there is none yet while (it != end()) { if( (*it).second.mInputFormat == inFormat) { outToken = (*it).first; it = end(); } else it++; } return; } void Remove (const UInt32 inACToken) { iterator it = find(inACToken); if (it != end()) { AudioConverterDispose((*it).second.mConverter); erase(it); } } // the map should be disposed after making this call void RemoveAllConverters () { iterator it = begin(); iterator theEnd = end(); while (it != theEnd) { AudioConverterDispose((*it).second.mConverter); it++; } } UInt32 Size () const { return size(); } bool Empty () const { return empty(); } }; typedef ACMap ACMap; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // OALSources // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #define kBufferQueueMutex "BUFFER_Q" #define kStateMutex "SOURCE_STATE" #pragma mark _____OALSource_____ class OALSource { #pragma mark __________ PRIVATE __________ private: UInt32 mSelfToken; // the token returned to the caller upon alGenSources() OALContext *mOwningContext; OALDevice *mOwningDevice; // the OALDevice object for the owning OpenAL device CAMutex mStateMutex; // used for non render thread locks bool mCalculateDistance; bool mResetBusFormat; bool mResetPitch; BufferQueue *mBufferQueueActive; // map of buffers for queueing BufferQueue *mBufferQueueInactive; // map of buffers already queued CAMutex mBufferQueueMutex; UInt32 mCurrentBufferIndex; // token for curent buffer being played bool mQueueIsProcessed; // state of the entire buffer queue int mRenderThreadID; CAGuard mPlayGuard; AURenderCallbackStruct mPlayCallback; int mCurrentPlayBus; // the mixer bus currently used by this source bool mShouldFade; ACMap *mACMap; bool mOutputSilence; Float32 mPosition[3]; Float32 mVelocity[3]; Float32 mDirection[3]; UInt32 mLooping; UInt32 mSourceRelative; Float32 mConeInnerAngle; Float32 mConeOuterAngle; Float32 mConeOuterGain; // support for pre 2.0 3DMixer float mReadIndex; float mCachedInputL1; float mCachedInputL2; float mCachedInputR1; float mCachedInputR2; UInt32 mTempSourceStorageBufferSize; AudioBufferList *mTempSourceStorage; UInt32 mState; // playback state: Playing, Stopped, Paused, Initial float mGain; Float32 mPitch; Float32 mDopplerScaler; Float32 mRollOffFactor; Float32 mReferenceDistance; Float32 mMaxDistance; Float32 mMinDistance; Float32 mMinGain; Float32 mMaxGain; void JoinBufferLists(); void LoopToBeginning(); void ChangeChannelSettings(bool isRenderThread); void InitSource(); void SetState(UInt32 inState); OSStatus DoPreRender (); OSStatus DoRender (AudioBufferList *ioData); OSStatus DoSRCRender (AudioBufferList *ioData); // support for pre 2.0 3DMixer OSStatus DoPostRender (); void CalculateDistanceAndAzimuth(Float32 *outDistance, Float32 *outAzimuth, Float32 *outDopplerShift, bool isRenderThread); bool CallingInRenderThread () const { return (int(pthread_self()) == mRenderThreadID); } void UpdateBusGain (bool isRenderThread); void UpdateBusFormat (bool isRenderThread); void Reset(); void PlayFinished(); OSStatus MuteCurrentPlayBus () const { return AudioUnitSetParameter ( mOwningDevice->GetMixerUnit(), k3DMixerParam_Gain, kAudioUnitScope_Input, mCurrentPlayBus, 0.0, 0); } static OSStatus SourceNotificationProc (void *inRefCon, AudioUnitRenderActionFlags *inActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData); static OSStatus SourceInputProc ( void *inRefCon, AudioUnitRenderActionFlags *inActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumberFrames, AudioBufferList *ioData); static OSStatus ACComplexInputDataProc ( AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void* inUserData); #pragma mark __________ PUBLIC __________ public: OALSource(const UInt32 inSelfToken, OALContext *inOwningContext); ~OALSource(); // set info methods - these may be called from either the render thread or the API caller void SetPitch (Float32 inPitch, bool isRenderThread); void SetGain (Float32 inGain, bool isRenderThread); void SetMinGain (Float32 inMinGain, bool isRenderThread); void SetMaxGain (Float32 inMaxGain, bool isRenderThread); void SetReferenceDistance (Float32 inReferenceDistance, bool isRenderThread); void SetMaxDistance (Float32 inMaxDistance, bool isRenderThread); void SetRollOffFactor (Float32 inRollOffFactor, bool isRenderThread); void SetLooping (UInt32 inLooping, bool isRenderThread); void SetPosition (Float32 inX, Float32 inY, Float32 inZ, bool isRenderThread); void SetVelocity (Float32 inX, Float32 inY, Float32 inZ, bool isRenderThread); void SetDirection (Float32 inX, Float32 inY, Float32 inZ, bool isRenderThread); void SetSourceRelative (UInt32 inSourceRelative, bool isRenderThread); void SetChannelParameters (bool isRenderThread); void SetConeInnerAngle (Float32 inConeInnerAngle, bool isRenderThread); void SetConeOuterAngle (Float32 inConeOuterAngle, bool isRenderThread); void SetConeOuterGain (Float32 inConeOuterGain, bool isRenderThread); // get info methods Float32 GetPitch (); Float32 GetDopplerScaler (); Float32 GetGain (); Float32 GetMinGain (); Float32 GetMaxGain (); Float32 GetReferenceDistance (); Float32 GetMaxDistance (); Float32 GetRollOffFactor (); UInt32 GetLooping (); void GetPosition (Float32 &inX, Float32 &inY, Float32 &inZ, bool isRenderThread); void GetVelocity (Float32 &inX, Float32 &inY, Float32 &inZ, bool isRenderThread); void GetDirection (Float32 &inX, Float32 &inY, Float32 &inZ, bool isRenderThread); UInt32 GetSourceRelative (); Float32 GetConeInnerAngle (); Float32 GetConeOuterAngle (); Float32 GetConeOuterGain (); UInt32 GetState(); UInt32 GetToken(); // buffer queue UInt32 GetQLength(); UInt32 GetBuffersProcessed(); void SetBuffer (UInt32 inBuffer, OALBuffer *inBuffer); UInt32 GetBuffer (); void AppendBufferToQueue(UInt32 inBufferToken, OALBuffer *inBuffer); void RemoveBuffersFromQueue(UInt32 inCount, UInt32 *outBufferTokens); // playback methods void Play(); void Pause(); void Resume(); void Stop(); }; // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ #pragma mark _____OALSourceMap_____ class OALSourceMap : std::multimap > { public: // add a new context to the map void Add (const UInt32 inSourceToken, OALSource **inSource) { iterator it = upper_bound(inSourceToken); insert(it, value_type (inSourceToken, *inSource)); } OALSource* GetSourceByIndex(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); } OALSource* Get(UInt32 inSourceToken) { iterator it = find(inSourceToken); if (it != end()) return ((*it).second); return (NULL); } void MarkAllSourcesForRecalculation(bool isRenderThread) { iterator it = begin(); while (it != end()) { (*it).second->SetChannelParameters(isRenderThread); it++; } return; } void SetDopplerForAllSources(bool isRenderThread) { MarkAllSourcesForRecalculation(isRenderThread); return; } void StopAllSources() { iterator it = begin(); while (it != end()) { (*it).second->Stop(); it++; } return; } void Remove (const UInt32 inSourceToken) { iterator it = find(inSourceToken); if (it != end()) erase(it); } UInt32 Size () const { return size(); } bool Empty () const { return empty(); } }; #endif