Commit 7be238c6 authored by Ryan C. Gordon's avatar Ryan C. Gordon

Replaced Mac OS X's C++ cdrom code with almost-direct translation to C. Sam

 requested this effort on the mailing list, apparently because of binary
 compatibility issues between 10.4 and earlier systems (or gcc4 and earlier
 compilers?).

Works fine with SDL12/test/testcdrom.c, with this command line:

./testcdrom -status -list -play -sleep 5000 -pause -sleep 3000 -resume \
            -sleep 5000 -stop -sleep 3000 -play -sleep 3000 -stop \
            -sleep 3000 -eject

Unix Makefiles work, XCode project still need updating for new filenames.

--HG--
extra : convert_revision : svn%3Ac70aab31-4412-0410-b14c-859654838e24/trunk%401146
parent cddfe242
...@@ -28,14 +28,16 @@ ...@@ -28,14 +28,16 @@
// //
#include "AudioFilePlayer.h" #include "AudioFilePlayer.h"
/*
void ThrowResult (OSStatus result, const char* str) void ThrowResult (OSStatus result, const char* str)
{ {
SDL_SetError ("Error: %s %d", str, result); SDL_SetError ("Error: %s %d", str, result);
throw result; throw result;
} }
*/
#if DEBUG #if DEBUG
void PrintStreamDesc (AudioStreamBasicDescription *inDesc) static void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
{ {
if (!inDesc) { if (!inDesc) {
printf ("Can't print a NULL desc!\n"); printf ("Can't print a NULL desc!\n");
...@@ -55,214 +57,181 @@ void PrintStreamDesc (AudioStreamBasicDescription *inDesc) ...@@ -55,214 +57,181 @@ void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
} }
#endif #endif
OSStatus AudioFileManager::FileInputProc (void *inRefCon,
AudioUnitRenderActionFlags inActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
AudioBuffer *ioData)
{
AudioFileManager* THIS = (AudioFileManager*)inRefCon;
return THIS->Render(*ioData);
}
OSStatus AudioFileManager::Render (AudioBuffer &ioData)
{
OSStatus result = noErr;
if (mBufferOffset >= mBufferSize) {
result = GetFileData(&mTmpBuffer, &mBufferSize);
if (result) {
SDL_SetError ("AudioConverterFillBuffer:%ld\n", result);
mParent.DoNotification (result);
return result;
}
mBufferOffset = 0;
}
if (ioData.mDataByteSize > mBufferSize - mBufferOffset)
ioData.mDataByteSize = mBufferSize - mBufferOffset;
ioData.mData = (char *)mTmpBuffer + mBufferOffset;
mBufferOffset += ioData.mDataByteSize;
mByteCounter += ioData.mDataByteSize;
AfterRender();
return result;
}
AudioFileManager::~AudioFileManager () static int AudioFilePlayer_SetDestination (AudioFilePlayer *afp, AudioUnit *inDestUnit)
{ {
if (mFileBuffer) { //if (afp->mConnected) throw static_cast<OSStatus>(-1); //can't set dest if already engaged
free (mFileBuffer); if (afp->mConnected)
mFileBuffer = 0; return 0 ;
}
}
AudioFilePlayer::AudioFilePlayer (const FSRef *inFileRef) memcpy(&afp->mPlayUnit, inDestUnit, sizeof (afp->mPlayUnit));
: mConnected (false),
mAudioFileManager (0),
mNotifier (0),
mStartFrame (0)
{
SInt64 fileDataSize = 0;
OpenFile (inFileRef, fileDataSize);
// we want about 4 seconds worth of data for the buffer
int bytesPerSecond = UInt32 (4 * mFileDescription.mSampleRate * mFileDescription.mBytesPerFrame);
#if DEBUG
printf("File format:\n");
PrintStreamDesc (&mFileDescription);
#endif
mAudioFileManager = new AudioFileManager (*this,
mForkRefNum,
fileDataSize,
bytesPerSecond);
}
void AudioFilePlayer::SetDestination (AudioUnit &inDestUnit)
{
if (mConnected) throw static_cast<OSStatus>(-1); //can't set dest if already engaged
mPlayUnit = inDestUnit;
OSStatus result = noErr; OSStatus result = noErr;
//we can "down" cast a component instance to a component //we can "down" cast a component instance to a component
ComponentDescription desc; ComponentDescription desc;
result = GetComponentInfo ((Component)inDestUnit, &desc, 0, 0, 0); result = GetComponentInfo ((Component)*inDestUnit, &desc, 0, 0, 0);
THROW_RESULT("GetComponentInfo") if (result) return 0; //THROW_RESULT("GetComponentInfo")
// we're going to use this to know which convert routine to call // we're going to use this to know which convert routine to call
// a v1 audio unit will have a type of 'aunt' // a v1 audio unit will have a type of 'aunt'
// a v2 audio unit will have one of several different types. // a v2 audio unit will have one of several different types.
if (desc.componentType != kAudioUnitComponentType) { if (desc.componentType != kAudioUnitComponentType) {
result = badComponentInstance; result = badComponentInstance;
THROW_RESULT("BAD COMPONENT") //THROW_RESULT("BAD COMPONENT")
if (result) return 0;
} }
/* Set the input format of the audio unit. */ /* Set the input format of the audio unit. */
result = AudioUnitSetProperty (inDestUnit, result = AudioUnitSetProperty (*inDestUnit,
kAudioUnitProperty_StreamFormat, kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input, kAudioUnitScope_Input,
0, 0,
&mFileDescription, &afp->mFileDescription,
sizeof (mFileDescription)); sizeof (afp->mFileDescription));
THROW_RESULT("AudioUnitSetProperty") //THROW_RESULT("AudioUnitSetProperty")
if (result) return 0;
return 1;
}
static void AudioFilePlayer_SetNotifier(AudioFilePlayer *afp, AudioFilePlayNotifier inNotifier, void *inRefCon)
{
afp->mNotifier = inNotifier;
afp->mRefCon = inRefCon;
}
static int AudioFilePlayer_IsConnected(AudioFilePlayer *afp)
{
return afp->mConnected;
}
static AudioUnit AudioFilePlayer_GetDestUnit(AudioFilePlayer *afp)
{
return afp->mPlayUnit;
}
static void AudioFilePlayer_Print(AudioFilePlayer *afp)
{
#if DEBUG
printf ("Is Connected:%s\n", (IsConnected() ? "true" : "false"));
printf ("- - - - - - - - - - - - - - \n");
#endif
} }
void AudioFilePlayer::SetStartFrame (int frame) static void AudioFilePlayer_SetStartFrame (AudioFilePlayer *afp, int frame)
{ {
SInt64 position = frame * 2352; SInt64 position = frame * 2352;
mStartFrame = frame; afp->mStartFrame = frame;
mAudioFileManager->SetPosition (position); afp->mAudioFileManager->SetPosition (afp->mAudioFileManager, position);
} }
int AudioFilePlayer::GetCurrentFrame () static int AudioFilePlayer_GetCurrentFrame (AudioFilePlayer *afp)
{ {
return mStartFrame + (mAudioFileManager->GetByteCounter() / 2352); return afp->mStartFrame + (afp->mAudioFileManager->GetByteCounter(afp->mAudioFileManager) / 2352);
} }
void AudioFilePlayer::SetStopFrame (int frame) static void AudioFilePlayer_SetStopFrame (AudioFilePlayer *afp, int frame)
{ {
SInt64 position = frame * 2352; SInt64 position = frame * 2352;
mAudioFileManager->SetEndOfFile (position); afp->mAudioFileManager->SetEndOfFile (afp->mAudioFileManager, position);
} }
AudioFilePlayer::~AudioFilePlayer() void delete_AudioFilePlayer(AudioFilePlayer *afp)
{ {
Disconnect(); if (afp != NULL)
{
afp->Disconnect(afp);
if (mAudioFileManager) { if (afp->mAudioFileManager) {
delete mAudioFileManager; delete_AudioFileManager(afp->mAudioFileManager);
mAudioFileManager = 0; afp->mAudioFileManager = 0;
} }
if (mForkRefNum) { if (afp->mForkRefNum) {
FSClose (mForkRefNum); FSClose (afp->mForkRefNum);
mForkRefNum = 0; afp->mForkRefNum = 0;
}
free(afp);
} }
} }
void AudioFilePlayer::Connect() static int AudioFilePlayer_Connect(AudioFilePlayer *afp)
{ {
#if DEBUG #if DEBUG
printf ("Connect:%x, engaged=%d\n", (int)mPlayUnit, (mConnected ? 1 : 0)); printf ("Connect:%x, engaged=%d\n", (int)afp->mPlayUnit, (afp->mConnected ? 1 : 0));
#endif #endif
if (!mConnected) if (!afp->mConnected)
{ {
mAudioFileManager->DoConnect(); if (!afp->mAudioFileManager->DoConnect(afp->mAudioFileManager))
return 0;
// set the render callback for the file data to be supplied to the sound converter AU // set the render callback for the file data to be supplied to the sound converter AU
mInputCallback.inputProc = AudioFileManager::FileInputProc; afp->mInputCallback.inputProc = afp->mAudioFileManager->FileInputProc;
mInputCallback.inputProcRefCon = mAudioFileManager; afp->mInputCallback.inputProcRefCon = afp->mAudioFileManager;
OSStatus result = AudioUnitSetProperty (mPlayUnit, OSStatus result = AudioUnitSetProperty (afp->mPlayUnit,
kAudioUnitProperty_SetInputCallback, kAudioUnitProperty_SetInputCallback,
kAudioUnitScope_Input, kAudioUnitScope_Input,
0, 0,
&mInputCallback, &afp->mInputCallback,
sizeof(mInputCallback)); sizeof(afp->mInputCallback));
THROW_RESULT("AudioUnitSetProperty") if (result) return 0; //THROW_RESULT("AudioUnitSetProperty")
mConnected = true; afp->mConnected = 1;
} }
return 1;
} }
// warning noted, now please go away ;-) // warning noted, now please go away ;-)
// #warning This should redirect the calling of notification code to some other thread // #warning This should redirect the calling of notification code to some other thread
void AudioFilePlayer::DoNotification (OSStatus inStatus) const static void AudioFilePlayer_DoNotification (AudioFilePlayer *afp, OSStatus inStatus)
{ {
AudioFilePlayer* THIS = const_cast<AudioFilePlayer*>(this); if (afp->mNotifier) {
(*afp->mNotifier) (afp->mRefCon, inStatus);
if (mNotifier) {
(*mNotifier) (mRefCon, inStatus);
} else { } else {
SDL_SetError ("Notification posted with no notifier in place"); SDL_SetError ("Notification posted with no notifier in place");
if (inStatus == kAudioFilePlay_FileIsFinished) if (inStatus == kAudioFilePlay_FileIsFinished)
THIS->Disconnect(); afp->Disconnect(afp);
else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun) else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun)
THIS->Disconnect(); afp->Disconnect(afp);
} }
} }
void AudioFilePlayer::Disconnect () static void AudioFilePlayer_Disconnect (AudioFilePlayer *afp)
{ {
#if DEBUG #if DEBUG
printf ("Disconnect:%x,%ld, engaged=%d\n", (int)mPlayUnit, 0, (mConnected ? 1 : 0)); printf ("Disconnect:%x,%ld, engaged=%d\n", (int)afp->mPlayUnit, 0, (afp->mConnected ? 1 : 0));
#endif #endif
if (mConnected) if (afp->mConnected)
{ {
mConnected = false; afp->mConnected = 0;
mInputCallback.inputProc = 0; afp->mInputCallback.inputProc = 0;
mInputCallback.inputProcRefCon = 0; afp->mInputCallback.inputProcRefCon = 0;
OSStatus result = AudioUnitSetProperty (mPlayUnit, OSStatus result = AudioUnitSetProperty (afp->mPlayUnit,
kAudioUnitProperty_SetInputCallback, kAudioUnitProperty_SetInputCallback,
kAudioUnitScope_Input, kAudioUnitScope_Input,
0, 0,
&mInputCallback, &afp->mInputCallback,
sizeof(mInputCallback)); sizeof(afp->mInputCallback));
if (result) if (result)
SDL_SetError ("AudioUnitSetProperty:RemoveInputCallback:%ld", result); SDL_SetError ("AudioUnitSetProperty:RemoveInputCallback:%ld", result);
mAudioFileManager->Disconnect(); afp->mAudioFileManager->Disconnect(afp->mAudioFileManager);
} }
} }
struct SSNDData { typedef struct {
UInt32 offset; UInt32 offset;
UInt32 blockSize; UInt32 blockSize;
}; } SSNDData;
void AudioFilePlayer::OpenFile (const FSRef *inRef, SInt64& outFileDataSize) static int AudioFilePlayer_OpenFile (AudioFilePlayer *afp, const FSRef *inRef, SInt64 *outFileDataSize)
{ {
ContainerChunk chunkHeader; ContainerChunk chunkHeader;
ChunkHeader chunk; ChunkHeader chunk;
...@@ -275,23 +244,23 @@ void AudioFilePlayer::OpenFile (const FSRef *inRef, SInt64& outFileDataSize) ...@@ -275,23 +244,23 @@ void AudioFilePlayer::OpenFile (const FSRef *inRef, SInt64& outFileDataSize)
// Open the data fork of the input file // Open the data fork of the input file
result = FSGetDataForkName(&dfName); result = FSGetDataForkName(&dfName);
THROW_RESULT("AudioFilePlayer::OpenFile(): FSGetDataForkName") if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSGetDataForkName")
result = FSOpenFork(inRef, dfName.length, dfName.unicode, fsRdPerm, &mForkRefNum); result = FSOpenFork(inRef, dfName.length, dfName.unicode, fsRdPerm, &afp->mForkRefNum);
THROW_RESULT("AudioFilePlayer::OpenFile(): FSOpenFork") if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSOpenFork")
// Read the file header, and check if it's indeed an AIFC file // Read the file header, and check if it's indeed an AIFC file
result = FSReadFork(mForkRefNum, fsAtMark, 0, sizeof(chunkHeader), &chunkHeader, &actual); result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(chunkHeader), &chunkHeader, &actual);
THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork") if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")
if (chunkHeader.ckID != 'FORM') { if (chunkHeader.ckID != 'FORM') {
result = -1; result = -1;
THROW_RESULT("AudioFilePlayer::OpenFile(): chunk id is not 'FORM'"); if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): chunk id is not 'FORM'");
} }
if (chunkHeader.formType != 'AIFC') { if (chunkHeader.formType != 'AIFC') {
result = -1; result = -1;
THROW_RESULT("AudioFilePlayer::OpenFile(): file format is not 'AIFC'"); if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): file format is not 'AIFC'");
} }
// Search for the SSND chunk. We ignore all compression etc. information // Search for the SSND chunk. We ignore all compression etc. information
...@@ -300,8 +269,8 @@ void AudioFilePlayer::OpenFile (const FSRef *inRef, SInt64& outFileDataSize) ...@@ -300,8 +269,8 @@ void AudioFilePlayer::OpenFile (const FSRef *inRef, SInt64& outFileDataSize)
// TODO: Parse the COMM chunk we currently skip to fill in mFileDescription. // TODO: Parse the COMM chunk we currently skip to fill in mFileDescription.
offset = 0; offset = 0;
do { do {
result = FSReadFork(mForkRefNum, fsFromMark, offset, sizeof(chunk), &chunk, &actual); result = FSReadFork(afp->mForkRefNum, fsFromMark, offset, sizeof(chunk), &chunk, &actual);
THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork") if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")
// Skip the chunk data // Skip the chunk data
offset = chunk.ckSize; offset = chunk.ckSize;
...@@ -309,22 +278,75 @@ void AudioFilePlayer::OpenFile (const FSRef *inRef, SInt64& outFileDataSize) ...@@ -309,22 +278,75 @@ void AudioFilePlayer::OpenFile (const FSRef *inRef, SInt64& outFileDataSize)
// Read the header of the SSND chunk. After this, we are positioned right // Read the header of the SSND chunk. After this, we are positioned right
// at the start of the audio data. // at the start of the audio data.
result = FSReadFork(mForkRefNum, fsAtMark, 0, sizeof(ssndData), &ssndData, &actual); result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(ssndData), &ssndData, &actual);
THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork") if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")
result = FSSetForkPosition(mForkRefNum, fsFromMark, ssndData.offset); result = FSSetForkPosition(afp->mForkRefNum, fsFromMark, ssndData.offset);
THROW_RESULT("AudioFilePlayer::OpenFile(): FSSetForkPosition") if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSSetForkPosition")
// Data size // Data size
outFileDataSize = chunk.ckSize - ssndData.offset - 8; *outFileDataSize = chunk.ckSize - ssndData.offset - 8;
// File format // File format
mFileDescription.mSampleRate = 44100; afp->mFileDescription.mSampleRate = 44100;
mFileDescription.mFormatID = kAudioFormatLinearPCM; afp->mFileDescription.mFormatID = kAudioFormatLinearPCM;
mFileDescription.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger; afp->mFileDescription.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger;
mFileDescription.mBytesPerPacket = 4; afp->mFileDescription.mBytesPerPacket = 4;
mFileDescription.mFramesPerPacket = 1; afp->mFileDescription.mFramesPerPacket = 1;
mFileDescription.mBytesPerFrame = 4; afp->mFileDescription.mBytesPerFrame = 4;
mFileDescription.mChannelsPerFrame = 2; afp->mFileDescription.mChannelsPerFrame = 2;
mFileDescription.mBitsPerChannel = 16; afp->mFileDescription.mBitsPerChannel = 16;
return 1;
} }
AudioFilePlayer *new_AudioFilePlayer (const FSRef *inFileRef)
{
SInt64 fileDataSize = 0;
AudioFilePlayer *afp = (AudioFilePlayer *) malloc(sizeof (AudioFilePlayer));
if (afp == NULL)
return NULL;
memset(afp, '\0', sizeof (*afp));
#define SET_AUDIOFILEPLAYER_METHOD(m) afp->m = AudioFilePlayer_##m
SET_AUDIOFILEPLAYER_METHOD(SetDestination);
SET_AUDIOFILEPLAYER_METHOD(SetNotifier);
SET_AUDIOFILEPLAYER_METHOD(SetStartFrame);
SET_AUDIOFILEPLAYER_METHOD(GetCurrentFrame);
SET_AUDIOFILEPLAYER_METHOD(SetStopFrame);
SET_AUDIOFILEPLAYER_METHOD(Connect);
SET_AUDIOFILEPLAYER_METHOD(Disconnect);
SET_AUDIOFILEPLAYER_METHOD(DoNotification);
SET_AUDIOFILEPLAYER_METHOD(IsConnected);
SET_AUDIOFILEPLAYER_METHOD(GetDestUnit);
SET_AUDIOFILEPLAYER_METHOD(Print);
SET_AUDIOFILEPLAYER_METHOD(OpenFile);
#undef SET_AUDIOFILEPLAYER_METHOD
if (!afp->OpenFile (afp, inFileRef, &fileDataSize))
{
free(afp);
return NULL;
}
// we want about 4 seconds worth of data for the buffer
int bytesPerSecond = (UInt32) (4 * afp->mFileDescription.mSampleRate * afp->mFileDescription.mBytesPerFrame);
#if DEBUG
printf("File format:\n");
PrintStreamDesc (&afp->mFileDescription);
#endif
afp->mAudioFileManager = new_AudioFileManager(afp, afp->mForkRefNum,
fileDataSize,
bytesPerSecond);
if (afp->mAudioFileManager == NULL)
{
delete_AudioFilePlayer(afp);
return NULL;
}
return afp;
}
...@@ -37,12 +37,14 @@ ...@@ -37,12 +37,14 @@
const char* AudioFilePlayerErrorStr (OSStatus error); const char* AudioFilePlayerErrorStr (OSStatus error);
/*
void ThrowResult (OSStatus result, const char *str); void ThrowResult (OSStatus result, const char *str);
#define THROW_RESULT(str) \ #define THROW_RESULT(str) \
if (result) { \ if (result) { \
ThrowResult (result, str); \ ThrowResult (result, str); \
} }
*/
typedef void (*AudioFilePlayNotifier)(void *inRefCon, typedef void (*AudioFilePlayNotifier)(void *inRefCon,
OSStatus inStatus); OSStatus inStatus);
...@@ -54,49 +56,25 @@ enum { ...@@ -54,49 +56,25 @@ enum {
}; };
class AudioFileManager; struct S_AudioFileManager;
#pragma mark __________ AudioFilePlayer #pragma mark __________ AudioFilePlayer
class AudioFilePlayer typedef struct S_AudioFilePlayer
{ {
public: //public:
AudioFilePlayer (const FSRef *inFileRef); int (*SetDestination)(struct S_AudioFilePlayer *afp, AudioUnit *inDestUnit);
void (*SetNotifier)(struct S_AudioFilePlayer *afp, AudioFilePlayNotifier inNotifier, void *inRefCon);
~AudioFilePlayer(); void (*SetStartFrame)(struct S_AudioFilePlayer *afp, int frame); // seek in the file
int (*GetCurrentFrame)(struct S_AudioFilePlayer *afp); // get the current frame position
void SetDestination (AudioUnit &inDestUnit); void (*SetStopFrame)(struct S_AudioFilePlayer *afp, int frame); // set limit in the file
int (*Connect)(struct S_AudioFilePlayer *afp);
void SetNotifier (AudioFilePlayNotifier inNotifier, void *inRefCon) void (*Disconnect)(struct S_AudioFilePlayer *afp);
{ void (*DoNotification)(struct S_AudioFilePlayer *afp, OSStatus inError);
mNotifier = inNotifier; int (*IsConnected)(struct S_AudioFilePlayer *afp);
mRefCon = inRefCon; AudioUnit (*GetDestUnit)(struct S_AudioFilePlayer *afp);
} void (*Print)(struct S_AudioFilePlayer *afp);
void SetStartFrame (int frame); // seek in the file //private:
int GetCurrentFrame (); // get the current frame position
void SetStopFrame (int frame); // set limit in the file
void Connect();
void Disconnect();
void DoNotification (OSStatus inError) const;
bool IsConnected () const { return mConnected; }
AudioUnit GetDestUnit () const { return mPlayUnit; }
#if DEBUG
void Print() const
{
printf ("Is Connected:%s\n", (IsConnected() ? "true" : "false"));
printf ("- - - - - - - - - - - - - - \n");
}
#endif
private:
AudioUnit mPlayUnit; AudioUnit mPlayUnit;
SInt16 mForkRefNum; SInt16 mForkRefNum;
...@@ -104,9 +82,9 @@ private: ...@@ -104,9 +82,9 @@ private:
AudioStreamBasicDescription mFileDescription; AudioStreamBasicDescription mFileDescription;
bool mConnected; int mConnected;
AudioFileManager* mAudioFileManager; struct S_AudioFileManager* mAudioFileManager;
AudioFilePlayNotifier mNotifier; AudioFilePlayNotifier mNotifier;
void* mRefCon; void* mRefCon;
...@@ -115,41 +93,32 @@ private: ...@@ -115,41 +93,32 @@ private:
#pragma mark __________ Private_Methods #pragma mark __________ Private_Methods
void OpenFile (const FSRef *inRef, SInt64& outFileSize); int (*OpenFile)(struct S_AudioFilePlayer *afp, const FSRef *inRef, SInt64 *outFileSize);
}; } AudioFilePlayer;
AudioFilePlayer *new_AudioFilePlayer(const FSRef *inFileRef);
void delete_AudioFilePlayer(AudioFilePlayer *afp);
#pragma mark __________ AudioFileManager #pragma mark __________ AudioFileManager
class AudioFileManager typedef struct S_AudioFileManager
{ {
public: //public:
AudioFileManager (AudioFilePlayer &inParent,
SInt16 inForkRefNum,
SInt64 inFileLength,
UInt32 inChunkSize);
~AudioFileManager();
// this method should NOT be called by an object of this class // this method should NOT be called by an object of this class
// as it is called by the parent's Disconnect() method // as it is called by the parent's Disconnect() method
void Disconnect (); void (*Disconnect)(struct S_AudioFileManager *afm);
int (*DoConnect)(struct S_AudioFileManager *afm);
void DoConnect (); OSStatus (*Read)(struct S_AudioFileManager *afm, char *buffer, UInt32 *len);
const char* (*GetFileBuffer)(struct S_AudioFileManager *afm);
OSStatus Read(char *buffer, UInt32 *len); const AudioFilePlayer *(*GetParent)(struct S_AudioFileManager *afm);
void (*SetPosition)(struct S_AudioFileManager *afm, SInt64 pos); // seek/rewind in the file
const char* GetFileBuffer () { return mFileBuffer; } int (*GetByteCounter)(struct S_AudioFileManager *afm); // return actual bytes streamed to audio hardware
void (*SetEndOfFile)(struct S_AudioFileManager *afm, SInt64 pos); // set the "EOF" (will behave just like it reached eof)
const AudioFilePlayer& GetParent () const { return mParent; }
void SetPosition (SInt64 pos); // seek/rewind in the file
int GetByteCounter () { return mByteCounter; } // return actual bytes streamed to audio hardware
void SetEndOfFile (SInt64 pos); // set the "EOF" (will behave just like it reached eof)
protected: //protected:
AudioFilePlayer& mParent; AudioFilePlayer* mParent;
SInt16 mForkRefNum; SInt16 mForkRefNum;
SInt64 mAudioDataOffset; SInt64 mAudioDataOffset;
...@@ -157,9 +126,9 @@ protected: ...@@ -157,9 +126,9 @@ protected:
int mByteCounter; int mByteCounter;
bool mReadFromFirstBuffer; int mReadFromFirstBuffer;
bool mLockUnsuccessful; int mLockUnsuccessful;
bool mIsEngaged; int mIsEngaged;
int mNumTimesAskedSinceFinished; int mNumTimesAskedSinceFinished;
...@@ -167,27 +136,34 @@ protected: ...@@ -167,27 +136,34 @@ protected:
void* mTmpBuffer; void* mTmpBuffer;
UInt32 mBufferSize; UInt32 mBufferSize;
UInt32 mBufferOffset; UInt32 mBufferOffset;
public: //public:
const UInt32 mChunkSize; UInt32 mChunkSize;
SInt64 mFileLength; SInt64 mFileLength;
SInt64 mReadFilePosition; SInt64 mReadFilePosition;
bool mWriteToFirstBuffer; int mWriteToFirstBuffer;
bool mFinishedReadingData; int mFinishedReadingData;
protected: //protected:
OSStatus Render (AudioBuffer &ioData); OSStatus (*Render)(struct S_AudioFileManager *afm, AudioBuffer *ioData);
OSStatus (*GetFileData)(struct S_AudioFileManager *afm, void** inOutData, UInt32 *inOutDataSize);
OSStatus GetFileData (void** inOutData, UInt32 *inOutDataSize); void (*AfterRender)(struct S_AudioFileManager *afm);
void AfterRender (); //public:
//static
public: OSStatus (*FileInputProc)(void *inRefCon,
static OSStatus FileInputProc (void *inRefCon, AudioUnitRenderActionFlags inActionFlags,
AudioUnitRenderActionFlags inActionFlags, const AudioTimeStamp *inTimeStamp,
const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber,
UInt32 inBusNumber, AudioBuffer *ioData);
AudioBuffer *ioData); } AudioFileManager;
};
AudioFileManager *new_AudioFileManager (AudioFilePlayer *inParent,
SInt16 inForkRefNum,
SInt64 inFileLength,
UInt32 inChunkSize);
void delete_AudioFileManager(AudioFileManager *afm);
#endif #endif
...@@ -28,105 +28,142 @@ ...@@ -28,105 +28,142 @@
// //
#include "AudioFilePlayer.h" #include "AudioFilePlayer.h"
#include <mach/mach.h> //used for setting policy of thread #include <mach/mach.h> //used for setting policy of thread
#include "CAGuard.h" #include "SDLOSXCAGuard.h"
#include <pthread.h> #include <pthread.h>
#include <list> //#include <list>
class FileReaderThread { //typedef void *FileData;
public: typedef struct S_FileData
FileReaderThread (); {
AudioFileManager *obj;
struct S_FileData *next;
} FileData;
CAGuard& GetGuard() { return mGuard; }
void AddReader();
void RemoveReader (AudioFileManager* inItem);
// returns true if succeeded
bool TryNextRead (AudioFileManager* inItem)
{
bool didLock = false;
bool succeeded = false;
if (mGuard.Try (didLock))
{
mFileData.push_back (inItem);
mGuard.Notify();
succeeded = true;
if (didLock) typedef struct S_FileReaderThread {
mGuard.Unlock(); //public:
} SDLOSXCAGuard* (*GetGuard)(struct S_FileReaderThread *frt);
void (*AddReader)(struct S_FileReaderThread *frt);
return succeeded; void (*RemoveReader)(struct S_FileReaderThread *frt, AudioFileManager* inItem);
} int (*TryNextRead)(struct S_FileReaderThread *frt, AudioFileManager* inItem);
int mThreadShouldDie; int mThreadShouldDie;
private: //private:
typedef std::list<AudioFileManager*> FileData; //typedef std::list<AudioFileManager*> FileData;
CAGuard mGuard; SDLOSXCAGuard *mGuard;
UInt32 mThreadPriority; UInt32 mThreadPriority;
int mNumReaders; int mNumReaders;
FileData mFileData; FileData *mFileData;
void ReadNextChunk (); void (*ReadNextChunk)(struct S_FileReaderThread *frt);
int (*StartFixedPriorityThread)(struct S_FileReaderThread *frt);
void StartFixedPriorityThread (); //static
static UInt32 GetThreadBasePriority (pthread_t inThread); UInt32 (*GetThreadBasePriority)(pthread_t inThread);
//static
static void* DiskReaderEntry (void *inRefCon); void* (*DiskReaderEntry)(void *inRefCon);
}; } FileReaderThread;
FileReaderThread::FileReaderThread ()
: mThreadPriority (62), static SDLOSXCAGuard* FileReaderThread_GetGuard(FileReaderThread *frt)
mNumReaders (0)
{ {
return frt->mGuard;
} }
void FileReaderThread::AddReader() // returns 1 if succeeded
static int FileReaderThread_TryNextRead (FileReaderThread *frt, AudioFileManager* inItem)
{ {
if (mNumReaders == 0) int didLock = 0;
int succeeded = 0;
if (frt->mGuard->Try(frt->mGuard, &didLock))
{ {
mThreadShouldDie = false; //frt->mFileData.push_back (inItem);
// !!! FIXME: this could be faster with a "tail" member. --ryan.
StartFixedPriorityThread (); FileData *i = frt->mFileData;
FileData *prev = NULL;
FileData *newfd = (FileData *) malloc(sizeof (FileData));
newfd->obj = inItem;
newfd->next = NULL;
while (i != NULL) { prev = i; i = i->next; }
if (prev == NULL)
frt->mFileData = newfd;
else
prev->next = newfd;
frt->mGuard->Notify(frt->mGuard);
succeeded = 1;
if (didLock)
frt->mGuard->Unlock(frt->mGuard);
} }
mNumReaders++;
return succeeded;
} }
void FileReaderThread::RemoveReader (AudioFileManager* inItem) static void FileReaderThread_AddReader(FileReaderThread *frt)
{ {
if (mNumReaders > 0) if (frt->mNumReaders == 0)
{ {
CAGuard::Locker fileReadLock (mGuard); frt->mThreadShouldDie = 0;
frt->StartFixedPriorityThread (frt);
mFileData.remove (inItem); }
frt->mNumReaders++;
}
static void FileReaderThread_RemoveReader (FileReaderThread *frt, AudioFileManager* inItem)
{
if (frt->mNumReaders > 0)
{
int bNeedsRelease = frt->mGuard->Lock(frt->mGuard);
if (--mNumReaders == 0) { //frt->mFileData.remove (inItem);
mThreadShouldDie = true; FileData *i = frt->mFileData;
mGuard.Notify(); // wake up thread so it will quit FileData *prev = NULL;
mGuard.Wait(); // wait for thread to die while (i != NULL)
{
FileData *next = i->next;
if (i->obj != inItem)
prev = i;
else
{
if (prev == NULL)
frt->mFileData = next;
else
prev->next = next;
free(i);
}
i = next;
}
if (--frt->mNumReaders == 0) {
frt->mThreadShouldDie = 1;
frt->mGuard->Notify(frt->mGuard); // wake up thread so it will quit
frt->mGuard->Wait(frt->mGuard); // wait for thread to die
} }
if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
} }
} }
void FileReaderThread::StartFixedPriorityThread () static int FileReaderThread_StartFixedPriorityThread (FileReaderThread *frt)
{ {
pthread_attr_t theThreadAttrs; pthread_attr_t theThreadAttrs;
pthread_t pThread; pthread_t pThread;
OSStatus result = pthread_attr_init(&theThreadAttrs); OSStatus result = pthread_attr_init(&theThreadAttrs);
THROW_RESULT("pthread_attr_init - Thread attributes could not be created.") if (result) return 0; //THROW_RESULT("pthread_attr_init - Thread attributes could not be created.")
result = pthread_attr_setdetachstate(&theThreadAttrs, PTHREAD_CREATE_DETACHED); result = pthread_attr_setdetachstate(&theThreadAttrs, PTHREAD_CREATE_DETACHED);
THROW_RESULT("pthread_attr_setdetachstate - Thread attributes could not be detached.") if (result) return 0; //THROW_RESULT("pthread_attr_setdetachstate - Thread attributes could not be detached.")
result = pthread_create (&pThread, &theThreadAttrs, DiskReaderEntry, this); result = pthread_create (&pThread, &theThreadAttrs, frt->DiskReaderEntry, frt);
THROW_RESULT("pthread_create - Create and start the thread.") if (result) return 0; //THROW_RESULT("pthread_create - Create and start the thread.")
pthread_attr_destroy(&theThreadAttrs); pthread_attr_destroy(&theThreadAttrs);
...@@ -138,19 +175,21 @@ void FileReaderThread::StartFixedPriorityThread () ...@@ -138,19 +175,21 @@ void FileReaderThread::StartFixedPriorityThread ()
SInt32 relativePriority; SInt32 relativePriority;
// make thread fixed // make thread fixed
theFixedPolicy.timeshare = false; // set to true for a non-fixed thread theFixedPolicy.timeshare = 0; // set to 1 for a non-fixed thread
result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT); result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT);
THROW_RESULT("thread_policy - Couldn't set thread as fixed priority.") if (result) return 0; //THROW_RESULT("thread_policy - Couldn't set thread as fixed priority.")
// set priority // set priority
// precedency policy's "importance" value is relative to spawning thread's priority // precedency policy's "importance" value is relative to spawning thread's priority
relativePriority = mThreadPriority - FileReaderThread::GetThreadBasePriority (pthread_self()); relativePriority = frt->mThreadPriority - frt->GetThreadBasePriority(pthread_self());
thePrecedencePolicy.importance = relativePriority; thePrecedencePolicy.importance = relativePriority;
result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_PRECEDENCE_POLICY, (thread_policy_t)&thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT); result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_PRECEDENCE_POLICY, (thread_policy_t)&thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT);
THROW_RESULT("thread_policy - Couldn't set thread priority.") if (result) return 0; //THROW_RESULT("thread_policy - Couldn't set thread priority.")
return 1;
} }
UInt32 FileReaderThread::GetThreadBasePriority (pthread_t inThread) static UInt32 FileReaderThread_GetThreadBasePriority (pthread_t inThread)
{ {
thread_basic_info_data_t threadInfo; thread_basic_info_data_t threadInfo;
policy_info_data_t thePolicyInfo; policy_info_data_t thePolicyInfo;
...@@ -191,10 +230,10 @@ UInt32 FileReaderThread::GetThreadBasePriority (pthread_t inThread) ...@@ -191,10 +230,10 @@ UInt32 FileReaderThread::GetThreadBasePriority (pthread_t inThread)
return 0; return 0;
} }
void *FileReaderThread::DiskReaderEntry (void *inRefCon) static void *FileReaderThread_DiskReaderEntry (void *inRefCon)
{ {
FileReaderThread *This = (FileReaderThread *)inRefCon; FileReaderThread *frt = (FileReaderThread *)inRefCon;
This->ReadNextChunk(); frt->ReadNextChunk(frt);
#if DEBUG #if DEBUG
printf ("finished with reading file\n"); printf ("finished with reading file\n");
#endif #endif
...@@ -202,7 +241,7 @@ void *FileReaderThread::DiskReaderEntry (void *inRefCon) ...@@ -202,7 +241,7 @@ void *FileReaderThread::DiskReaderEntry (void *inRefCon)
return 0; return 0;
} }
void FileReaderThread::ReadNextChunk () static void FileReaderThread_ReadNextChunk (FileReaderThread *frt)
{ {
OSStatus result; OSStatus result;
UInt32 dataChunkSize; UInt32 dataChunkSize;
...@@ -211,28 +250,40 @@ void FileReaderThread::ReadNextChunk () ...@@ -211,28 +250,40 @@ void FileReaderThread::ReadNextChunk ()
for (;;) for (;;)
{ {
{ // this is a scoped based lock { // this is a scoped based lock
CAGuard::Locker fileReadLock (mGuard); int bNeedsRelease = frt->mGuard->Lock(frt->mGuard);
if (this->mThreadShouldDie) {
mGuard.Notify(); if (frt->mThreadShouldDie) {
frt->mGuard->Notify(frt->mGuard);
if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
return; return;
} }
if (mFileData.empty()) //if (frt->mFileData.empty())
if (frt->mFileData == NULL)
{ {
mGuard.Wait(); frt->mGuard->Wait(frt->mGuard);
} }
// kill thread // kill thread
if (this->mThreadShouldDie) { if (frt->mThreadShouldDie) {
mGuard.Notify(); frt->mGuard->Notify(frt->mGuard);
if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
return; return;
} }
theItem = mFileData.front(); //theItem = frt->mFileData.front();
mFileData.pop_front(); //frt->mFileData.pop_front();
theItem = NULL;
if (frt->mFileData != NULL)
{
FileData *next = frt->mFileData->next;
theItem = frt->mFileData->obj;
free(frt->mFileData);
frt->mFileData = next;
}
if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
} }
if ((theItem->mFileLength - theItem->mReadFilePosition) < theItem->mChunkSize) if ((theItem->mFileLength - theItem->mReadFilePosition) < theItem->mChunkSize)
...@@ -242,17 +293,18 @@ void FileReaderThread::ReadNextChunk () ...@@ -242,17 +293,18 @@ void FileReaderThread::ReadNextChunk ()
// this is the exit condition for the thread // this is the exit condition for the thread
if (dataChunkSize <= 0) { if (dataChunkSize <= 0) {
theItem->mFinishedReadingData = true; theItem->mFinishedReadingData = 1;
continue; continue;
} }
// construct pointer // construct pointer
char* writePtr = const_cast<char*>(theItem->GetFileBuffer() + char* writePtr = (char *) (theItem->GetFileBuffer(theItem) +
(theItem->mWriteToFirstBuffer ? 0 : theItem->mChunkSize)); (theItem->mWriteToFirstBuffer ? 0 : theItem->mChunkSize));
// read data // read data
result = theItem->Read(writePtr, &dataChunkSize); result = theItem->Read(theItem, writePtr, &dataChunkSize);
if (result != noErr && result != eofErr) { if (result != noErr && result != eofErr) {
theItem->GetParent().DoNotification(result); AudioFilePlayer *afp = (AudioFilePlayer *) theItem->GetParent(theItem);
afp->DoNotification(afp, result);
continue; continue;
} }
...@@ -273,150 +325,278 @@ void FileReaderThread::ReadNextChunk () ...@@ -273,150 +325,278 @@ void FileReaderThread::ReadNextChunk ()
} }
} }
void delete_FileReaderThread(FileReaderThread *frt)
{
if (frt != NULL)
{
delete_SDLOSXCAGuard(frt->mGuard);
free(frt);
}
}
static FileReaderThread sReaderThread; FileReaderThread *new_FileReaderThread ()
{
AudioFileManager::AudioFileManager (AudioFilePlayer &inParent, FileReaderThread *frt = (FileReaderThread *) malloc(sizeof (FileReaderThread));
SInt16 inForkRefNum, if (frt == NULL)
SInt64 inFileLength, return NULL;
UInt32 inChunkSize) memset(frt, '\0', sizeof (*frt));
: mParent (inParent),
mForkRefNum (inForkRefNum),
mFileBuffer (0),
mByteCounter (0),
mLockUnsuccessful (false),
mIsEngaged (false),
mBufferSize (inChunkSize), frt->mGuard = new_SDLOSXCAGuard();
mBufferOffset (inChunkSize), if (frt->mGuard == NULL)
mChunkSize (inChunkSize), {
mFileLength (inFileLength), free(frt);
mReadFilePosition (0), return NULL;
mWriteToFirstBuffer (false), }
mFinishedReadingData (false)
{ #define SET_FILEREADERTHREAD_METHOD(m) frt->m = FileReaderThread_##m
mFileBuffer = (char*) malloc (mChunkSize * 2); SET_FILEREADERTHREAD_METHOD(GetGuard);
FSGetForkPosition(mForkRefNum, &mAudioDataOffset); SET_FILEREADERTHREAD_METHOD(AddReader);
assert (mFileBuffer != NULL); SET_FILEREADERTHREAD_METHOD(RemoveReader);
SET_FILEREADERTHREAD_METHOD(TryNextRead);
SET_FILEREADERTHREAD_METHOD(ReadNextChunk);
SET_FILEREADERTHREAD_METHOD(StartFixedPriorityThread);
SET_FILEREADERTHREAD_METHOD(GetThreadBasePriority);
SET_FILEREADERTHREAD_METHOD(DiskReaderEntry);
#undef SET_FILEREADERTHREAD_METHOD
frt->mThreadPriority = 62;
return frt;
} }
void AudioFileManager::DoConnect ()
static FileReaderThread *sReaderThread;
static int AudioFileManager_DoConnect (AudioFileManager *afm)
{ {
if (!mIsEngaged) if (!afm->mIsEngaged)
{ {
//mReadFilePosition = 0; //afm->mReadFilePosition = 0;
mFinishedReadingData = false; afm->mFinishedReadingData = 0;
mNumTimesAskedSinceFinished = 0; afm->mNumTimesAskedSinceFinished = 0;
mLockUnsuccessful = false; afm->mLockUnsuccessful = 0;
OSStatus result; OSStatus result;
UInt32 dataChunkSize; UInt32 dataChunkSize;
if ((mFileLength - mReadFilePosition) < mChunkSize) if ((afm->mFileLength - afm->mReadFilePosition) < afm->mChunkSize)
dataChunkSize = mFileLength - mReadFilePosition; dataChunkSize = afm->mFileLength - afm->mReadFilePosition;
else else
dataChunkSize = mChunkSize; dataChunkSize = afm->mChunkSize;
result = Read(mFileBuffer, &dataChunkSize); result = afm->Read(afm, afm->mFileBuffer, &dataChunkSize);
THROW_RESULT("AudioFileManager::DoConnect(): Read") if (result) return 0; //THROW_RESULT("AudioFileManager::DoConnect(): Read")
mReadFilePosition += dataChunkSize; afm->mReadFilePosition += dataChunkSize;
mWriteToFirstBuffer = false; afm->mWriteToFirstBuffer = 0;
mReadFromFirstBuffer = true; afm->mReadFromFirstBuffer = 1;
sReaderThread.AddReader(); sReaderThread->AddReader(sReaderThread);
mIsEngaged = true; afm->mIsEngaged = 1;
} }
else //else
throw static_cast<OSStatus>(-1); //thread has already been started // throw static_cast<OSStatus>(-1); //thread has already been started
return 1;
} }
void AudioFileManager::Disconnect () static void AudioFileManager_Disconnect (AudioFileManager *afm)
{ {
if (mIsEngaged) if (afm->mIsEngaged)
{ {
sReaderThread.RemoveReader (this); sReaderThread->RemoveReader (sReaderThread, afm);
mIsEngaged = false; afm->mIsEngaged = 0;
} }
} }
OSStatus AudioFileManager::Read(char *buffer, UInt32 *len) static OSStatus AudioFileManager_Read(AudioFileManager *afm, char *buffer, UInt32 *len)
{ {
return FSReadFork (mForkRefNum, return FSReadFork (afm->mForkRefNum,
fsFromStart, fsFromStart,
mReadFilePosition + mAudioDataOffset, afm->mReadFilePosition + afm->mAudioDataOffset,
*len, *len,
buffer, buffer,
len); len);
} }
OSStatus AudioFileManager::GetFileData (void** inOutData, UInt32 *inOutDataSize) static OSStatus AudioFileManager_GetFileData (AudioFileManager *afm, void** inOutData, UInt32 *inOutDataSize)
{ {
if (mFinishedReadingData) if (afm->mFinishedReadingData)
{ {
++mNumTimesAskedSinceFinished; ++afm->mNumTimesAskedSinceFinished;
*inOutDataSize = 0; *inOutDataSize = 0;
*inOutData = 0; *inOutData = 0;
return noErr; return noErr;
} }
if (mReadFromFirstBuffer == mWriteToFirstBuffer) { if (afm->mReadFromFirstBuffer == afm->mWriteToFirstBuffer) {
#if DEBUG #if DEBUG
printf ("* * * * * * * Can't keep up with reading file\n"); printf ("* * * * * * * Can't keep up with reading file\n");
#endif #endif
mParent.DoNotification (kAudioFilePlayErr_FilePlayUnderrun); afm->mParent->DoNotification (afm->mParent, kAudioFilePlayErr_FilePlayUnderrun);
*inOutDataSize = 0; *inOutDataSize = 0;
*inOutData = 0; *inOutData = 0;
} else { } else {
*inOutDataSize = mChunkSize; *inOutDataSize = afm->mChunkSize;
*inOutData = mReadFromFirstBuffer ? mFileBuffer : (mFileBuffer + mChunkSize); *inOutData = afm->mReadFromFirstBuffer ? afm->mFileBuffer : (afm->mFileBuffer + afm->mChunkSize);
} }
mLockUnsuccessful = !sReaderThread.TryNextRead (this); afm->mLockUnsuccessful = !sReaderThread->TryNextRead (sReaderThread, afm);
mReadFromFirstBuffer = !mReadFromFirstBuffer; afm->mReadFromFirstBuffer = !afm->mReadFromFirstBuffer;
return noErr; return noErr;
} }
void AudioFileManager::AfterRender () static void AudioFileManager_AfterRender (AudioFileManager *afm)
{ {
if (mNumTimesAskedSinceFinished > 0) if (afm->mNumTimesAskedSinceFinished > 0)
{ {
bool didLock = false; int didLock = 0;
if (sReaderThread.GetGuard().Try (didLock)) { SDLOSXCAGuard *guard = sReaderThread->GetGuard(sReaderThread);
mParent.DoNotification (kAudioFilePlay_FileIsFinished); if (guard->Try(guard, &didLock)) {
afm->mParent->DoNotification (afm->mParent, kAudioFilePlay_FileIsFinished);
if (didLock) if (didLock)
sReaderThread.GetGuard().Unlock(); guard->Unlock(guard);
} }
} }
if (mLockUnsuccessful) if (afm->mLockUnsuccessful)
mLockUnsuccessful = !sReaderThread.TryNextRead (this); afm->mLockUnsuccessful = !sReaderThread->TryNextRead (sReaderThread, afm);
} }
void AudioFileManager::SetPosition (SInt64 pos) static void AudioFileManager_SetPosition (AudioFileManager *afm, SInt64 pos)
{ {
if (pos < 0 || pos >= mFileLength) { if (pos < 0 || pos >= afm->mFileLength) {
SDL_SetError ("AudioFileManager::SetPosition - position invalid: %d filelen=%d\n", SDL_SetError ("AudioFileManager::SetPosition - position invalid: %d filelen=%d\n",
(unsigned int)pos, (unsigned int)mFileLength); (unsigned int)pos, (unsigned int)afm->mFileLength);
pos = 0; pos = 0;
} }
mReadFilePosition = pos; afm->mReadFilePosition = pos;
} }
void AudioFileManager::SetEndOfFile (SInt64 pos) static void AudioFileManager_SetEndOfFile (AudioFileManager *afm, SInt64 pos)
{ {
if (pos <= 0 || pos > mFileLength) { if (pos <= 0 || pos > afm->mFileLength) {
SDL_SetError ("AudioFileManager::SetEndOfFile - position beyond actual eof\n"); SDL_SetError ("AudioFileManager::SetEndOfFile - position beyond actual eof\n");
pos = mFileLength; pos = afm->mFileLength;
} }
mFileLength = pos; afm->mFileLength = pos;
}
static const char *AudioFileManager_GetFileBuffer(AudioFileManager *afm)
{
return afm->mFileBuffer;
}
const AudioFilePlayer *AudioFileManager_GetParent(AudioFileManager *afm)
{
return afm->mParent;
}
static int AudioFileManager_GetByteCounter(AudioFileManager *afm)
{
return afm->mByteCounter;
}
static OSStatus AudioFileManager_FileInputProc (void *inRefCon,
AudioUnitRenderActionFlags inActionFlags,
const AudioTimeStamp *inTimeStamp,
UInt32 inBusNumber,
AudioBuffer *ioData)
{
AudioFileManager* afm = (AudioFileManager*)inRefCon;
return afm->Render(afm, ioData);
} }
static OSStatus AudioFileManager_Render (AudioFileManager *afm, AudioBuffer *ioData)
{
OSStatus result = noErr;
if (afm->mBufferOffset >= afm->mBufferSize) {
result = afm->GetFileData(afm, &afm->mTmpBuffer, &afm->mBufferSize);
if (result) {
SDL_SetError ("AudioConverterFillBuffer:%ld\n", result);
afm->mParent->DoNotification(afm->mParent, result);
return result;
}
afm->mBufferOffset = 0;
}
if (ioData->mDataByteSize > afm->mBufferSize - afm->mBufferOffset)
ioData->mDataByteSize = afm->mBufferSize - afm->mBufferOffset;
ioData->mData = (char *)afm->mTmpBuffer + afm->mBufferOffset;
afm->mBufferOffset += ioData->mDataByteSize;
afm->mByteCounter += ioData->mDataByteSize;
afm->AfterRender(afm);
return result;
}
void delete_AudioFileManager (AudioFileManager *afm)
{
if (afm != NULL) {
if (afm->mFileBuffer) {
free (afm->mFileBuffer);
}
free(afm);
}
}
AudioFileManager *new_AudioFileManager(AudioFilePlayer *inParent,
SInt16 inForkRefNum,
SInt64 inFileLength,
UInt32 inChunkSize)
{
AudioFileManager *afm;
if (sReaderThread == NULL)
{
sReaderThread = new_FileReaderThread();
if (sReaderThread == NULL)
return NULL;
}
afm = (AudioFileManager *) malloc(sizeof (AudioFileManager));
if (afm == NULL)
return NULL;
memset(afm, '\0', sizeof (*afm));
#define SET_AUDIOFILEMANAGER_METHOD(m) afm->m = AudioFileManager_##m
SET_AUDIOFILEMANAGER_METHOD(Disconnect);
SET_AUDIOFILEMANAGER_METHOD(DoConnect);
SET_AUDIOFILEMANAGER_METHOD(Read);
SET_AUDIOFILEMANAGER_METHOD(GetFileBuffer);
SET_AUDIOFILEMANAGER_METHOD(GetParent);
SET_AUDIOFILEMANAGER_METHOD(SetPosition);
SET_AUDIOFILEMANAGER_METHOD(GetByteCounter);
SET_AUDIOFILEMANAGER_METHOD(SetEndOfFile);
SET_AUDIOFILEMANAGER_METHOD(Render);
SET_AUDIOFILEMANAGER_METHOD(GetFileData);
SET_AUDIOFILEMANAGER_METHOD(AfterRender);
SET_AUDIOFILEMANAGER_METHOD(FileInputProc);
#undef SET_AUDIOFILEMANAGER_METHOD
afm->mParent = inParent;
afm->mForkRefNum = inForkRefNum;
afm->mBufferSize = inChunkSize;
afm->mBufferOffset = inChunkSize;
afm->mChunkSize = inChunkSize;
afm->mFileLength = inFileLength;
afm->mFileBuffer = (char*) malloc (afm->mChunkSize * 2);
FSGetForkPosition(afm->mForkRefNum, &afm->mAudioDataOffset);
assert (afm->mFileBuffer != NULL);
return afm;
}
...@@ -22,10 +22,10 @@ ...@@ -22,10 +22,10 @@
#include "CDPlayer.h" #include "CDPlayer.h"
#include "AudioFilePlayer.h" #include "AudioFilePlayer.h"
#include "CAGuard.h" #include "SDLOSXCAGuard.h"
// we're exporting these functions into C land for SDL_syscdrom.c // we're exporting these functions into C land for SDL_syscdrom.c
extern "C" { //extern "C" {
// //
// Constants // Constants
...@@ -52,7 +52,7 @@ extern "C" { ...@@ -52,7 +52,7 @@ extern "C" {
#pragma mark -- Globals -- #pragma mark -- Globals --
static bool playBackWasInit = false; static int playBackWasInit = 0;
static AudioUnit theUnit; static AudioUnit theUnit;
static AudioFilePlayer* thePlayer = NULL; static AudioFilePlayer* thePlayer = NULL;
static CDPlayerCompletionProc completionProc = NULL; static CDPlayerCompletionProc completionProc = NULL;
...@@ -411,38 +411,40 @@ int LoadFile (const FSRef *ref, int startFrame, int stopFrame) ...@@ -411,38 +411,40 @@ int LoadFile (const FSRef *ref, int startFrame, int stopFrame)
printf ("LoadFile: %d %d\n", startFrame, stopFrame); printf ("LoadFile: %d %d\n", startFrame, stopFrame);
#endif #endif
try { //try {
// create a new player, and attach to the audio unit // create a new player, and attach to the audio unit
thePlayer = new AudioFilePlayer(ref); thePlayer = new_AudioFilePlayer(ref);
if (thePlayer == NULL) { if (thePlayer == NULL) {
SDL_SetError ("LoadFile: Could not create player"); SDL_SetError ("LoadFile: Could not create player");
throw (-3); return -3; //throw (-3);
} }
thePlayer->SetDestination(theUnit); if (!thePlayer->SetDestination(thePlayer, &theUnit))
goto bail;
if (startFrame >= 0) if (startFrame >= 0)
thePlayer->SetStartFrame (startFrame); thePlayer->SetStartFrame (thePlayer, startFrame);
if (stopFrame >= 0 && stopFrame > startFrame) if (stopFrame >= 0 && stopFrame > startFrame)
thePlayer->SetStopFrame (stopFrame); thePlayer->SetStopFrame (thePlayer, stopFrame);
// we set the notifier later // we set the notifier later
//thePlayer->SetNotifier(FilePlayNotificationHandler, NULL); //thePlayer->SetNotifier(thePlayer, FilePlayNotificationHandler, NULL);
thePlayer->Connect(); if (!thePlayer->Connect(thePlayer))
goto bail;
#if DEBUG_CDROM #if DEBUG_CDROM
thePlayer->Print(); thePlayer->Print(thePlayer);
fflush (stdout); fflush (stdout);
#endif #endif
} //}
catch (...) //catch (...)
{ //{
goto bail; // goto bail;
} //}
error = 0; error = 0;
...@@ -458,24 +460,25 @@ int ReleaseFile () ...@@ -458,24 +460,25 @@ int ReleaseFile ()
{ {
int error = -1; int error = -1;
try { // (Don't see any way that the original C++ code could throw here.) --ryan.
//try {
if (thePlayer != NULL) { if (thePlayer != NULL) {
thePlayer->Disconnect(); thePlayer->Disconnect(thePlayer);
delete thePlayer; delete_AudioFilePlayer(thePlayer);
thePlayer = NULL; thePlayer = NULL;
} }
} //}
catch (...) //catch (...)
{ //{
goto bail; // goto bail;
} //}
error = 0; error = 0;
bail: // bail:
return error; return error;
} }
...@@ -490,17 +493,17 @@ int PlayFile () ...@@ -490,17 +493,17 @@ int PlayFile ()
if (CheckInit () < 0) if (CheckInit () < 0)
goto bail; goto bail;
try { // try {
// start processing of the audio unit // start processing of the audio unit
result = AudioOutputUnitStart (theUnit); result = AudioOutputUnitStart (theUnit);
THROW_RESULT("PlayFile: AudioOutputUnitStart") if (result) goto bail; //THROW_RESULT("PlayFile: AudioOutputUnitStart")
} // }
catch (...) // catch (...)
{ // {
goto bail; // goto bail;
} // }
result = 0; result = 0;
...@@ -519,16 +522,16 @@ int PauseFile () ...@@ -519,16 +522,16 @@ int PauseFile ()
if (CheckInit () < 0) if (CheckInit () < 0)
goto bail; goto bail;
try { //try {
// stop processing the audio unit // stop processing the audio unit
result = AudioOutputUnitStop (theUnit); result = AudioOutputUnitStop (theUnit);
THROW_RESULT("PauseFile: AudioOutputUnitStop") if (result) goto bail; //THROW_RESULT("PauseFile: AudioOutputUnitStop")
} //}
catch (...) //catch (...)
{ //{
goto bail; // goto bail;
} //}
result = 0; result = 0;
bail: bail:
...@@ -545,7 +548,7 @@ void SetCompletionProc (CDPlayerCompletionProc proc, SDL_CD *cdrom) ...@@ -545,7 +548,7 @@ void SetCompletionProc (CDPlayerCompletionProc proc, SDL_CD *cdrom)
theCDROM = cdrom; theCDROM = cdrom;
completionProc = proc; completionProc = proc;
thePlayer->SetNotifier (FilePlayNotificationHandler, cdrom); thePlayer->SetNotifier (thePlayer, FilePlayNotificationHandler, cdrom);
} }
// //
...@@ -559,7 +562,7 @@ int GetCurrentFrame () ...@@ -559,7 +562,7 @@ int GetCurrentFrame ()
if (thePlayer == NULL) if (thePlayer == NULL)
frame = 0; frame = 0;
else else
frame = thePlayer->GetCurrentFrame (); frame = thePlayer->GetCurrentFrame (thePlayer);
return frame; return frame;
} }
...@@ -580,7 +583,7 @@ static OSStatus CheckInit () ...@@ -580,7 +583,7 @@ static OSStatus CheckInit ()
// Start callback thread // Start callback thread
SDL_CreateThread(RunCallBackThread, NULL); SDL_CreateThread(RunCallBackThread, NULL);
try { { //try {
ComponentDescription desc; ComponentDescription desc;
desc.componentType = kAudioUnitComponentType; desc.componentType = kAudioUnitComponentType;
...@@ -592,23 +595,23 @@ static OSStatus CheckInit () ...@@ -592,23 +595,23 @@ static OSStatus CheckInit ()
Component comp = FindNextComponent (NULL, &desc); Component comp = FindNextComponent (NULL, &desc);
if (comp == NULL) { if (comp == NULL) {
SDL_SetError ("CheckInit: FindNextComponent returned NULL"); SDL_SetError ("CheckInit: FindNextComponent returned NULL");
throw(internalComponentErr); if (result) return -1; //throw(internalComponentErr);
} }
result = OpenAComponent (comp, &theUnit); result = OpenAComponent (comp, &theUnit);
THROW_RESULT("CheckInit: OpenAComponent") if (result) return -1; //THROW_RESULT("CheckInit: OpenAComponent")
// you need to initialize the output unit before you set it as a destination // you need to initialize the output unit before you set it as a destination
result = AudioUnitInitialize (theUnit); result = AudioUnitInitialize (theUnit);
THROW_RESULT("CheckInit: AudioUnitInitialize") if (result) return -1; //THROW_RESULT("CheckInit: AudioUnitInitialize")
playBackWasInit = true; playBackWasInit = true;
} }
catch (...) //catch (...)
{ //{
return -1; // return -1;
} //}
return 0; return 0;
} }
...@@ -657,4 +660,4 @@ static int RunCallBackThread (void *param) ...@@ -657,4 +660,4 @@ static int RunCallBackThread (void *param)
return 0; return 0;
} }
}; // extern "C" //}; // extern "C"
...@@ -9,9 +9,8 @@ SRCS = \ ...@@ -9,9 +9,8 @@ SRCS = \
SDL_syscdrom_c.h \ SDL_syscdrom_c.h \
SDL_syscdrom.c \ SDL_syscdrom.c \
AudioFilePlayer.h \ AudioFilePlayer.h \
AudioFilePlayer.cpp \ AudioFilePlayer.c \
AudioFileReaderThread.cpp \ AudioFileReaderThread.c \
CAGuard.h \ SDLOSXCAGuard.c \
CAGuard.cpp \
CDPlayer.h \ CDPlayer.h \
CDPlayer.cpp CDPlayer.c
...@@ -69,92 +69,114 @@ ...@@ -69,92 +69,114 @@
//============================================================================= //=============================================================================
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h>
//#define NDEBUG 1 //#define NDEBUG 1
#include <assert.h> #include <assert.h>
#include "CAGuard.h" #include "SDLOSXCAGuard.h"
//#warning Need a try-based Locker too //#warning Need a try-based Locker too
//============================================================================= //=============================================================================
// CAGuard // SDLOSXCAGuard
//============================================================================= //=============================================================================
CAGuard::CAGuard() static int SDLOSXCAGuard_Lock(SDLOSXCAGuard *cag)
{ {
OSStatus theError = pthread_mutex_init(&mMutex, NULL); int theAnswer = 0;
assert(theError == 0);
theError = pthread_cond_init(&mCondVar, NULL);
assert(theError == 0);
mOwner = 0;
}
CAGuard::~CAGuard()
{
pthread_mutex_destroy(&mMutex);
pthread_cond_destroy(&mCondVar);
}
bool CAGuard::Lock()
{
bool theAnswer = false;
if(pthread_self() != mOwner) if(pthread_self() != cag->mOwner)
{ {
OSStatus theError = pthread_mutex_lock(&mMutex); OSStatus theError = pthread_mutex_lock(&cag->mMutex);
assert(theError == 0); assert(theError == 0);
mOwner = pthread_self(); cag->mOwner = pthread_self();
theAnswer = true; theAnswer = 1;
} }
return theAnswer; return theAnswer;
} }
void CAGuard::Unlock() static void SDLOSXCAGuard_Unlock(SDLOSXCAGuard *cag)
{ {
assert(pthread_self() == mOwner); assert(pthread_self() == cag->mOwner);
mOwner = 0; cag->mOwner = 0;
OSStatus theError = pthread_mutex_unlock(&mMutex); OSStatus theError = pthread_mutex_unlock(&cag->mMutex);
assert(theError == 0); assert(theError == 0);
} }
bool CAGuard::Try (bool& outWasLocked) static int SDLOSXCAGuard_Try (SDLOSXCAGuard *cag, int *outWasLocked)
{ {
bool theAnswer = false; int theAnswer = 0;
outWasLocked = false; *outWasLocked = 0;
if (pthread_self() == mOwner) { if (pthread_self() == cag->mOwner) {
theAnswer = true; theAnswer = 1;
outWasLocked = false; *outWasLocked = 0;
} else { } else {
OSStatus theError = pthread_mutex_trylock(&mMutex); OSStatus theError = pthread_mutex_trylock(&cag->mMutex);
if (theError == 0) { if (theError == 0) {
mOwner = pthread_self(); cag->mOwner = pthread_self();
theAnswer = true; theAnswer = 1;
outWasLocked = true; *outWasLocked = 1;
} }
} }
return theAnswer; return theAnswer;
} }
void CAGuard::Wait() static void SDLOSXCAGuard_Wait(SDLOSXCAGuard *cag)
{ {
assert(pthread_self() == mOwner); assert(pthread_self() == cag->mOwner);
cag->mOwner = 0;
mOwner = 0; OSStatus theError = pthread_cond_wait(&cag->mCondVar, &cag->mMutex);
assert(theError == 0);
cag->mOwner = pthread_self();
}
OSStatus theError = pthread_cond_wait(&mCondVar, &mMutex); static void SDLOSXCAGuard_Notify(SDLOSXCAGuard *cag)
{
OSStatus theError = pthread_cond_signal(&cag->mCondVar);
assert(theError == 0); assert(theError == 0);
mOwner = pthread_self();
} }
void CAGuard::Notify()
SDLOSXCAGuard *new_SDLOSXCAGuard(void)
{ {
OSStatus theError = pthread_cond_signal(&mCondVar); SDLOSXCAGuard *cag = (SDLOSXCAGuard *) malloc(sizeof (SDLOSXCAGuard));
if (cag == NULL)
return NULL;
memset(cag, '\0', sizeof (*cag));
#define SET_SDLOSXCAGUARD_METHOD(m) cag->m = SDLOSXCAGuard_##m
SET_SDLOSXCAGUARD_METHOD(Lock);
SET_SDLOSXCAGUARD_METHOD(Unlock);
SET_SDLOSXCAGUARD_METHOD(Try);
SET_SDLOSXCAGUARD_METHOD(Wait);
SET_SDLOSXCAGUARD_METHOD(Notify);
#undef SET_SDLOSXCAGUARD_METHOD
OSStatus theError = pthread_mutex_init(&cag->mMutex, NULL);
assert(theError == 0); assert(theError == 0);
theError = pthread_cond_init(&cag->mCondVar, NULL);
assert(theError == 0);
cag->mOwner = 0;
return cag;
} }
void delete_SDLOSXCAGuard(SDLOSXCAGuard *cag)
{
if (cag != NULL)
{
pthread_mutex_destroy(&cag->mMutex);
pthread_cond_destroy(&cag->mCondVar);
free(cag);
}
}
...@@ -87,57 +87,28 @@ ...@@ -87,57 +87,28 @@
// return false if they receive notification any other way. // return false if they receive notification any other way.
//============================================================================= //=============================================================================
class CAGuard typedef struct S_SDLOSXCAGuard
{ {
// Construction/Destruction // Construction/Destruction
public: //public:
CAGuard();
virtual ~CAGuard();
// Actions // Actions
public: //public:
virtual bool Lock(); int (*Lock)(struct S_SDLOSXCAGuard *cag);
virtual void Unlock(); void (*Unlock)(struct S_SDLOSXCAGuard *cag);
virtual bool Try(bool& outWasLocked); // returns true if lock is free, false if not int (*Try)(struct S_SDLOSXCAGuard *cag, int *outWasLocked); // returns true if lock is free, false if not
void (*Wait)(struct S_SDLOSXCAGuard *cag);
virtual void Wait(); void (*Notify)(struct S_SDLOSXCAGuard *cag);
virtual void Notify();
// Implementation // Implementation
protected: //protected:
pthread_mutex_t mMutex; pthread_mutex_t mMutex;
pthread_cond_t mCondVar; pthread_cond_t mCondVar;
pthread_t mOwner; pthread_t mOwner;
} SDLOSXCAGuard;
// Helper class to manage taking and releasing recursively
public: SDLOSXCAGuard *new_SDLOSXCAGuard(void);
class Locker void delete_SDLOSXCAGuard(SDLOSXCAGuard *cag);
{
// Construction/Destruction
public:
Locker(CAGuard& inGuard) : mGuard(inGuard), mNeedsRelease(false) { mNeedsRelease = mGuard.Lock(); }
~Locker() { if(mNeedsRelease) { mGuard.Unlock(); } }
private:
Locker(const Locker&);
Locker& operator=(const Locker&);
// Actions
public:
void Wait() { mGuard.Wait(); }
void Notify() { mGuard.Notify(); }
// Implementation
private:
CAGuard& mGuard;
bool mNeedsRelease;
};
};
#endif #endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment