From a855d6202536ff28e5aae4f22a0f31d8f5b325d0 Mon Sep 17 00:00:00 2001 From: Franklin Wei Date: Sat, 21 Jan 2017 15:18:31 -0500 Subject: Port of Duke Nukem 3D This ports Fabien Sanglard's Chocolate Duke to run on a version of SDL for Rockbox. Change-Id: I8f2c4c78af19de10c1633ed7bb7a997b43256dd9 --- .../plugins/sdl/src/cdrom/macosx/AudioFilePlayer.c | 360 ++++++++++++ .../plugins/sdl/src/cdrom/macosx/AudioFilePlayer.h | 178 ++++++ .../sdl/src/cdrom/macosx/AudioFileReaderThread.c | 610 ++++++++++++++++++++ apps/plugins/sdl/src/cdrom/macosx/CDPlayer.c | 636 +++++++++++++++++++++ apps/plugins/sdl/src/cdrom/macosx/CDPlayer.h | 69 +++ apps/plugins/sdl/src/cdrom/macosx/SDLOSXCAGuard.c | 199 +++++++ apps/plugins/sdl/src/cdrom/macosx/SDLOSXCAGuard.h | 116 ++++ apps/plugins/sdl/src/cdrom/macosx/SDL_syscdrom.c | 514 +++++++++++++++++ apps/plugins/sdl/src/cdrom/macosx/SDL_syscdrom_c.h | 136 +++++ 9 files changed, 2818 insertions(+) create mode 100644 apps/plugins/sdl/src/cdrom/macosx/AudioFilePlayer.c create mode 100644 apps/plugins/sdl/src/cdrom/macosx/AudioFilePlayer.h create mode 100644 apps/plugins/sdl/src/cdrom/macosx/AudioFileReaderThread.c create mode 100644 apps/plugins/sdl/src/cdrom/macosx/CDPlayer.c create mode 100644 apps/plugins/sdl/src/cdrom/macosx/CDPlayer.h create mode 100644 apps/plugins/sdl/src/cdrom/macosx/SDLOSXCAGuard.c create mode 100644 apps/plugins/sdl/src/cdrom/macosx/SDLOSXCAGuard.h create mode 100644 apps/plugins/sdl/src/cdrom/macosx/SDL_syscdrom.c create mode 100644 apps/plugins/sdl/src/cdrom/macosx/SDL_syscdrom_c.h (limited to 'apps/plugins/sdl/src/cdrom/macosx') diff --git a/apps/plugins/sdl/src/cdrom/macosx/AudioFilePlayer.c b/apps/plugins/sdl/src/cdrom/macosx/AudioFilePlayer.c new file mode 100644 index 0000000000..97cb9b2874 --- /dev/null +++ b/apps/plugins/sdl/src/cdrom/macosx/AudioFilePlayer.c @@ -0,0 +1,360 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org + + This file based on Apple sample code. We haven't changed the file name, + so if you want to see the original search for it on apple.com/developer +*/ +#include "SDL_config.h" +#include "SDL_endian.h" + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + AudioFilePlayer.cpp +*/ +#include "AudioFilePlayer.h" + +/* +void ThrowResult (OSStatus result, const char* str) +{ + SDL_SetError ("Error: %s %d", str, result); + throw result; +} +*/ + +#if DEBUG +static void PrintStreamDesc (AudioStreamBasicDescription *inDesc) +{ + if (!inDesc) { + printf ("Can't print a NULL desc!\n"); + return; + } + + printf ("- - - - - - - - - - - - - - - - - - - -\n"); + printf (" Sample Rate:%f\n", inDesc->mSampleRate); + printf (" Format ID:%s\n", (char*)&inDesc->mFormatID); + printf (" Format Flags:%lX\n", inDesc->mFormatFlags); + printf (" Bytes per Packet:%ld\n", inDesc->mBytesPerPacket); + printf (" Frames per Packet:%ld\n", inDesc->mFramesPerPacket); + printf (" Bytes per Frame:%ld\n", inDesc->mBytesPerFrame); + printf (" Channels per Frame:%ld\n", inDesc->mChannelsPerFrame); + printf (" Bits per Channel:%ld\n", inDesc->mBitsPerChannel); + printf ("- - - - - - - - - - - - - - - - - - - -\n"); +} +#endif + + +static int AudioFilePlayer_SetDestination (AudioFilePlayer *afp, AudioUnit *inDestUnit) +{ + /*if (afp->mConnected) throw static_cast(-1);*/ /* can't set dest if already engaged */ + if (afp->mConnected) + return 0 ; + + SDL_memcpy(&afp->mPlayUnit, inDestUnit, sizeof (afp->mPlayUnit)); + + OSStatus result = noErr; + + + /* we can "down" cast a component instance to a component */ + ComponentDescription desc; + result = GetComponentInfo ((Component)*inDestUnit, &desc, 0, 0, 0); + if (result) return 0; /*THROW_RESULT("GetComponentInfo")*/ + + /* we're going to use this to know which convert routine to call + a v1 audio unit will have a type of 'aunt' + a v2 audio unit will have one of several different types. */ + if (desc.componentType != kAudioUnitType_Output) { + result = badComponentInstance; + /*THROW_RESULT("BAD COMPONENT")*/ + if (result) return 0; + } + + /* Set the input format of the audio unit. */ + result = AudioUnitSetProperty (*inDestUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, + &afp->mFileDescription, + sizeof (afp->mFileDescription)); + /*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 +} + +static void AudioFilePlayer_SetStartFrame (AudioFilePlayer *afp, int frame) +{ + SInt64 position = frame * 2352; + + afp->mStartFrame = frame; + afp->mAudioFileManager->SetPosition (afp->mAudioFileManager, position); +} + + +static int AudioFilePlayer_GetCurrentFrame (AudioFilePlayer *afp) +{ + return afp->mStartFrame + (afp->mAudioFileManager->GetByteCounter(afp->mAudioFileManager) / 2352); +} + +static void AudioFilePlayer_SetStopFrame (AudioFilePlayer *afp, int frame) +{ + SInt64 position = frame * 2352; + + afp->mAudioFileManager->SetEndOfFile (afp->mAudioFileManager, position); +} + +void delete_AudioFilePlayer(AudioFilePlayer *afp) +{ + if (afp != NULL) + { + afp->Disconnect(afp); + + if (afp->mAudioFileManager) { + delete_AudioFileManager(afp->mAudioFileManager); + afp->mAudioFileManager = 0; + } + + if (afp->mForkRefNum) { + FSCloseFork (afp->mForkRefNum); + afp->mForkRefNum = 0; + } + SDL_free(afp); + } +} + +static int AudioFilePlayer_Connect(AudioFilePlayer *afp) +{ +#if DEBUG + printf ("Connect:%x, engaged=%d\n", (int)afp->mPlayUnit, (afp->mConnected ? 1 : 0)); +#endif + if (!afp->mConnected) + { + if (!afp->mAudioFileManager->DoConnect(afp->mAudioFileManager)) + return 0; + + /* set the render callback for the file data to be supplied to the sound converter AU */ + afp->mInputCallback.inputProc = afp->mAudioFileManager->FileInputProc; + afp->mInputCallback.inputProcRefCon = afp->mAudioFileManager; + + OSStatus result = AudioUnitSetProperty (afp->mPlayUnit, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, + 0, + &afp->mInputCallback, + sizeof(afp->mInputCallback)); + if (result) return 0; /*THROW_RESULT("AudioUnitSetProperty")*/ + afp->mConnected = 1; + } + + return 1; +} + +/* warning noted, now please go away ;-) */ +/* #warning This should redirect the calling of notification code to some other thread */ +static void AudioFilePlayer_DoNotification (AudioFilePlayer *afp, OSStatus inStatus) +{ + if (afp->mNotifier) { + (*afp->mNotifier) (afp->mRefCon, inStatus); + } else { + SDL_SetError ("Notification posted with no notifier in place"); + + if (inStatus == kAudioFilePlay_FileIsFinished) + afp->Disconnect(afp); + else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun) + afp->Disconnect(afp); + } +} + +static void AudioFilePlayer_Disconnect (AudioFilePlayer *afp) +{ +#if DEBUG + printf ("Disconnect:%x,%ld, engaged=%d\n", (int)afp->mPlayUnit, 0, (afp->mConnected ? 1 : 0)); +#endif + if (afp->mConnected) + { + afp->mConnected = 0; + + afp->mInputCallback.inputProc = 0; + afp->mInputCallback.inputProcRefCon = 0; + OSStatus result = AudioUnitSetProperty (afp->mPlayUnit, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Input, + 0, + &afp->mInputCallback, + sizeof(afp->mInputCallback)); + if (result) + SDL_SetError ("AudioUnitSetProperty:RemoveInputCallback:%ld", result); + + afp->mAudioFileManager->Disconnect(afp->mAudioFileManager); + } +} + +typedef struct { + UInt32 offset; + UInt32 blockSize; +} SSNDData; + +static int AudioFilePlayer_OpenFile (AudioFilePlayer *afp, const FSRef *inRef, SInt64 *outFileDataSize) +{ + ContainerChunk chunkHeader; + ChunkHeader chunk; + SSNDData ssndData; + + OSErr result; + HFSUniStr255 dfName; + ByteCount actual; + SInt64 offset; + + /* Open the data fork of the input file */ + result = FSGetDataForkName(&dfName); + if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSGetDataForkName")*/ + + result = FSOpenFork(inRef, dfName.length, dfName.unicode, fsRdPerm, &afp->mForkRefNum); + if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSOpenFork")*/ + + /* Read the file header, and check if it's indeed an AIFC file */ + result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(chunkHeader), &chunkHeader, &actual); + if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/ + + if (SDL_SwapBE32(chunkHeader.ckID) != 'FORM') { + result = -1; + if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): chunk id is not 'FORM'");*/ + } + + if (SDL_SwapBE32(chunkHeader.formType) != 'AIFC') { + result = -1; + if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): file format is not 'AIFC'");*/ + } + + /* Search for the SSND chunk. We ignore all compression etc. information + in other chunks. Of course that is kind of evil, but for now we are lazy + and rely on the cdfs to always give us the same fixed format. + TODO: Parse the COMM chunk we currently skip to fill in mFileDescription. + */ + offset = 0; + do { + result = FSReadFork(afp->mForkRefNum, fsFromMark, offset, sizeof(chunk), &chunk, &actual); + if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/ + + chunk.ckID = SDL_SwapBE32(chunk.ckID); + chunk.ckSize = SDL_SwapBE32(chunk.ckSize); + + /* Skip the chunk data */ + offset = chunk.ckSize; + } while (chunk.ckID != 'SSND'); + + /* Read the header of the SSND chunk. After this, we are positioned right + at the start of the audio data. */ + result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(ssndData), &ssndData, &actual); + if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/ + + ssndData.offset = SDL_SwapBE32(ssndData.offset); + + result = FSSetForkPosition(afp->mForkRefNum, fsFromMark, ssndData.offset); + if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSSetForkPosition")*/ + + /* Data size */ + *outFileDataSize = chunk.ckSize - ssndData.offset - 8; + + /* File format */ + afp->mFileDescription.mSampleRate = 44100; + afp->mFileDescription.mFormatID = kAudioFormatLinearPCM; + afp->mFileDescription.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger; + afp->mFileDescription.mBytesPerPacket = 4; + afp->mFileDescription.mFramesPerPacket = 1; + afp->mFileDescription.mBytesPerFrame = 4; + afp->mFileDescription.mChannelsPerFrame = 2; + afp->mFileDescription.mBitsPerChannel = 16; + + return 1; +} + +AudioFilePlayer *new_AudioFilePlayer (const FSRef *inFileRef) +{ + SInt64 fileDataSize = 0; + + AudioFilePlayer *afp = (AudioFilePlayer *) SDL_malloc(sizeof (AudioFilePlayer)); + if (afp == NULL) + return NULL; + SDL_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)) + { + SDL_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; +} + diff --git a/apps/plugins/sdl/src/cdrom/macosx/AudioFilePlayer.h b/apps/plugins/sdl/src/cdrom/macosx/AudioFilePlayer.h new file mode 100644 index 0000000000..886d017a59 --- /dev/null +++ b/apps/plugins/sdl/src/cdrom/macosx/AudioFilePlayer.h @@ -0,0 +1,178 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org + + This file based on Apple sample code. We haven't changed the file name, + so if you want to see the original search for it on apple.com/developer +*/ +#include "SDL_config.h" + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + AudioFilePlayer.h +*/ +#ifndef __AudioFilePlayer_H__ +#define __AudioFilePlayer_H__ + +#include + +#include +#if MAC_OS_X_VERSION_MAX_ALLOWED <= 1050 +#include +#endif + +#if (MAC_OS_X_VERSION_MAX_ALLOWED < 1050) +typedef SInt16 FSIORefNum; +#endif + +#include "SDL_error.h" + +const char* AudioFilePlayerErrorStr (OSStatus error); + +/* +void ThrowResult (OSStatus result, const char *str); + +#define THROW_RESULT(str) \ + if (result) { \ + ThrowResult (result, str); \ + } +*/ + +typedef void (*AudioFilePlayNotifier)(void *inRefCon, + OSStatus inStatus); + +enum { + kAudioFilePlayErr_FilePlayUnderrun = -10000, + kAudioFilePlay_FileIsFinished = -10001, + kAudioFilePlay_PlayerIsUninitialized = -10002 +}; + + +struct S_AudioFileManager; + +#pragma mark __________ AudioFilePlayer +typedef struct S_AudioFilePlayer +{ +/*public:*/ + int (*SetDestination)(struct S_AudioFilePlayer *afp, AudioUnit *inDestUnit); + void (*SetNotifier)(struct S_AudioFilePlayer *afp, AudioFilePlayNotifier inNotifier, void *inRefCon); + void (*SetStartFrame)(struct S_AudioFilePlayer *afp, int frame); /* seek in the file */ + int (*GetCurrentFrame)(struct S_AudioFilePlayer *afp); /* get the current frame position */ + void (*SetStopFrame)(struct S_AudioFilePlayer *afp, int frame); /* set limit in the file */ + int (*Connect)(struct S_AudioFilePlayer *afp); + void (*Disconnect)(struct S_AudioFilePlayer *afp); + void (*DoNotification)(struct S_AudioFilePlayer *afp, OSStatus inError); + int (*IsConnected)(struct S_AudioFilePlayer *afp); + AudioUnit (*GetDestUnit)(struct S_AudioFilePlayer *afp); + void (*Print)(struct S_AudioFilePlayer *afp); + +/*private:*/ + AudioUnit mPlayUnit; + FSIORefNum mForkRefNum; + + AURenderCallbackStruct mInputCallback; + + AudioStreamBasicDescription mFileDescription; + + int mConnected; + + struct S_AudioFileManager* mAudioFileManager; + + AudioFilePlayNotifier mNotifier; + void* mRefCon; + + int mStartFrame; + +#pragma mark __________ Private_Methods + + 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 +typedef struct S_AudioFileManager +{ +/*public:*/ + /* this method should NOT be called by an object of this class + as it is called by the parent's Disconnect() method */ + void (*Disconnect)(struct S_AudioFileManager *afm); + int (*DoConnect)(struct S_AudioFileManager *afm); + OSStatus (*Read)(struct S_AudioFileManager *afm, char *buffer, ByteCount *len); + const char* (*GetFileBuffer)(struct S_AudioFileManager *afm); + const AudioFilePlayer *(*GetParent)(struct S_AudioFileManager *afm); + void (*SetPosition)(struct S_AudioFileManager *afm, SInt64 pos); /* seek/rewind in the file */ + 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) */ + +/*protected:*/ + AudioFilePlayer* mParent; + SInt16 mForkRefNum; + SInt64 mAudioDataOffset; + + char* mFileBuffer; + + int mByteCounter; + + int mReadFromFirstBuffer; + int mLockUnsuccessful; + int mIsEngaged; + + int mNumTimesAskedSinceFinished; + + + void* mTmpBuffer; + UInt32 mBufferSize; + UInt32 mBufferOffset; +/*public:*/ + UInt32 mChunkSize; + SInt64 mFileLength; + SInt64 mReadFilePosition; + int mWriteToFirstBuffer; + int mFinishedReadingData; + +/*protected:*/ + OSStatus (*Render)(struct S_AudioFileManager *afm, AudioBufferList *ioData); + OSStatus (*GetFileData)(struct S_AudioFileManager *afm, void** inOutData, UInt32 *inOutDataSize); + void (*AfterRender)(struct S_AudioFileManager *afm); + +/*public:*/ + /*static*/ + OSStatus (*FileInputProc)(void *inRefCon, + AudioUnitRenderActionFlags *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData); +} AudioFileManager; + + +AudioFileManager *new_AudioFileManager (AudioFilePlayer *inParent, + SInt16 inForkRefNum, + SInt64 inFileLength, + UInt32 inChunkSize); + +void delete_AudioFileManager(AudioFileManager *afm); + +#endif + diff --git a/apps/plugins/sdl/src/cdrom/macosx/AudioFileReaderThread.c b/apps/plugins/sdl/src/cdrom/macosx/AudioFileReaderThread.c new file mode 100644 index 0000000000..0007c07f61 --- /dev/null +++ b/apps/plugins/sdl/src/cdrom/macosx/AudioFileReaderThread.c @@ -0,0 +1,610 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org + + This file based on Apple sample code. We haven't changed the file name, + so if you want to see the original search for it on apple.com/developer +*/ +#include "SDL_config.h" + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + AudioFileManager.cpp +*/ +#include "AudioFilePlayer.h" +#include /* used for setting policy of thread */ +#include "SDLOSXCAGuard.h" +#include + +/*#include */ + +/*typedef void *FileData;*/ +typedef struct S_FileData +{ + AudioFileManager *obj; + struct S_FileData *next; +} FileData; + + +typedef struct S_FileReaderThread { +/*public:*/ + SDLOSXCAGuard* (*GetGuard)(struct S_FileReaderThread *frt); + void (*AddReader)(struct S_FileReaderThread *frt); + void (*RemoveReader)(struct S_FileReaderThread *frt, AudioFileManager* inItem); + int (*TryNextRead)(struct S_FileReaderThread *frt, AudioFileManager* inItem); + + int mThreadShouldDie; + +/*private:*/ + /*typedef std::list FileData;*/ + + SDLOSXCAGuard *mGuard; + UInt32 mThreadPriority; + + int mNumReaders; + FileData *mFileData; + + + void (*ReadNextChunk)(struct S_FileReaderThread *frt); + int (*StartFixedPriorityThread)(struct S_FileReaderThread *frt); + /*static*/ + UInt32 (*GetThreadBasePriority)(pthread_t inThread); + /*static*/ + void* (*DiskReaderEntry)(void *inRefCon); +} FileReaderThread; + + +static SDLOSXCAGuard* FileReaderThread_GetGuard(FileReaderThread *frt) +{ + return frt->mGuard; +} + +/* returns 1 if succeeded */ +static int FileReaderThread_TryNextRead (FileReaderThread *frt, AudioFileManager* inItem) +{ + int didLock = 0; + int succeeded = 0; + if (frt->mGuard->Try(frt->mGuard, &didLock)) + { + /*frt->mFileData.push_back (inItem);*/ + /* !!! FIXME: this could be faster with a "tail" member. --ryan. */ + FileData *i = frt->mFileData; + FileData *prev = NULL; + + FileData *newfd = (FileData *) SDL_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); + } + + return succeeded; +} + +static void FileReaderThread_AddReader(FileReaderThread *frt) +{ + if (frt->mNumReaders == 0) + { + frt->mThreadShouldDie = 0; + frt->StartFixedPriorityThread (frt); + } + frt->mNumReaders++; +} + +static void FileReaderThread_RemoveReader (FileReaderThread *frt, AudioFileManager* inItem) +{ + if (frt->mNumReaders > 0) + { + int bNeedsRelease = frt->mGuard->Lock(frt->mGuard); + + /*frt->mFileData.remove (inItem);*/ + FileData *i = frt->mFileData; + FileData *prev = NULL; + while (i != NULL) + { + FileData *next = i->next; + if (i->obj != inItem) + prev = i; + else + { + if (prev == NULL) + frt->mFileData = next; + else + prev->next = next; + SDL_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); + } +} + +static int FileReaderThread_StartFixedPriorityThread (FileReaderThread *frt) +{ + pthread_attr_t theThreadAttrs; + pthread_t pThread; + + OSStatus result = pthread_attr_init(&theThreadAttrs); + if (result) return 0; /*THROW_RESULT("pthread_attr_init - Thread attributes could not be created.")*/ + + result = pthread_attr_setdetachstate(&theThreadAttrs, PTHREAD_CREATE_DETACHED); + if (result) return 0; /*THROW_RESULT("pthread_attr_setdetachstate - Thread attributes could not be detached.")*/ + + result = pthread_create (&pThread, &theThreadAttrs, frt->DiskReaderEntry, frt); + if (result) return 0; /*THROW_RESULT("pthread_create - Create and start the thread.")*/ + + pthread_attr_destroy(&theThreadAttrs); + + /* we've now created the thread and started it + we'll now set the priority of the thread to the nominated priority + and we'll also make the thread fixed */ + thread_extended_policy_data_t theFixedPolicy; + thread_precedence_policy_data_t thePrecedencePolicy; + SInt32 relativePriority; + + /* make thread fixed */ + 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); + if (result) return 0; /*THROW_RESULT("thread_policy - Couldn't set thread as fixed priority.")*/ + /* set priority */ + /* precedency policy's "importance" value is relative to spawning thread's priority */ + relativePriority = frt->mThreadPriority - frt->GetThreadBasePriority(pthread_self()); + + thePrecedencePolicy.importance = relativePriority; + result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_PRECEDENCE_POLICY, (thread_policy_t)&thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT); + if (result) return 0; /*THROW_RESULT("thread_policy - Couldn't set thread priority.")*/ + + return 1; +} + +static UInt32 FileReaderThread_GetThreadBasePriority (pthread_t inThread) +{ + thread_basic_info_data_t threadInfo; + policy_info_data_t thePolicyInfo; + unsigned int count; + + /* get basic info */ + count = THREAD_BASIC_INFO_COUNT; + thread_info (pthread_mach_thread_np (inThread), THREAD_BASIC_INFO, (integer_t*)&threadInfo, &count); + + switch (threadInfo.policy) { + case POLICY_TIMESHARE: + count = POLICY_TIMESHARE_INFO_COUNT; + thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_TIMESHARE_INFO, (integer_t*)&(thePolicyInfo.ts), &count); + return thePolicyInfo.ts.base_priority; + break; + + case POLICY_FIFO: + count = POLICY_FIFO_INFO_COUNT; + thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_FIFO_INFO, (integer_t*)&(thePolicyInfo.fifo), &count); + if (thePolicyInfo.fifo.depressed) { + return thePolicyInfo.fifo.depress_priority; + } else { + return thePolicyInfo.fifo.base_priority; + } + break; + + case POLICY_RR: + count = POLICY_RR_INFO_COUNT; + thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_RR_INFO, (integer_t*)&(thePolicyInfo.rr), &count); + if (thePolicyInfo.rr.depressed) { + return thePolicyInfo.rr.depress_priority; + } else { + return thePolicyInfo.rr.base_priority; + } + break; + } + + return 0; +} + +static void *FileReaderThread_DiskReaderEntry (void *inRefCon) +{ + FileReaderThread *frt = (FileReaderThread *)inRefCon; + frt->ReadNextChunk(frt); + #if DEBUG + printf ("finished with reading file\n"); + #endif + + return 0; +} + +static void FileReaderThread_ReadNextChunk (FileReaderThread *frt) +{ + OSStatus result; + ByteCount dataChunkSize; + AudioFileManager* theItem = 0; + + for (;;) + { + { /* this is a scoped based lock */ + int bNeedsRelease = frt->mGuard->Lock(frt->mGuard); + + if (frt->mThreadShouldDie) { + frt->mGuard->Notify(frt->mGuard); + if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard); + return; + } + + /*if (frt->mFileData.empty())*/ + if (frt->mFileData == NULL) + { + frt->mGuard->Wait(frt->mGuard); + } + + /* kill thread */ + if (frt->mThreadShouldDie) { + + frt->mGuard->Notify(frt->mGuard); + if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard); + return; + } + + /*theItem = frt->mFileData.front();*/ + /*frt->mFileData.pop_front();*/ + theItem = NULL; + if (frt->mFileData != NULL) + { + FileData *next = frt->mFileData->next; + theItem = frt->mFileData->obj; + SDL_free(frt->mFileData); + frt->mFileData = next; + } + + if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard); + } + + if ((theItem->mFileLength - theItem->mReadFilePosition) < theItem->mChunkSize) + dataChunkSize = theItem->mFileLength - theItem->mReadFilePosition; + else + dataChunkSize = theItem->mChunkSize; + + /* this is the exit condition for the thread */ + if (dataChunkSize <= 0) { + theItem->mFinishedReadingData = 1; + continue; + } + /* construct pointer */ + char* writePtr = (char *) (theItem->GetFileBuffer(theItem) + + (theItem->mWriteToFirstBuffer ? 0 : theItem->mChunkSize)); + + /* read data */ + result = theItem->Read(theItem, writePtr, &dataChunkSize); + if (result != noErr && result != eofErr) { + AudioFilePlayer *afp = (AudioFilePlayer *) theItem->GetParent(theItem); + afp->DoNotification(afp, result); + continue; + } + + if (dataChunkSize != theItem->mChunkSize) + { + writePtr += dataChunkSize; + + /* can't exit yet.. we still have to pass the partial buffer back */ + SDL_memset(writePtr, 0, (theItem->mChunkSize - dataChunkSize)); + } + + theItem->mWriteToFirstBuffer = !theItem->mWriteToFirstBuffer; /* switch buffers */ + + if (result == eofErr) + theItem->mReadFilePosition = theItem->mFileLength; + else + theItem->mReadFilePosition += dataChunkSize; /* increment count */ + } +} + +void delete_FileReaderThread(FileReaderThread *frt) +{ + if (frt != NULL) + { + delete_SDLOSXCAGuard(frt->mGuard); + SDL_free(frt); + } +} + +FileReaderThread *new_FileReaderThread () +{ + FileReaderThread *frt = (FileReaderThread *) SDL_malloc(sizeof (FileReaderThread)); + if (frt == NULL) + return NULL; + SDL_memset(frt, '\0', sizeof (*frt)); + + frt->mGuard = new_SDLOSXCAGuard(); + if (frt->mGuard == NULL) + { + SDL_free(frt); + return NULL; + } + + #define SET_FILEREADERTHREAD_METHOD(m) frt->m = FileReaderThread_##m + SET_FILEREADERTHREAD_METHOD(GetGuard); + SET_FILEREADERTHREAD_METHOD(AddReader); + 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; +} + + +static FileReaderThread *sReaderThread; + + +static int AudioFileManager_DoConnect (AudioFileManager *afm) +{ + if (!afm->mIsEngaged) + { + OSStatus result; + + /*afm->mReadFilePosition = 0;*/ + afm->mFinishedReadingData = 0; + + afm->mNumTimesAskedSinceFinished = 0; + afm->mLockUnsuccessful = 0; + + ByteCount dataChunkSize; + + if ((afm->mFileLength - afm->mReadFilePosition) < afm->mChunkSize) + dataChunkSize = afm->mFileLength - afm->mReadFilePosition; + else + dataChunkSize = afm->mChunkSize; + + result = afm->Read(afm, afm->mFileBuffer, &dataChunkSize); + if (result) return 0; /*THROW_RESULT("AudioFileManager::DoConnect(): Read")*/ + + afm->mReadFilePosition += dataChunkSize; + + afm->mWriteToFirstBuffer = 0; + afm->mReadFromFirstBuffer = 1; + + sReaderThread->AddReader(sReaderThread); + + afm->mIsEngaged = 1; + } + /* + else + throw static_cast(-1); */ /* thread has already been started */ + + return 1; +} + +static void AudioFileManager_Disconnect (AudioFileManager *afm) +{ + if (afm->mIsEngaged) + { + sReaderThread->RemoveReader (sReaderThread, afm); + afm->mIsEngaged = 0; + } +} + +static OSStatus AudioFileManager_Read(AudioFileManager *afm, char *buffer, ByteCount *len) +{ + return FSReadFork (afm->mForkRefNum, + fsFromStart, + afm->mReadFilePosition + afm->mAudioDataOffset, + *len, + buffer, + len); +} + +static OSStatus AudioFileManager_GetFileData (AudioFileManager *afm, void** inOutData, UInt32 *inOutDataSize) +{ + if (afm->mFinishedReadingData) + { + ++afm->mNumTimesAskedSinceFinished; + *inOutDataSize = 0; + *inOutData = 0; + return noErr; + } + + if (afm->mReadFromFirstBuffer == afm->mWriteToFirstBuffer) { + #if DEBUG + printf ("* * * * * * * Can't keep up with reading file\n"); + #endif + + afm->mParent->DoNotification (afm->mParent, kAudioFilePlayErr_FilePlayUnderrun); + *inOutDataSize = 0; + *inOutData = 0; + } else { + *inOutDataSize = afm->mChunkSize; + *inOutData = afm->mReadFromFirstBuffer ? afm->mFileBuffer : (afm->mFileBuffer + afm->mChunkSize); + } + + afm->mLockUnsuccessful = !sReaderThread->TryNextRead (sReaderThread, afm); + + afm->mReadFromFirstBuffer = !afm->mReadFromFirstBuffer; + + return noErr; +} + +static void AudioFileManager_AfterRender (AudioFileManager *afm) +{ + if (afm->mNumTimesAskedSinceFinished > 0) + { + int didLock = 0; + SDLOSXCAGuard *guard = sReaderThread->GetGuard(sReaderThread); + if (guard->Try(guard, &didLock)) { + afm->mParent->DoNotification (afm->mParent, kAudioFilePlay_FileIsFinished); + if (didLock) + guard->Unlock(guard); + } + } + + if (afm->mLockUnsuccessful) + afm->mLockUnsuccessful = !sReaderThread->TryNextRead (sReaderThread, afm); +} + +static void AudioFileManager_SetPosition (AudioFileManager *afm, SInt64 pos) +{ + if (pos < 0 || pos >= afm->mFileLength) { + SDL_SetError ("AudioFileManager::SetPosition - position invalid: %d filelen=%d\n", + (unsigned int)pos, (unsigned int)afm->mFileLength); + pos = 0; + } + + afm->mReadFilePosition = pos; +} + +static void AudioFileManager_SetEndOfFile (AudioFileManager *afm, SInt64 pos) +{ + if (pos <= 0 || pos > afm->mFileLength) { + SDL_SetError ("AudioFileManager::SetEndOfFile - position beyond actual eof\n"); + pos = afm->mFileLength; + } + + 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 *ioActionFlags, + const AudioTimeStamp *inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList *ioData) +{ + AudioFileManager* afm = (AudioFileManager*)inRefCon; + return afm->Render(afm, ioData); +} + +static OSStatus AudioFileManager_Render (AudioFileManager *afm, AudioBufferList *ioData) +{ + OSStatus result = noErr; + AudioBuffer *abuf; + UInt32 i; + + for (i = 0; i < ioData->mNumberBuffers; i++) { + abuf = &ioData->mBuffers[i]; + 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 (abuf->mDataByteSize > afm->mBufferSize - afm->mBufferOffset) + abuf->mDataByteSize = afm->mBufferSize - afm->mBufferOffset; + abuf->mData = (char *)afm->mTmpBuffer + afm->mBufferOffset; + afm->mBufferOffset += abuf->mDataByteSize; + + afm->mByteCounter += abuf->mDataByteSize; + afm->AfterRender(afm); + } + return result; +} + + +void delete_AudioFileManager (AudioFileManager *afm) +{ + if (afm != NULL) { + if (afm->mFileBuffer) { + free(afm->mFileBuffer); + } + + SDL_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 *) SDL_malloc(sizeof (AudioFileManager)); + if (afm == NULL) + return NULL; + SDL_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*) SDL_malloc(afm->mChunkSize * 2); + FSGetForkPosition(afm->mForkRefNum, &afm->mAudioDataOffset); + assert (afm->mFileBuffer != NULL); + return afm; +} + diff --git a/apps/plugins/sdl/src/cdrom/macosx/CDPlayer.c b/apps/plugins/sdl/src/cdrom/macosx/CDPlayer.c new file mode 100644 index 0000000000..beb87cd85b --- /dev/null +++ b/apps/plugins/sdl/src/cdrom/macosx/CDPlayer.c @@ -0,0 +1,636 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#include "CDPlayer.h" +#include "AudioFilePlayer.h" +#include "SDLOSXCAGuard.h" + +/* we're exporting these functions into C land for SDL_syscdrom.c */ +/*extern "C" {*/ + +/*/////////////////////////////////////////////////////////////////////////// + Constants + //////////////////////////////////////////////////////////////////////////*/ + +#define kAudioCDFilesystemID (UInt16)(('J' << 8) | 'H') /* 'JH'; this avoids compiler warning */ + +/* XML PList keys */ +#define kRawTOCDataString "Format 0x02 TOC Data" +#define kSessionsString "Sessions" +#define kSessionTypeString "Session Type" +#define kTrackArrayString "Track Array" +#define kFirstTrackInSessionString "First Track" +#define kLastTrackInSessionString "Last Track" +#define kLeadoutBlockString "Leadout Block" +#define kDataKeyString "Data" +#define kPointKeyString "Point" +#define kSessionNumberKeyString "Session Number" +#define kStartBlockKeyString "Start Block" + +/*/////////////////////////////////////////////////////////////////////////// + Globals + //////////////////////////////////////////////////////////////////////////*/ + +#pragma mark -- Globals -- + +static int playBackWasInit = 0; +static AudioUnit theUnit; +static AudioFilePlayer* thePlayer = NULL; +static CDPlayerCompletionProc completionProc = NULL; +static SDL_mutex *apiMutex = NULL; +static SDL_sem *callbackSem; +static SDL_CD* theCDROM; + +/*/////////////////////////////////////////////////////////////////////////// + Prototypes + //////////////////////////////////////////////////////////////////////////*/ + +#pragma mark -- Prototypes -- + +static OSStatus CheckInit (); + +static void FilePlayNotificationHandler (void* inRefCon, OSStatus inStatus); + +static int RunCallBackThread (void* inRefCon); + + +#pragma mark -- Public Functions -- + +void Lock () +{ + if (!apiMutex) { + apiMutex = SDL_CreateMutex(); + } + SDL_mutexP(apiMutex); +} + +void Unlock () +{ + SDL_mutexV(apiMutex); +} + +int DetectAudioCDVolumes(FSVolumeRefNum *volumes, int numVolumes) +{ + int volumeIndex; + int cdVolumeCount = 0; + OSStatus result = noErr; + + for (volumeIndex = 1; result == noErr || result != nsvErr; volumeIndex++) + { + FSVolumeRefNum actualVolume; + FSVolumeInfo volumeInfo; + + memset (&volumeInfo, 0, sizeof(volumeInfo)); + + result = FSGetVolumeInfo (kFSInvalidVolumeRefNum, + volumeIndex, + &actualVolume, + kFSVolInfoFSInfo, + &volumeInfo, + NULL, + NULL); + + if (result == noErr) + { + if (volumeInfo.filesystemID == kAudioCDFilesystemID) /* It's an audio CD */ + { + if (volumes != NULL && cdVolumeCount < numVolumes) + volumes[cdVolumeCount] = actualVolume; + + cdVolumeCount++; + } + } + else + { + /* I'm commenting this out because it seems to be harmless */ + /*SDL_SetError ("DetectAudioCDVolumes: FSGetVolumeInfo returned %d", result);*/ + } + } + + return cdVolumeCount; +} + +int ReadTOCData (FSVolumeRefNum theVolume, SDL_CD *theCD) +{ + HFSUniStr255 dataForkName; + OSStatus theErr; + FSIORefNum forkRefNum; + SInt64 forkSize; + Ptr forkData = 0; + ByteCount actualRead; + CFDataRef dataRef = 0; + CFPropertyListRef propertyListRef = 0; + FSRefParam fsRefPB; + FSRef tocPlistFSRef; + FSRef rootRef; + const char* error = "Unspecified Error"; + const UniChar uniName[] = { '.','T','O','C','.','p','l','i','s','t' }; + + theErr = FSGetVolumeInfo(theVolume, 0, 0, kFSVolInfoNone, 0, 0, &rootRef); + if(theErr != noErr) { + error = "FSGetVolumeInfo"; + goto bail; + } + + SDL_memset(&fsRefPB, '\0', sizeof (fsRefPB)); + + /* get stuff from .TOC.plist */ + fsRefPB.ref = &rootRef; + fsRefPB.newRef = &tocPlistFSRef; + fsRefPB.nameLength = sizeof (uniName) / sizeof (uniName[0]); + fsRefPB.name = uniName; + fsRefPB.textEncodingHint = kTextEncodingUnknown; + + theErr = PBMakeFSRefUnicodeSync (&fsRefPB); + if(theErr != noErr) { + error = "PBMakeFSRefUnicodeSync"; + goto bail; + } + + /* Load and parse the TOC XML data */ + + theErr = FSGetDataForkName (&dataForkName); + if (theErr != noErr) { + error = "FSGetDataForkName"; + goto bail; + } + + theErr = FSOpenFork (&tocPlistFSRef, dataForkName.length, dataForkName.unicode, fsRdPerm, &forkRefNum); + if (theErr != noErr) { + error = "FSOpenFork"; + goto bail; + } + + theErr = FSGetForkSize (forkRefNum, &forkSize); + if (theErr != noErr) { + error = "FSGetForkSize"; + goto bail; + } + + /* Allocate some memory for the XML data */ + forkData = NewPtr (forkSize); + if(forkData == NULL) { + error = "NewPtr"; + goto bail; + } + + theErr = FSReadFork (forkRefNum, fsFromStart, 0 /* offset location */, forkSize, forkData, &actualRead); + if(theErr != noErr) { + error = "FSReadFork"; + goto bail; + } + + dataRef = CFDataCreate (kCFAllocatorDefault, (UInt8 *)forkData, forkSize); + if(dataRef == 0) { + error = "CFDataCreate"; + goto bail; + } + + propertyListRef = CFPropertyListCreateFromXMLData (kCFAllocatorDefault, + dataRef, + kCFPropertyListImmutable, + NULL); + if (propertyListRef == NULL) { + error = "CFPropertyListCreateFromXMLData"; + goto bail; + } + + /* Now we got the Property List in memory. Parse it. */ + + /* First, make sure the root item is a CFDictionary. If not, release and bail. */ + if(CFGetTypeID(propertyListRef)== CFDictionaryGetTypeID()) + { + CFDictionaryRef dictRef = (CFDictionaryRef)propertyListRef; + + CFDataRef theRawTOCDataRef; + CFArrayRef theSessionArrayRef; + CFIndex numSessions; + CFIndex index; + + /* This is how we get the Raw TOC Data */ + theRawTOCDataRef = (CFDataRef)CFDictionaryGetValue (dictRef, CFSTR(kRawTOCDataString)); + + /* Get the session array info. */ + theSessionArrayRef = (CFArrayRef)CFDictionaryGetValue (dictRef, CFSTR(kSessionsString)); + + /* Find out how many sessions there are. */ + numSessions = CFArrayGetCount (theSessionArrayRef); + + /* Initialize the total number of tracks to 0 */ + theCD->numtracks = 0; + + /* Iterate over all sessions, collecting the track data */ + for(index = 0; index < numSessions; index++) + { + CFDictionaryRef theSessionDict; + CFNumberRef leadoutBlock; + CFArrayRef trackArray; + CFIndex numTracks; + CFIndex trackIndex; + UInt32 value = 0; + + theSessionDict = (CFDictionaryRef) CFArrayGetValueAtIndex (theSessionArrayRef, index); + leadoutBlock = (CFNumberRef) CFDictionaryGetValue (theSessionDict, CFSTR(kLeadoutBlockString)); + + trackArray = (CFArrayRef)CFDictionaryGetValue (theSessionDict, CFSTR(kTrackArrayString)); + + numTracks = CFArrayGetCount (trackArray); + + for(trackIndex = 0; trackIndex < numTracks; trackIndex++) { + + CFDictionaryRef theTrackDict; + CFNumberRef trackNumber; + CFNumberRef sessionNumber; + CFNumberRef startBlock; + CFBooleanRef isDataTrack; + UInt32 value; + + theTrackDict = (CFDictionaryRef) CFArrayGetValueAtIndex (trackArray, trackIndex); + + trackNumber = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kPointKeyString)); + sessionNumber = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kSessionNumberKeyString)); + startBlock = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kStartBlockKeyString)); + isDataTrack = (CFBooleanRef) CFDictionaryGetValue (theTrackDict, CFSTR(kDataKeyString)); + + /* Fill in the SDL_CD struct */ + int idx = theCD->numtracks++; + + CFNumberGetValue (trackNumber, kCFNumberSInt32Type, &value); + theCD->track[idx].id = value; + + CFNumberGetValue (startBlock, kCFNumberSInt32Type, &value); + theCD->track[idx].offset = value; + + theCD->track[idx].type = (isDataTrack == kCFBooleanTrue) ? SDL_DATA_TRACK : SDL_AUDIO_TRACK; + + /* Since the track lengths are not stored in .TOC.plist we compute them. */ + if (trackIndex > 0) { + theCD->track[idx-1].length = theCD->track[idx].offset - theCD->track[idx-1].offset; + } + } + + /* Compute the length of the last track */ + CFNumberGetValue (leadoutBlock, kCFNumberSInt32Type, &value); + + theCD->track[theCD->numtracks-1].length = + value - theCD->track[theCD->numtracks-1].offset; + + /* Set offset to leadout track */ + theCD->track[theCD->numtracks].offset = value; + } + + } + + theErr = 0; + goto cleanup; +bail: + SDL_SetError ("ReadTOCData: %s returned %d", error, theErr); + theErr = -1; +cleanup: + + if (propertyListRef != NULL) + CFRelease(propertyListRef); + if (dataRef != NULL) + CFRelease(dataRef); + if (forkData != NULL) + DisposePtr(forkData); + + FSCloseFork (forkRefNum); + + return theErr; +} + +int ListTrackFiles (FSVolumeRefNum theVolume, FSRef *trackFiles, int numTracks) +{ + OSStatus result = -1; + FSIterator iterator; + ItemCount actualObjects; + FSRef rootDirectory; + FSRef ref; + HFSUniStr255 nameStr; + + result = FSGetVolumeInfo (theVolume, + 0, + NULL, + kFSVolInfoFSInfo, + NULL, + NULL, + &rootDirectory); + + if (result != noErr) { + SDL_SetError ("ListTrackFiles: FSGetVolumeInfo returned %d", result); + return result; + } + + result = FSOpenIterator (&rootDirectory, kFSIterateFlat, &iterator); + if (result == noErr) { + do + { + result = FSGetCatalogInfoBulk (iterator, 1, &actualObjects, + NULL, kFSCatInfoNone, NULL, &ref, NULL, &nameStr); + if (result == noErr) { + + CFStringRef name; + name = CFStringCreateWithCharacters (NULL, nameStr.unicode, nameStr.length); + + /* Look for .aiff extension */ + if (CFStringHasSuffix (name, CFSTR(".aiff")) || + CFStringHasSuffix (name, CFSTR(".cdda"))) { + + /* Extract the track id from the filename */ + int trackID = 0, i = 0; + while (i < nameStr.length && !isdigit(nameStr.unicode[i])) { + ++i; + } + while (i < nameStr.length && isdigit(nameStr.unicode[i])) { + trackID = 10 * trackID +(nameStr.unicode[i] - '0'); + ++i; + } + + #if DEBUG_CDROM + printf("Found AIFF for track %d: '%s'\n", trackID, + CFStringGetCStringPtr (name, CFStringGetSystemEncoding())); + #endif + + /* Track ID's start at 1, but we want to start at 0 */ + trackID--; + + assert(0 <= trackID && trackID <= SDL_MAX_TRACKS); + + if (trackID < numTracks) + memcpy (&trackFiles[trackID], &ref, sizeof(FSRef)); + } + CFRelease (name); + } + } while(noErr == result); + FSCloseIterator (iterator); + } + + return 0; +} + +int LoadFile (const FSRef *ref, int startFrame, int stopFrame) +{ + int error = -1; + + if (CheckInit () < 0) + goto bail; + + /* release any currently playing file */ + if (ReleaseFile () < 0) + goto bail; + + #if DEBUG_CDROM + printf ("LoadFile: %d %d\n", startFrame, stopFrame); + #endif + + /*try {*/ + + /* create a new player, and attach to the audio unit */ + + thePlayer = new_AudioFilePlayer(ref); + if (thePlayer == NULL) { + SDL_SetError ("LoadFile: Could not create player"); + return -3; /*throw (-3);*/ + } + + if (!thePlayer->SetDestination(thePlayer, &theUnit)) + goto bail; + + if (startFrame >= 0) + thePlayer->SetStartFrame (thePlayer, startFrame); + + if (stopFrame >= 0 && stopFrame > startFrame) + thePlayer->SetStopFrame (thePlayer, stopFrame); + + /* we set the notifier later */ + /*thePlayer->SetNotifier(thePlayer, FilePlayNotificationHandler, NULL);*/ + + if (!thePlayer->Connect(thePlayer)) + goto bail; + + #if DEBUG_CDROM + thePlayer->Print(thePlayer); + fflush (stdout); + #endif + /*} + catch (...) + { + goto bail; + }*/ + + error = 0; + + bail: + return error; +} + +int ReleaseFile () +{ + int error = -1; + + /* (Don't see any way that the original C++ code could throw here.) --ryan. */ + /*try {*/ + if (thePlayer != NULL) { + + thePlayer->Disconnect(thePlayer); + + delete_AudioFilePlayer(thePlayer); + + thePlayer = NULL; + } + /*} + catch (...) + { + goto bail; + }*/ + + error = 0; + +/* bail: */ + return error; +} + +int PlayFile () +{ + OSStatus result = -1; + + if (CheckInit () < 0) + goto bail; + + /*try {*/ + + // start processing of the audio unit + result = AudioOutputUnitStart (theUnit); + if (result) goto bail; //THROW_RESULT("PlayFile: AudioOutputUnitStart") + + /*} + catch (...) + { + goto bail; + }*/ + + result = 0; + +bail: + return result; +} + +int PauseFile () +{ + OSStatus result = -1; + + if (CheckInit () < 0) + goto bail; + + /*try {*/ + + /* stop processing the audio unit */ + result = AudioOutputUnitStop (theUnit); + if (result) goto bail; /*THROW_RESULT("PauseFile: AudioOutputUnitStop")*/ + /*} + catch (...) + { + goto bail; + }*/ + + result = 0; +bail: + return result; +} + +void SetCompletionProc (CDPlayerCompletionProc proc, SDL_CD *cdrom) +{ + assert(thePlayer != NULL); + + theCDROM = cdrom; + completionProc = proc; + thePlayer->SetNotifier (thePlayer, FilePlayNotificationHandler, cdrom); +} + +int GetCurrentFrame () +{ + int frame; + + if (thePlayer == NULL) + frame = 0; + else + frame = thePlayer->GetCurrentFrame (thePlayer); + + return frame; +} + + +#pragma mark -- Private Functions -- + +static OSStatus CheckInit () +{ + if (playBackWasInit) + return 0; + + OSStatus result = noErr; + + /* Create the callback semaphore */ + callbackSem = SDL_CreateSemaphore(0); + + /* Start callback thread */ + SDL_CreateThread(RunCallBackThread, NULL); + + { /*try {*/ + ComponentDescription desc; + + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_DefaultOutput; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + Component comp = FindNextComponent (NULL, &desc); + if (comp == NULL) { + SDL_SetError ("CheckInit: FindNextComponent returned NULL"); + if (result) return -1; //throw(internalComponentErr); + } + + result = OpenAComponent (comp, &theUnit); + if (result) return -1; //THROW_RESULT("CheckInit: OpenAComponent") + + // you need to initialize the output unit before you set it as a destination + result = AudioUnitInitialize (theUnit); + if (result) return -1; //THROW_RESULT("CheckInit: AudioUnitInitialize") + + + playBackWasInit = true; + } + /*catch (...) + { + return -1; + }*/ + + return 0; +} + +static void FilePlayNotificationHandler(void * inRefCon, OSStatus inStatus) +{ + if (inStatus == kAudioFilePlay_FileIsFinished) { + + /* notify non-CA thread to perform the callback */ + SDL_SemPost(callbackSem); + + } else if (inStatus == kAudioFilePlayErr_FilePlayUnderrun) { + + SDL_SetError ("CDPlayer Notification: buffer underrun"); + } else if (inStatus == kAudioFilePlay_PlayerIsUninitialized) { + + SDL_SetError ("CDPlayer Notification: player is uninitialized"); + } else { + + SDL_SetError ("CDPlayer Notification: unknown error %ld", inStatus); + } +} + +static int RunCallBackThread (void *param) +{ + for (;;) { + + SDL_SemWait(callbackSem); + + if (completionProc && theCDROM) { + #if DEBUG_CDROM + printf ("callback!\n"); + #endif + (*completionProc)(theCDROM); + } else { + #if DEBUG_CDROM + printf ("callback?\n"); + #endif + } + } + + #if DEBUG_CDROM + printf ("thread dying now...\n"); + #endif + + return 0; +} + +/*}; // extern "C" */ diff --git a/apps/plugins/sdl/src/cdrom/macosx/CDPlayer.h b/apps/plugins/sdl/src/cdrom/macosx/CDPlayer.h new file mode 100644 index 0000000000..be1ac1826a --- /dev/null +++ b/apps/plugins/sdl/src/cdrom/macosx/CDPlayer.h @@ -0,0 +1,69 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifndef __CDPlayer__H__ +#define __CDPlayer__H__ 1 + +#include + +#include +#include +#include + +#include "SDL.h" +#include "SDL_thread.h" +#include "SDL_mutex.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void (*CDPlayerCompletionProc)(SDL_CD *cdrom) ; + +void Lock (); + +void Unlock(); + +int LoadFile (const FSRef *ref, int startFrame, int endFrame); /* pass -1 to do nothing */ + +int ReleaseFile (); + +int PlayFile (); + +int PauseFile (); + +void SetCompletionProc (CDPlayerCompletionProc proc, SDL_CD *cdrom); + +int ReadTOCData (FSVolumeRefNum theVolume, SDL_CD *theCD); + +int ListTrackFiles (FSVolumeRefNum theVolume, FSRef *trackFiles, int numTracks); + +int DetectAudioCDVolumes (FSVolumeRefNum *volumes, int numVolumes); + +int GetCurrentFrame (); + +#ifdef __cplusplus +}; +#endif + +#endif /* __CD_Player__H__ */ diff --git a/apps/plugins/sdl/src/cdrom/macosx/SDLOSXCAGuard.c b/apps/plugins/sdl/src/cdrom/macosx/SDLOSXCAGuard.c new file mode 100644 index 0000000000..e8caf1bf10 --- /dev/null +++ b/apps/plugins/sdl/src/cdrom/macosx/SDLOSXCAGuard.c @@ -0,0 +1,199 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* + Note: This file hasn't been modified so technically we have to keep the disclaimer :-( + + Copyright: © Copyright 2002 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CAGuard.cp + +=============================================================================*/ + +/*============================================================================= + Includes + =============================================================================*/ + +/* +#include +#include +#include +*/ +#include "SDL_stdinc.h" + +/*#define NDEBUG 1*/ +/* +#include +*/ +#define assert(X) + + +#include "SDLOSXCAGuard.h" + +/*#warning Need a try-based Locker too*/ +/*============================================================================= + SDLOSXCAGuard + =============================================================================*/ + +static int SDLOSXCAGuard_Lock(SDLOSXCAGuard *cag) +{ + int theAnswer = 0; + + if(pthread_self() != cag->mOwner) + { + OSStatus theError = pthread_mutex_lock(&cag->mMutex); + (void)theError; + assert(theError == 0); + cag->mOwner = pthread_self(); + theAnswer = 1; + } + + return theAnswer; +} + +static void SDLOSXCAGuard_Unlock(SDLOSXCAGuard *cag) +{ + OSStatus theError; + assert(pthread_self() == cag->mOwner); + + cag->mOwner = 0; + theError = pthread_mutex_unlock(&cag->mMutex); + (void)theError; + assert(theError == 0); +} + +static int SDLOSXCAGuard_Try (SDLOSXCAGuard *cag, int *outWasLocked) +{ + int theAnswer = 0; + *outWasLocked = 0; + + if (pthread_self() == cag->mOwner) { + theAnswer = 1; + *outWasLocked = 0; + } else { + OSStatus theError = pthread_mutex_trylock(&cag->mMutex); + if (theError == 0) { + cag->mOwner = pthread_self(); + theAnswer = 1; + *outWasLocked = 1; + } + } + + return theAnswer; +} + +static void SDLOSXCAGuard_Wait(SDLOSXCAGuard *cag) +{ + OSStatus theError; + assert(pthread_self() == cag->mOwner); + + cag->mOwner = 0; + + theError = pthread_cond_wait(&cag->mCondVar, &cag->mMutex); + (void)theError; + assert(theError == 0); + cag->mOwner = pthread_self(); +} + +static void SDLOSXCAGuard_Notify(SDLOSXCAGuard *cag) +{ + OSStatus theError = pthread_cond_signal(&cag->mCondVar); + (void)theError; + assert(theError == 0); +} + + +SDLOSXCAGuard *new_SDLOSXCAGuard(void) +{ + OSStatus theError; + SDLOSXCAGuard *cag = (SDLOSXCAGuard *) SDL_malloc(sizeof (SDLOSXCAGuard)); + if (cag == NULL) + return NULL; + SDL_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 + + theError = pthread_mutex_init(&cag->mMutex, NULL); + (void)theError; + assert(theError == 0); + + theError = pthread_cond_init(&cag->mCondVar, NULL); + (void)theError; + 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); + SDL_free(cag); + } +} + diff --git a/apps/plugins/sdl/src/cdrom/macosx/SDLOSXCAGuard.h b/apps/plugins/sdl/src/cdrom/macosx/SDLOSXCAGuard.h new file mode 100644 index 0000000000..f22c6956fe --- /dev/null +++ b/apps/plugins/sdl/src/cdrom/macosx/SDLOSXCAGuard.h @@ -0,0 +1,116 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* + Note: This file hasn't been modified so technically we have to keep the disclaimer :-( + + + Copyright: © Copyright 2002 Apple Computer, Inc. All rights reserved. + + Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Computer, Inc. + ("Apple") in consideration of your agreement to the following terms, and your + use, installation, modification or redistribution of this Apple software + constitutes acceptance of these terms. If you do not agree with these terms, + please do not use, install, modify or redistribute this Apple software. + + In consideration of your agreement to abide by the following terms, and subject + to these terms, Apple grants you a personal, non-exclusive license, under AppleÕs + copyrights in this original Apple software (the "Apple Software"), to use, + reproduce, modify and redistribute the Apple Software, with or without + modifications, in source and/or binary forms; provided that if you redistribute + the Apple Software in its entirety and without modifications, you must retain + this notice and the following text and disclaimers in all such redistributions of + the Apple Software. Neither the name, trademarks, service marks or logos of + Apple Computer, Inc. may be used to endorse or promote products derived from the + Apple Software without specific prior written permission from Apple. Except as + expressly stated in this notice, no other rights or licenses, express or implied, + are granted by Apple herein, including but not limited to any patent rights that + may be infringed by your derivative works or by other works in which the Apple + Software may be incorporated. + + The Apple Software is provided by Apple on an "AS IS" basis. APPLE MAKES NO + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED + WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN + COMBINATION WITH YOUR PRODUCTS. + + IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE + GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION + OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT + (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/*============================================================================= + CAGuard.h + +=============================================================================*/ +#if !defined(__CAGuard_h__) +#define __CAGuard_h__ + +/*============================================================================= + Includes + =============================================================================*/ + +#include +#include + + +/*============================================================================= + CAGuard + + This is your typical mutex with signalling implemented via pthreads. + Lock() will return true if and only if the guard is locked on that call. + A thread that already has the guard will receive 'false' if it locks it + again. Use of the stack-based CAGuard::Locker class is highly recommended + to properly manage the recursive nesting. The Wait calls with timeouts + will return true if and only if the timeout period expired. They will + return false if they receive notification any other way. + =============================================================================*/ + +typedef struct S_SDLOSXCAGuard +{ + +/* Construction/Destruction */ +/*public:*/ +/* Actions */ +/*public:*/ + int (*Lock)(struct S_SDLOSXCAGuard *cag); + void (*Unlock)(struct S_SDLOSXCAGuard *cag); + int (*Try)(struct S_SDLOSXCAGuard *cag, int *outWasLocked); /* returns true if lock is free, false if not */ + void (*Wait)(struct S_SDLOSXCAGuard *cag); + void (*Notify)(struct S_SDLOSXCAGuard *cag); + +/* Implementation */ +/*protected:*/ + pthread_mutex_t mMutex; + pthread_cond_t mCondVar; + pthread_t mOwner; +} SDLOSXCAGuard; + +SDLOSXCAGuard *new_SDLOSXCAGuard(void); +void delete_SDLOSXCAGuard(SDLOSXCAGuard *cag); + +#endif + diff --git a/apps/plugins/sdl/src/cdrom/macosx/SDL_syscdrom.c b/apps/plugins/sdl/src/cdrom/macosx/SDL_syscdrom.c new file mode 100644 index 0000000000..5018750222 --- /dev/null +++ b/apps/plugins/sdl/src/cdrom/macosx/SDL_syscdrom.c @@ -0,0 +1,514 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#ifdef SDL_CDROM_MACOSX + +#include "SDL_syscdrom_c.h" + +#pragma mark -- Globals -- + +static FSRef** tracks; +static FSVolumeRefNum* volumes; +static CDstatus status; +static int nextTrackFrame; +static int nextTrackFramesRemaining; +static int fakeCD; +static int currentTrack; +static int didReadTOC; +static int cacheTOCNumTracks; +static int currentDrive; /* Only allow 1 drive in use at a time */ + +#pragma mark -- Prototypes -- + +static const char *SDL_SYS_CDName (int drive); +static int SDL_SYS_CDOpen (int drive); +static int SDL_SYS_CDGetTOC (SDL_CD *cdrom); +static CDstatus SDL_SYS_CDStatus (SDL_CD *cdrom, int *position); +static int SDL_SYS_CDPlay (SDL_CD *cdrom, int start, int length); +static int SDL_SYS_CDPause (SDL_CD *cdrom); +static int SDL_SYS_CDResume (SDL_CD *cdrom); +static int SDL_SYS_CDStop (SDL_CD *cdrom); +static int SDL_SYS_CDEject (SDL_CD *cdrom); +static void SDL_SYS_CDClose (SDL_CD *cdrom); + +#pragma mark -- Helper Functions -- + +/* Read a list of tracks from the volume */ +static int LoadTracks (SDL_CD *cdrom) +{ + /* Check if tracks are already loaded */ + if ( tracks[cdrom->id] != NULL ) + return 0; + + /* Allocate memory for tracks */ + tracks[cdrom->id] = (FSRef*) SDL_calloc (1, sizeof(**tracks) * cdrom->numtracks); + if (tracks[cdrom->id] == NULL) { + SDL_OutOfMemory (); + return -1; + } + + /* Load tracks */ + if (ListTrackFiles (volumes[cdrom->id], tracks[cdrom->id], cdrom->numtracks) < 0) + return -1; + + return 0; +} + +/* Find a file for a given start frame and length */ +static FSRef* GetFileForOffset (SDL_CD *cdrom, int start, int length, int *outStartFrame, int *outStopFrame) +{ + int i; + + for (i = 0; i < cdrom->numtracks; i++) { + + if (cdrom->track[i].offset <= start && + start < (cdrom->track[i].offset + cdrom->track[i].length)) + break; + } + + if (i == cdrom->numtracks) + return NULL; + + currentTrack = i; + + *outStartFrame = start - cdrom->track[i].offset; + + if ((*outStartFrame + length) < cdrom->track[i].length) { + *outStopFrame = *outStartFrame + length; + length = 0; + nextTrackFrame = -1; + nextTrackFramesRemaining = -1; + } + else { + *outStopFrame = -1; + length -= cdrom->track[i].length - *outStartFrame; + nextTrackFrame = cdrom->track[i+1].offset; + nextTrackFramesRemaining = length; + } + + return &tracks[cdrom->id][i]; +} + +/* Setup another file for playback, or stop playback (called from another thread) */ +static void CompletionProc (SDL_CD *cdrom) +{ + + Lock (); + + if (nextTrackFrame > 0 && nextTrackFramesRemaining > 0) { + + /* Load the next file to play */ + int startFrame, stopFrame; + FSRef *file; + + PauseFile (); + ReleaseFile (); + + file = GetFileForOffset (cdrom, nextTrackFrame, + nextTrackFramesRemaining, &startFrame, &stopFrame); + + if (file == NULL) { + status = CD_STOPPED; + Unlock (); + return; + } + + LoadFile (file, startFrame, stopFrame); + + SetCompletionProc (CompletionProc, cdrom); + + PlayFile (); + } + else { + + /* Release the current file */ + PauseFile (); + ReleaseFile (); + status = CD_STOPPED; + } + + Unlock (); +} + + +#pragma mark -- Driver Functions -- + +/* Initialize */ +int SDL_SYS_CDInit (void) +{ + /* Initialize globals */ + volumes = NULL; + tracks = NULL; + status = CD_STOPPED; + nextTrackFrame = -1; + nextTrackFramesRemaining = -1; + fakeCD = SDL_FALSE; + currentTrack = -1; + didReadTOC = SDL_FALSE; + cacheTOCNumTracks = -1; + currentDrive = -1; + + /* Fill in function pointers */ + SDL_CDcaps.Name = SDL_SYS_CDName; + SDL_CDcaps.Open = SDL_SYS_CDOpen; + SDL_CDcaps.GetTOC = SDL_SYS_CDGetTOC; + SDL_CDcaps.Status = SDL_SYS_CDStatus; + SDL_CDcaps.Play = SDL_SYS_CDPlay; + SDL_CDcaps.Pause = SDL_SYS_CDPause; + SDL_CDcaps.Resume = SDL_SYS_CDResume; + SDL_CDcaps.Stop = SDL_SYS_CDStop; + SDL_CDcaps.Eject = SDL_SYS_CDEject; + SDL_CDcaps.Close = SDL_SYS_CDClose; + + /* + Read the list of "drives" + + This is currently a hack that infers drives from + mounted audio CD volumes, rather than + actual CD-ROM devices - which means it may not + act as expected sometimes. + */ + + /* Find out how many cd volumes are mounted */ + SDL_numcds = DetectAudioCDVolumes (NULL, 0); + + /* + If there are no volumes, fake a cd device + so tray empty can be reported. + */ + if (SDL_numcds == 0) { + + fakeCD = SDL_TRUE; + SDL_numcds = 1; + status = CD_TRAYEMPTY; + + return 0; + } + + /* Allocate space for volumes */ + volumes = (FSVolumeRefNum*) SDL_calloc (1, sizeof(*volumes) * SDL_numcds); + if (volumes == NULL) { + SDL_OutOfMemory (); + return -1; + } + + /* Allocate space for tracks */ + tracks = (FSRef**) SDL_calloc (1, sizeof(*tracks) * (SDL_numcds + 1)); + if (tracks == NULL) { + SDL_OutOfMemory (); + return -1; + } + + /* Mark the end of the tracks array */ + tracks[ SDL_numcds ] = (FSRef*)-1; + + /* + Redetect, now save all volumes for later + Update SDL_numcds just in case it changed + */ + { + int numVolumes = SDL_numcds; + + SDL_numcds = DetectAudioCDVolumes (volumes, numVolumes); + + /* If more cds suddenly show up, ignore them */ + if (SDL_numcds > numVolumes) { + SDL_SetError ("Some CD's were added but they will be ignored"); + SDL_numcds = numVolumes; + } + } + + return 0; +} + +/* Shutdown and cleanup */ +void SDL_SYS_CDQuit(void) +{ + ReleaseFile(); + + if (volumes != NULL) + free (volumes); + + if (tracks != NULL) { + + FSRef **ptr; + for (ptr = tracks; *ptr != (FSRef*)-1; ptr++) + if (*ptr != NULL) + free (*ptr); + + free (tracks); + } +} + +/* Get the Unix disk name of the volume */ +static const char *SDL_SYS_CDName (int drive) +{ + /* + * !!! FIXME: PBHGetVolParmsSync() is gone in 10.6, + * !!! FIXME: replaced with FSGetVolumeParms(), which + * !!! FIXME: isn't available before 10.5. :/ + */ + return "Mac OS X CD-ROM Device"; + +#if 0 + OSStatus err = noErr; + HParamBlockRec pb; + GetVolParmsInfoBuffer volParmsInfo; + + if (fakeCD) + return "Fake CD-ROM Device"; + + pb.ioParam.ioNamePtr = NULL; + pb.ioParam.ioVRefNum = volumes[drive]; + pb.ioParam.ioBuffer = (Ptr)&volParmsInfo; + pb.ioParam.ioReqCount = (SInt32)sizeof(volParmsInfo); + err = PBHGetVolParmsSync(&pb); + + if (err != noErr) { + SDL_SetError ("PBHGetVolParmsSync returned %d", err); + return NULL; + } + + return volParmsInfo.vMDeviceID; +#endif +} + +/* Open the "device" */ +static int SDL_SYS_CDOpen (int drive) +{ + /* Only allow 1 device to be open */ + if (currentDrive >= 0) { + SDL_SetError ("Only one cdrom is supported"); + return -1; + } + else + currentDrive = drive; + + return drive; +} + +/* Get the table of contents */ +static int SDL_SYS_CDGetTOC (SDL_CD *cdrom) +{ + if (fakeCD) { + SDL_SetError (kErrorFakeDevice); + return -1; + } + + if (didReadTOC) { + cdrom->numtracks = cacheTOCNumTracks; + return 0; + } + + + ReadTOCData (volumes[cdrom->id], cdrom); + didReadTOC = SDL_TRUE; + cacheTOCNumTracks = cdrom->numtracks; + + return 0; +} + +/* Get CD-ROM status */ +static CDstatus SDL_SYS_CDStatus (SDL_CD *cdrom, int *position) +{ + if (position) { + int trackFrame; + + Lock (); + trackFrame = GetCurrentFrame (); + Unlock (); + + *position = cdrom->track[currentTrack].offset + trackFrame; + } + + return status; +} + +/* Start playback */ +static int SDL_SYS_CDPlay(SDL_CD *cdrom, int start, int length) +{ + int startFrame, stopFrame; + FSRef *ref; + + if (fakeCD) { + SDL_SetError (kErrorFakeDevice); + return -1; + } + + Lock(); + + if (LoadTracks (cdrom) < 0) + return -2; + + if (PauseFile () < 0) + return -3; + + if (ReleaseFile () < 0) + return -4; + + ref = GetFileForOffset (cdrom, start, length, &startFrame, &stopFrame); + if (ref == NULL) { + SDL_SetError ("SDL_SYS_CDPlay: No file for start=%d, length=%d", start, length); + return -5; + } + + if (LoadFile (ref, startFrame, stopFrame) < 0) + return -6; + + SetCompletionProc (CompletionProc, cdrom); + + if (PlayFile () < 0) + return -7; + + status = CD_PLAYING; + + Unlock(); + + return 0; +} + +/* Pause playback */ +static int SDL_SYS_CDPause(SDL_CD *cdrom) +{ + if (fakeCD) { + SDL_SetError (kErrorFakeDevice); + return -1; + } + + Lock (); + + if (PauseFile () < 0) { + Unlock (); + return -2; + } + + status = CD_PAUSED; + + Unlock (); + + return 0; +} + +/* Resume playback */ +static int SDL_SYS_CDResume(SDL_CD *cdrom) +{ + if (fakeCD) { + SDL_SetError (kErrorFakeDevice); + return -1; + } + + Lock (); + + if (PlayFile () < 0) { + Unlock (); + return -2; + } + + status = CD_PLAYING; + + Unlock (); + + return 0; +} + +/* Stop playback */ +static int SDL_SYS_CDStop(SDL_CD *cdrom) +{ + if (fakeCD) { + SDL_SetError (kErrorFakeDevice); + return -1; + } + + Lock (); + + if (PauseFile () < 0) { + Unlock (); + return -2; + } + + if (ReleaseFile () < 0) { + Unlock (); + return -3; + } + + status = CD_STOPPED; + + Unlock (); + + return 0; +} + +/* Eject the CD-ROM (Unmount the volume) */ +static int SDL_SYS_CDEject(SDL_CD *cdrom) +{ + OSStatus err; + pid_t dissenter; + + if (fakeCD) { + SDL_SetError (kErrorFakeDevice); + return -1; + } + + Lock (); + + if (PauseFile () < 0) { + Unlock (); + return -2; + } + + if (ReleaseFile () < 0) { + Unlock (); + return -3; + } + + status = CD_STOPPED; + + /* Eject the volume */ + err = FSEjectVolumeSync(volumes[cdrom->id], kNilOptions, &dissenter); + + if (err != noErr) { + Unlock (); + SDL_SetError ("PBUnmountVol returned %d", err); + return -4; + } + + status = CD_TRAYEMPTY; + + /* Invalidate volume and track info */ + volumes[cdrom->id] = 0; + free (tracks[cdrom->id]); + tracks[cdrom->id] = NULL; + + Unlock (); + + return 0; +} + +/* Close the CD-ROM */ +static void SDL_SYS_CDClose(SDL_CD *cdrom) +{ + currentDrive = -1; + return; +} + +#endif /* SDL_CDROM_MACOSX */ diff --git a/apps/plugins/sdl/src/cdrom/macosx/SDL_syscdrom_c.h b/apps/plugins/sdl/src/cdrom/macosx/SDL_syscdrom_c.h new file mode 100644 index 0000000000..589c5897e6 --- /dev/null +++ b/apps/plugins/sdl/src/cdrom/macosx/SDL_syscdrom_c.h @@ -0,0 +1,136 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +/* This is the Mac OS X / CoreAudio specific header for the SDL CD-ROM API + Contributed by Darrell Walisser and Max Horn + */ + +/*********************************************************************************** + Implementation Notes + ********************* + + This code has several limitations currently (all of which are proabaly fixable): + + 1. A CD-ROM device is inferred from a mounted cdfs volume, so device 0 is + not necessarily the first CD-ROM device on the system. (Somewhat easy to fix + by useing the device name from the volume id's to reorder the volumes) + + 2. You can only open and control 1 CD-ROM device at a time. (Challenging to fix, + due to extensive code restructuring) + + 3. The status reported by SDL_CDStatus only changes to from CD_PLAYING to CD_STOPPED in + 1-second intervals (because the audio is buffered in 1-second chunks) If + the audio data is less than 1 second, the remainder is filled with silence. + + If you need to play sequences back-to-back that are less that 1 second long, + use the frame position to determine when to play the next sequence, instead + of SDL_CDStatus. + + This may be possible to fix with a clever usage of the AudioUnit API. + + 4. When new volumes are inserted, our volume information is not updated. The only way + to refresh this information is to reinit the CD-ROM subsystem of SDL. To fix this, + one would probably have to fix point 1 above first, then figure out how to register + for a notification when new media is mounted in order to perform an automatic + rescan for cdfs volumes. + + + + So, here comes a description of how this all works. + + < Initializing > + + To get things rolling, we have to locate mounted volumes that contain + audio (since nearly all Macs don't have analog audio-in on the sound card). + That's easy, since these volumes have a flag that indicates this special + filesystem. See DetectAudioCDVolumes() in CDPlayer.cpp for this code. + + Next, we parse the invisible .TOC.plist in the root of the volume, which gets us + the track information (number, offset, length, leadout, etc). See ReadTOCData() in + CDPlayer.cpp for the skinny on this. + + + < The Playback Loop > + + Now come the tricky parts. Let's start with basic audio playback. When a frame + range to play is requested, we must first find the .aiff files on the volume, + hopefully in the right order. Since these files all begin with a number "1 Audio Track", + etc, this is used to determine the correct track order. + + Once all files are determined, we have to find what file corresponds to the start + and length parameter to SDL_SYS_CDPlay(). Again, this is quite simple by walking the + cdrom's track list. At this point, we also save the offset to the next track and frames + remaining, if we're going to have to play another file after the first one. See + GetFileForOffset() for this code. + + At this point we have all info needed to start playback, so we hand off to the LoadFile() + function, which proceeds to do its magic and plays back the file. + + When the file is finished playing, CompletionProc() is invoked, at which time we can + play the next file if the previously saved next track and frames remaining + indicates that we should. + + + < Magic > + + OK, so it's not really magic, but since I don't fully understand all the hidden details it + seems like it to me ;-) The API's involved are the AudioUnit and AudioFile API's. These + appear to be an extension of CoreAudio for creating modular playback and f/x entities. + The important thing is that CPU usage is very low and reliability is very high. You'd + be hard-pressed to find a way to stutter the playback with other CPU-intensive tasks. + + One part of this magic is that it uses multiple threads, which carries the usual potential + for disaster if not handled carefully. Playback currently requires 4 additional threads: + 1. The coreaudio runloop thread + 2. The coreaudio device i/o thread + 3. The file streaming thread + 4. The notification/callback thread + + The first 2 threads are necessary evil - CoreAudio creates this no matter what the situation + is (even the SDL sound implementation creates theses suckers). The last two are are created + by us. + + The file is streamed from disk using a threaded double-buffer approach. + This way, the high latency operation of reading from disk can be performed without interrupting + the real-time device thread (which amounts to avoiding dropouts). The device thread grabs the + buffer that isn't being read and sends it to the CoreAudio mixer where it eventually gets + to the sound card. + + The device thread posts a notification when the file streaming thread is out of data. This + notification must be handled in a separate thread to avoid potential deadlock in the + device thread. That's where the notification thread comes in. This thread is signaled + whenever a notification needs to be processed, so another file can be played back if need be. + + The API in CDPlayer.cpp contains synchronization because otherwise both the notification thread + and main thread (or another other thread using the SDL CD api) can potentially call it at the same time. + +************************************************************************************/ + + +#include "SDL_cdrom.h" +#include "../SDL_syscdrom.h" + +#include "CDPlayer.h" + +#define kErrorFakeDevice "Error: Cannot proceed since we're faking a CD-ROM device. Reinit the CD-ROM subsystem to scan for new volumes." + -- cgit v1.2.3