#ifndef __OAL_SOURCE__
#define __OAL_SOURCE__
#include "oalImp.h"
#include "oalDevice.h"
#include "oalContext.h"
#include "al.h"
#include <Carbon/Carbon.h>
#include <AudioToolbox/AudioConverter.h>
#include <list>
#include <libkern/OSAtomic.h>
#include <vector>
#include <queue>
#include <dispatch/dispatch.h>
#include "CAStreamBasicDescription.h"
#include "CAAtomicStack.h"
#include "CAGuard.h"
class OALBuffer;
enum {
kSecondsOffset = 1,
kSampleOffset = 2,
kByteOffset = 3
};
enum {
kPendingProcessing = 0,
kInProgress = 1,
kProcessed = 2
};
enum {
kRampingComplete = -2,
kRampDown = -1,
kNoRamping = 0,
kRampUp = 1
};
enum {
kMQ_NoMessage = 0,
kMQ_Stop = 1,
kMQ_Rewind = 2,
kMQ_SetBuffer = 3,
kMQ_Play = 4,
kMQ_Pause = 5,
kMQ_Resume = 6,
kMQ_SetFramePosition = 7,
kMQ_ClearBuffersFromQueue = 8,
kMQ_DeconstructionStop = 9,
kMQ_Retrigger = 10
};
#define OALSourceError_CallConverterAgain 'agan'
#define kSourceNeedsBus -1
#define kDistanceScalar 10.0
#define kTransitionToStop 0XDD01
#define kTransitionToPlay 0XDD02
#define kTransitionToPause 0XDD03
#define kTransitionToRewind 0XDD04
#define kTransitionToRetrigger 0XDD05
#define kTransitionToResume 0XDD06
#pragma mark _____PlaybackMessage_____
class PlaybackMessage {
public:
PlaybackMessage(UInt32 inMessage, OALBuffer* inDeferredAppendBuffer, UInt32 inBuffersToUnqueue) :
mMessageID(inMessage),
mAppendBuffer(inDeferredAppendBuffer),
mBuffersToUnqueueInPostRender(inBuffersToUnqueue)
{ };
~PlaybackMessage(){};
PlaybackMessage* mNext;
UInt32 mMessageID;
OALBuffer* mAppendBuffer;
UInt32 mBuffersToUnqueueInPostRender;
PlaybackMessage *& next() { return mNext; }
void set_next(PlaybackMessage *next) { mNext = next; }
};
#pragma mark _____BufferQueue_____
struct BufferInfo {
ALuint mBufferToken; OALBuffer *mBuffer; UInt32 mOffset; UInt32 mProcessedState; ALuint mACToken; };
class BufferQueue : std::vector<BufferInfo> {
public:
BufferQueue() { mBufferQueueSize = 0;}
void AppendBuffer(OALSource* thisSource, ALuint inBufferToken, OALBuffer *inBuffer, ALuint inACToken);
ALuint RemoveQueueEntryByIndex(OALSource* thisSource, UInt32 inIndex, bool inReleaseIt);
UInt32 GetQueueSizeInFrames() ;
UInt32 GetBufferFrameCount(UInt32 inBufferIndex);
void SetFirstBufferOffset(UInt32 inFrameOffset) ;
ALuint GetBufferTokenByIndex(UInt32 inBufferIndex);
UInt32 GetCurrentFrame(UInt32 inBufferIndex);
BufferInfo* Get(short inBufferIndex) {
iterator it = begin();
std::advance(it, inBufferIndex);
if (it != end())
return(&(*it));
return (NULL);
}
void SetBufferAsProcessed(UInt32 inBufferIndex) {
iterator it = begin();
std::advance(it, inBufferIndex);
if (it != end())
it->mProcessedState = kProcessed;
}
void ResetBuffers() {
iterator it = begin();
while (it != end())
{
it->mProcessedState = kPendingProcessing;
it->mOffset = 0;
++it;
}
}
UInt32 GetQueueSize () { return mBufferQueueSize; }
void SetQueueSize () { mBufferQueueSize = Size(); }
bool Empty () const { return empty(); }
void Reserve(UInt32 reserveSize) {return reserve(reserveSize); }
private:
UInt32 GetPacketSize() ;
UInt32 FrameOffsetToPacketOffset(UInt32 inFrameOffset);
UInt32 Size () const { return size(); }
volatile int32_t mBufferQueueSize; };
typedef BufferQueue BufferQueue;
#pragma mark _____ACMap_____
struct ACInfo {
AudioConverterRef mConverter;
CAStreamBasicDescription mInputFormat;
};
class ACMap : std::multimap<ALuint, ACInfo, std::less<ALuint> > {
public:
void Add (const ALuint inACToken, ACInfo *inACInfo) {
iterator it = upper_bound(inACToken);
insert(it, value_type (inACToken, *inACInfo));
}
AudioConverterRef Get(ALuint inACToken) {
const_iterator it = find(inACToken);
iterator theEnd = end();
if (it != theEnd)
return ((*it).second.mConverter);
return (NULL);
}
void GetACForFormat (CAStreamBasicDescription* inFormat, ALuint &outToken) {
iterator it = begin();
outToken = 0; while (it != end()) {
if( (*it).second.mInputFormat == *inFormat) {
outToken = (*it).first;
it = end();
}
else
++it;
}
return;
}
void Remove (const ALuint inACToken) {
iterator it = find(inACToken);
if (it != end()) {
AudioConverterDispose((*it).second.mConverter);
erase(it);
}
}
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;
#pragma mark _____SourceNotifyInfo_____
class SourceNotificationDispatchMessage
{
public:
SourceNotificationDispatchMessage()
: mID(0),
mProc(0),
mUserData(0),
mSourceToken(0),
flagForDeletion(false){}
ALuint mID;
alSourceNotificationProc mProc;
void *mUserData;
ALuint mSourceToken;
bool flagForDeletion;
};
void CallSourceNotification(void* inMessage);
class SourceNotificationDispatch : public std::queue<SourceNotificationDispatchMessage>
{
public:
void AddSourceNotificationDispatchMessage(SourceNotificationDispatchMessage inMessage)
{
push(inMessage);
dispatch_async_f(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), (void*)(&back()), CallSourceNotification);
RemoveOldSourceNotificationDispatchMessages();
}
void RemoveOldSourceNotificationDispatchMessages()
{
while(front().flagForDeletion)
pop();
}
};
class SourceNotifyInfo
{
public:
SourceNotifyInfo()
: mID(0),
mProc(0),
mUserData(0)
{}
friend class SourceNotifications;
private:
ALuint mID;
alSourceNotificationProc mProc;
void *mUserData;
};
class SourceNotifications : public std::vector<SourceNotifyInfo>
{
public:
SourceNotifications(ALuint inSourceID)
: mSourceToken(inSourceID)
{
mNotifyMutex = new CAMutex("Notifcation Mutex");
mSourceNotificationDispatch = new SourceNotificationDispatch;
}
~SourceNotifications()
{
delete mNotifyMutex;
delete mSourceNotificationDispatch;
}
OSStatus AddSourceNotification(ALuint inID, alSourceNotificationProc inProc, void *inUserData)
{
CAMutex::Locker lock(*mNotifyMutex);
return AddSourceNotificationImp(inID, inProc, inUserData);
}
OSStatus RemoveSourceNotification(ALuint inID, alSourceNotificationProc inProc, void *inUserData)
{
CAMutex::Locker lock(*mNotifyMutex);
return RemoveSourceNotificationImp(inID, inProc, inUserData);
}
OSStatus RemoveAllSourceNotifications(ALuint inID)
{
CAMutex::Locker lock(*mNotifyMutex);
return RemoveAllSourceNotificationsImp(inID);
}
void CallSourceNotifications(ALuint inID)
{
if (mNotifyMutex != NULL) {
CAMutex::Locker lock(*mNotifyMutex);
return CallSourceNotificationsImp(inID);
}
}
private:
const SourceNotifications::iterator FindNotificationInfo(ALuint inID, alSourceNotificationProc inProc, void *inUserData)
{
SourceNotifications::iterator it;
for (it = begin(); it != end(); ++it) {
if ((it->mID == inID) && (it->mProc == inProc) && (it->mUserData == inUserData))
return it;
}
return it; }
OSStatus AddSourceNotificationImp(ALuint inID, alSourceNotificationProc inProc, void *inUserData)
{
OSStatus status = noErr;
SourceNotifications::iterator it = FindNotificationInfo(inID, inProc, inUserData);
if (it == end()) {
SourceNotifyInfo info;
info.mID = inID;
info.mProc = inProc;
info.mUserData = inUserData;
push_back(info);
}
else {
status = -50;
}
return status;
}
OSStatus RemoveSourceNotificationImp(ALuint inID, alSourceNotificationProc inProc, void *inUserData)
{
OSStatus status = noErr;
SourceNotifications::iterator it = FindNotificationInfo(inID, inProc, inUserData);
if (it != end())
{
erase(it);
for (it = begin(); it != end(); ++it) {
if (it->mID == inID)
break;
}
}
else {
status = -50;
}
return status;
}
OSStatus RemoveAllSourceNotificationsImp(ALuint inID)
{
SourceNotifications::iterator it;
for (int index = size() - 1; index >= 0; index--)
{
it = begin() + index;
if (it->mID == inID)
erase(it);
}
OSStatus status = noErr;
return status;
}
void CallSourceNotificationsImp(ALuint inID)
{
SourceNotifications::iterator it;
for (it = begin(); it != end(); ++it)
if (it->mID == inID) {
SourceNotificationDispatchMessage newMessage;
newMessage.mProc = it->mProc;
newMessage.mUserData = it->mUserData;
newMessage.mID = inID;
newMessage.mSourceToken = mSourceToken;
mSourceNotificationDispatch->AddSourceNotificationDispatchMessage(newMessage);
}
}
CAMutex* mNotifyMutex;
ALuint mSourceToken;
SourceNotificationDispatch* mSourceNotificationDispatch;
};
#pragma mark _____OALRenderLocker_____
class OALRenderLocker {
int mAcquireFlag, mTryFlag;
OALRenderLocker& operator= (const OALRenderLocker& as) { return *this; }
OALRenderLocker (const OALRenderLocker& as) {}
public:
OALRenderLocker () : mAcquireFlag(0), mTryFlag (0) {}
class RenderLocker {
OALRenderLocker &mEditor;
bool mInRenderThread;
public:
RenderLocker (OALRenderLocker &editor, bool inRenderThread)
: mEditor(editor),
mInRenderThread(inRenderThread)
{
if (!mInRenderThread)
{
OSAtomicIncrement32Barrier(&mEditor.mAcquireFlag);
while (mEditor.mTryFlag) { usleep(500); }
}
}
~RenderLocker ()
{
if (!mInRenderThread)
{
OSAtomicDecrement32Barrier (&mEditor.mAcquireFlag);
}
}
};
class RenderTryer {
OALRenderLocker &mTrier;
public:
RenderTryer (OALRenderLocker & trier)
: mTrier (trier)
{
OSAtomicIncrement32Barrier(&mTrier.mTryFlag);
}
~RenderTryer ()
{
OSAtomicDecrement32Barrier (&mTrier.mTryFlag);
}
bool Acquired () const { return !mTrier.mAcquireFlag; }
};
};
#pragma mark _____OALSource_____
class OALSource
{
#pragma mark __________ PRIVATE __________
private:
ALuint mSelfToken; bool mSafeForDeletion;
OALContext *mOwningContext;
bool mIsSuspended;
bool mCalculateDistance;
bool mResetBusFormat;
bool mResetBus;
bool mResetPitch;
BufferQueue *mBufferQueueActive; BufferQueue *mBufferQueueInactive; volatile int32_t mQueueLength; UInt32 mBuffersQueuedForClear; ALuint mCurrentBufferIndex; bool mQueueIsProcessed;
volatile int32_t mInUseFlag; CAGuard mSourceLock;
AURenderCallbackStruct mPlayCallback;
int mCurrentPlayBus;
ACMap *mACMap;
bool mOutputSilence;
Float32 mPosition[3];
Float32 mVelocity[3];
Float32 mConeDirection[3];
UInt32 mLooping;
UInt32 mSourceRelative;
UInt32 mSourceType;
Float32 mConeInnerAngle;
Float32 mConeOuterAngle;
Float32 mConeOuterGain;
Float32 mConeGainScaler;
Float32 mAttenuationGainScaler;
float mReadIndex;
float mCachedInputL1;
float mCachedInputL2;
float mCachedInputR1;
float mCachedInputR2;
UInt32 mTempSourceStorageBufferSize;
AudioBufferList *mTempSourceStorage;
UInt32 mState; float mGain;
Float32 mPitch;
Float32 mDopplerScaler;
Float32 mRollOffFactor;
Float32 mReferenceDistance;
Float32 mMaxDistance;
Float32 mMinGain;
Float32 mMaxGain;
SInt32 mRampState;
UInt32 mBufferCountToUnqueueInPostRender;
bool mTransitioningToFlushQ;
UInt32 mPlaybackHeadPosition;
Float32 mASAReverbSendLevel;
Float32 mASAOcclusion;
Float32 mASAObstruction;
OALRenderLocker mRenderLocker;
AUNode mRogerBeepNode;
AudioUnit mRogerBeepAU;
Boolean mASARogerBeepEnable;
Boolean mASARogerBeepOn;
Float32 mASARogerBeepGain;
SInt32 mASARogerBeepSensitivity;
SInt32 mASARogerBeepType;
char* mASARogerBeepPreset;
AUNode mDistortionNode;
AudioUnit mDistortionAU;
Boolean mASADistortionEnable;
Boolean mASADistortionOn;
Float32 mASADistortionMix;
SInt32 mASADistortionType;
char* mASADistortionPreset;
AudioUnit mRenderUnit;
UInt32 mRenderElement;
SourceNotifications* mSourceNotifications;
typedef TAtomicStack<PlaybackMessage> PlaybackMessageList;
PlaybackMessageList mMessageQueue;
void JoinBufferLists();
void LoopToBeginning();
void ChangeChannelSettings();
void InitSource();
void SetState(UInt32 inState);
OSStatus DoRender (AudioBufferList *ioData);
OSStatus DoSRCRender (AudioBufferList *ioData); bool ConeAttenuation();
void CalculateDistanceAndAzimuth(Float32 *outDistance, Float32 *outAzimuth, Float32 *outElevation, Float32 *outDopplerShift);
void UpdateBusGain ();
void UpdateMinBusGain ();
void UpdateMaxBusGain ();
void UpdateBusFormat ();
void UpdateBusReverb ();
void UpdateBusOcclusion ();
void UpdateBusObstruction ();
void UpdateQueue ();
void RampDown (AudioBufferList *ioData);
void RampUp (AudioBufferList *ioData);
void ClearActiveQueue();
void AddNotifyAndRenderProcs();
void ReleaseNotifyAndRenderProcs();
void DisconnectFromBus();
void SetupMixerBus();
void ResetMixerBus();
void SetupDistortionAU();
void SetupRogerBeepAU();
bool PrepBufferQueueForPlayback();
UInt32 SecondsToFrames(Float32 inSeconds);
UInt32 BytesToFrames(Float32 inBytes);
void AppendBufferToQueue(ALuint inBufferToken, OALBuffer *inBuffer);
UInt32 FramesToSecondsInt(UInt32 inFrames);
Float32 FramesToSecondsFloat(UInt32 inFrames);
UInt32 FramesToBytes(UInt32 inFrames);
void PostRenderSetBuffer(ALuint inBufferToken, OALBuffer *inBuffer);
void PostRenderRemoveBuffersFromQueue(UInt32 inBuffersToUnqueue);
void FlushBufferQueue();
void AdvanceQueueToFrameIndex(UInt32 inFrameOffset);
OSStatus SetDistanceParams(bool inChangeReferenceDistance, bool inChangeMaxDistance);
Float32 GetMaxAttenuation(Float32 inRefDistance, Float32 inMaxDistance, Float32 inRolloff);
BufferInfo* NextPlayableBufferInActiveQ();
inline bool InRenderThread() { return mOwningContext->CallingInRenderThread(); }
void SetQueueLength() { mQueueLength = mBufferQueueActive->GetQueueSize() + mBufferQueueInactive->GetQueueSize() - mBuffersQueuedForClear; }
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(ALuint inSelfToken, OALContext *inOwningContext);
~OALSource();
void SetPitch (Float32 inPitch);
void SetGain (Float32 inGain);
void SetMinGain (Float32 inMinGain);
void SetMaxGain (Float32 inMaxGain);
void SetReferenceDistance (Float32 inReferenceDistance);
void SetMaxDistance (Float32 inMaxDistance);
void SetRollOffFactor (Float32 inRollOffFactor);
void SetLooping (UInt32 inLooping);
void SetPosition (Float32 inX, Float32 inY, Float32 inZ);
void SetVelocity (Float32 inX, Float32 inY, Float32 inZ);
void SetDirection (Float32 inX, Float32 inY, Float32 inZ);
void SetSourceRelative (UInt32 inSourceRelative);
void SetChannelParameters ();
void SetConeInnerAngle (Float32 inConeInnerAngle);
void SetConeOuterAngle (Float32 inConeOuterAngle);
void SetConeOuterGain (Float32 inConeOuterGain);
void SetQueueOffset(UInt32 inOffsetType, Float32 inSecondOffset);
void SetUpDeconstruction();
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);
void GetVelocity (Float32 &inX, Float32 &inY, Float32 &inZ);
void GetDirection (Float32 &inX, Float32 &inY, Float32 &inZ);
UInt32 GetSourceRelative ();
UInt32 GetSourceType ();
Float32 GetConeInnerAngle ();
Float32 GetConeOuterAngle ();
Float32 GetConeOuterGain ();
UInt32 GetState();
ALuint GetToken();
UInt32 GetQueueFrameOffset();
UInt32 GetQueueOffset(UInt32 inOffsetType);
Float32 GetQueueOffsetSecondsFloat();
void SetInUseFlag() { OSAtomicIncrement32Barrier(&mInUseFlag); }
void ClearInUseFlag() { OSAtomicDecrement32Barrier(&mInUseFlag); }
UInt32 GetQLength();
UInt32 GetBuffersProcessed();
void SetBuffer (ALuint inBufferToken, OALBuffer *inBuffer);
ALuint GetBuffer ();
void AddToQueue(ALuint inBufferToken, OALBuffer *inBuffer);
void RemoveBuffersFromQueue(UInt32 inCount, ALuint *outBufferTokens);
bool IsSourceTransitioningToFlushQ();
bool IsSafeForDeletion () { return (mSafeForDeletion && (mInUseFlag <= 0) && mSourceLock.IsFree()); }
ALenum AddNotification(ALuint notificationID, alSourceNotificationProc notifyProc, ALvoid* userData);
void RemoveNotification(ALuint notificationID, alSourceNotificationProc notifyProc, ALvoid* userData);
void ClearMessageQueue();
void SwapBufferQueues();
void AddPlaybackMessage(UInt32 inMessage, OALBuffer* inDeferredAppendBuffer, UInt32 inBuffersToUnqueue);
void SetPlaybackState(ALuint inState, bool sendSourceStateChangeNotification=false);
OSStatus DoPreRender ();
OSStatus DoPostRender ();
void Play();
void Pause();
void Resume();
void Rewind();
void Stop();
void Suspend();
void Unsuspend();
void SetReverbSendLevel(Float32 inReverbLevel);
void SetOcclusion(Float32 inOcclusion);
void SetObstruction(Float32 inObstruction);
void SetRogerBeepEnable(Boolean inEnable);
void SetRogerBeepOn(Boolean inOn);
void SetRogerBeepGain(Float32 inGain);
void SetRogerBeepSensitivity(SInt32 inSensitivity);
void SetRogerBeepType(SInt32 inType);
void SetRogerBeepPreset(FSRef* inRef);
void SetDistortionEnable(Boolean inEnable);
void SetDistortionOn(Boolean inOn);
void SetDistortionMix(Float32 inMix);
void SetDistortionType(SInt32 inType);
void SetDistortionPreset(FSRef* inRef);
Float32 GetReverbSendLevel() {return mASAReverbSendLevel;}
Float32 GetOcclusion() {return mASAOcclusion;}
Float32 GetObstruction() {return mASAObstruction;}
Boolean GetRogerBeepEnable() {return mASARogerBeepEnable;}
Boolean GetRogerBeepOn() {return mASARogerBeepOn;}
Float32 GetRogerBeepGain() {return mASARogerBeepGain;}
UInt32 GetRogerBeepSensitivity() {return mASARogerBeepSensitivity;}
UInt32 GetRogerBeepType() {return mASARogerBeepType;}
Boolean GetDistortionEnable() {return mASADistortionEnable;}
Boolean GetDistortionOn() {return mASADistortionOn;}
Float32 GetDistortionMix() {return mASADistortionMix;}
SInt32 GetDistortionType() {return mASADistortionType;}
};
#pragma mark _____OALSourceMap_____
class OALSourceMap : std::multimap<ALuint, OALSource*, std::less<ALuint> > {
public:
void Add (const ALuint 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(ALuint inSourceToken) {
iterator it = find(inSourceToken);
if (it != end())
return ((*it).second);
return (NULL);
}
void MarkAllSourcesForRecalculation() {
iterator it = begin();
while (it != end()) {
(*it).second->SetChannelParameters();
++it;
}
return;
}
void SuspendAllSources() {
iterator it = begin();
while (it != end())
{
(*it).second->Suspend();
++it;
}
return;
}
void UnsuspendAllSources() {
iterator it = begin();
while (it != end())
{
(*it).second->Unsuspend();
++it;
}
return;
}
void Remove (const ALuint inSourceToken) {
iterator it = find(inSourceToken);
if (it != end())
erase(it);
}
UInt32 Size () const { return size(); }
bool Empty () const { return empty(); }
};
#endif