--- a/configure.in Tue Apr 15 16:04:31 2003 +0000
+++ b/configure.in Tue Apr 15 16:33:56 2003 +0000
@@ -2404,8 +2404,9 @@
fi
# Set up files for the cdrom library
if test x$enable_cdrom = xyes; then
- CDROM_SUBDIRS="$CDROM_SUBDIRS dummy"
- CDROM_DRIVERS="$CDROM_DRIVERS dummy/libcdrom_dummy.la"
+ CDROM_SUBDIRS="$CDROM_SUBDIRS macosx"
+ CDROM_DRIVERS="$CDROM_DRIVERS macosx/libcdrom_macosx.la"
+ SYSTEM_LIBS="$SYSTEM_LIBS -framework AudioToolbox -framework AudioUnit -lstdc++"
fi
# Set up files for the thread library
if test x$enable_threads = xyes; then
@@ -2672,6 +2673,7 @@
src/cdrom/freebsd/Makefile
src/cdrom/linux/Makefile
src/cdrom/macos/Makefile
+src/cdrom/macosx/Makefile
src/cdrom/openbsd/Makefile
src/cdrom/qnx/Makefile
src/cdrom/win32/Makefile
--- a/src/cdrom/Makefile.am Tue Apr 15 16:04:31 2003 +0000
+++ b/src/cdrom/Makefile.am Tue Apr 15 16:33:56 2003 +0000
@@ -5,7 +5,19 @@
# Define which subdirectories need to be built
SUBDIRS = @CDROM_SUBDIRS@
-DIST_SUBDIRS = aix beos bsdi dc dummy freebsd linux macos openbsd qnx win32
+DIST_SUBDIRS = \
+ aix \
+ beos \
+ bsdi \
+ dc \
+ dummy \
+ freebsd \
+ linux \
+ macos \
+ macosx \
+ openbsd \
+ qnx \
+ win32
DRIVERS = @CDROM_DRIVERS@
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cdrom/macosx/AudioFilePlayer.cpp Tue Apr 15 16:33:56 2003 +0000
@@ -0,0 +1,377 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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
+*/
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// AudioFilePlayer.cpp
+//
+#include "AudioFilePlayer.h"
+
+extern const char* AudioFilePlayerErrorStr (OSStatus error)
+{
+ const char *str;
+
+ switch (error) {
+ case kAudioFileUnspecifiedError: str = "wht?"; break;
+ case kAudioFileUnsupportedFileTypeError: str = "typ?"; break;
+ case kAudioFileUnsupportedDataFormatError: str = "fmt?"; break;
+ case kAudioFileUnsupportedPropertyError: str = "pty?"; break;
+ case kAudioFileBadPropertySizeError: str = "!siz"; break;
+ case kAudioFileNotOptimizedError: str = "optm"; break;
+ case kAudioFilePermissionsError: str = "prm?"; break;
+ case kAudioFileFormatNameUnavailableError: str = "nme?"; break;
+ case kAudioFileInvalidChunkError: str = "chk?"; break;
+ case kAudioFileDoesNotAllow64BitDataSizeError: str = "off?"; break;
+ default: str = "error unspecified";
+ }
+
+ return str;
+}
+
+void ThrowResult (OSStatus result, const char* str)
+{
+ SDL_SetError ("Error: %s %d (%s)",
+ str, result, AudioFilePlayerErrorStr(result));
+ throw result;
+}
+
+#if DEBUG
+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
+
+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 = AudioConverterFillBuffer(mParentConverter,
+ AudioFileManager::ACInputProc,
+ this,
+ &ioData.mDataByteSize,
+ ioData.mData);
+ if (result) {
+ SDL_SetError ("AudioConverterFillBuffer:%ld\n", result);
+ mParent.DoNotification (result);
+ } else {
+ mByteCounter += ioData.mDataByteSize / 2;
+ AfterRender();
+ }
+ return result;
+}
+
+OSStatus AudioFileManager::ACInputProc (AudioConverterRef inAudioConverter,
+ UInt32* outDataSize,
+ void** outData,
+ void* inUserData)
+{
+ AudioFileManager* THIS = (AudioFileManager*)inUserData;
+ return THIS->GetFileData(outData, outDataSize);
+}
+
+AudioFileManager::~AudioFileManager ()
+{
+ if (mFileBuffer) {
+ free (mFileBuffer);
+ mFileBuffer = 0;
+ }
+}
+
+AudioFilePlayer::AudioFilePlayer (const FSRef *inFileRef)
+ : mConnected (false),
+ mAudioFileManager (0),
+ mConverter (0),
+ mNotifier (0),
+ mStartFrame (0)
+{
+ SInt64 fileDataSize = 0;
+
+ OpenFile (inFileRef, fileDataSize);
+
+ // we want about a seconds worth of data for the buffer
+ int secsBytes = UInt32 (mFileDescription.mSampleRate * mFileDescription.mBytesPerFrame);
+
+#if DEBUG
+ printf("File format:\n");
+ PrintStreamDesc (&mFileDescription);
+#endif
+
+ //round to a 32K boundary
+ //if ((secsBytes & 0xFFFF8000) > (128 * 1024))
+ //secsBytes &= 0xFFFF8000;
+ //else
+ //secsBytes = (secsBytes + 0x7FFF) & 0xFFFF8000;
+
+ mAudioFileManager = new AudioFileReaderThread (*this,
+ mAudioFileID,
+ fileDataSize,
+ secsBytes);
+}
+
+// you can put a rate scalar here to play the file faster or slower
+// by multiplying the same rate by the desired factor
+// eg fileSampleRate * 2 -> twice as fast
+// before you create the AudioConverter
+void AudioFilePlayer::SetDestination (AudioUnit &inDestUnit,
+ int inBusNumber)
+{
+ if (mConnected) throw static_cast<OSStatus>(-1); //can't set dest if already engaged
+
+ mPlayUnit = inDestUnit;
+ mBusNumber = inBusNumber;
+
+ OSStatus result = noErr;
+
+ if (mConverter) {
+ result = AudioConverterDispose (mConverter);
+ THROW_RESULT("AudioConverterDispose")
+ }
+
+ AudioStreamBasicDescription destDesc;
+ UInt32 size = sizeof (destDesc);
+ result = AudioUnitGetProperty (inDestUnit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input,
+ inBusNumber,
+ &destDesc,
+ &size);
+ THROW_RESULT("AudioUnitGetProperty")
+
+#if DEBUG
+ printf("Destination format:\n");
+ PrintStreamDesc (&destDesc);
+#endif
+
+ //we can "down" cast a component instance to a component
+ ComponentDescription desc;
+ result = GetComponentInfo ((Component)inDestUnit, &desc, 0, 0, 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.
+ mIsAUNTUnit = (desc.componentType == kAudioUnitComponentType);
+
+ if (!mIsAUNTUnit) {
+ result = badComponentInstance;
+ THROW_RESULT("BAD COMPONENT")
+ }
+
+
+ // HACK - the AIFF files on CDs are in little endian order!
+ if (mFileDescription.mFormatFlags == 0xE)
+ mFileDescription.mFormatFlags &= ~kAudioFormatFlagIsBigEndian;
+
+
+ result = AudioConverterNew (&mFileDescription, &destDesc, &mConverter);
+ THROW_RESULT("AudioConverterNew")
+
+
+/*
+ // if we have a mono source, we're going to copy each channel into
+ // the destination's channel source...
+ if (mFileDescription.mChannelsPerFrame == 1) {
+
+ SInt32* channelMap = new SInt32 [destDesc.mChannelsPerFrame];
+ for (unsigned int i = 0; i < destDesc.mChannelsPerFrame; ++i)
+ channelMap[i] = 0; //set first channel to all output channels
+
+ result = AudioConverterSetProperty(mConverter,
+ kAudioConverterChannelMap,
+ (sizeof(SInt32) * destDesc.mChannelsPerFrame),
+ channelMap);
+ THROW_RESULT("AudioConverterSetProperty")
+
+ delete [] channelMap;
+ }
+*/
+ assert (mFileDescription.mChannelsPerFrame == 2);
+
+#if 0
+ // this uses the better quality SRC
+ UInt32 srcID = kAudioUnitSRCAlgorithm_Polyphase;
+ result = AudioConverterSetProperty(mConverter,
+ kAudioConverterSampleRateConverterAlgorithm,
+ sizeof(srcID),
+ &srcID);
+ THROW_RESULT("AudioConverterSetProperty")
+#endif
+}
+
+void AudioFilePlayer::SetStartFrame (int frame)
+{
+ SInt64 position = frame * 2352;
+
+ mStartFrame = frame;
+ mAudioFileManager->SetPosition (position);
+}
+
+
+int AudioFilePlayer::GetCurrentFrame ()
+{
+ return mStartFrame + (mAudioFileManager->GetByteCounter() / 2352);
+}
+
+void AudioFilePlayer::SetStopFrame (int frame)
+{
+ SInt64 position = frame * 2352;
+
+ mAudioFileManager->SetEndOfFile (position);
+}
+
+AudioFilePlayer::~AudioFilePlayer()
+{
+ Disconnect();
+
+ if (mAudioFileManager) {
+ delete mAudioFileManager;
+ mAudioFileManager = 0;
+ }
+
+ if (mAudioFileID) {
+ ::AudioFileClose (mAudioFileID);
+ mAudioFileID = 0;
+ }
+
+ if (mConverter) {
+ AudioConverterDispose (mConverter);
+ mConverter = 0;
+ }
+}
+
+void AudioFilePlayer::Connect()
+{
+#if DEBUG
+ printf ("Connect:%x,%ld, engaged=%d\n", (int)mPlayUnit, mBusNumber, (mConnected ? 1 : 0));
+#endif
+ if (!mConnected)
+ {
+ mAudioFileManager->Connect(mConverter);
+
+ // set the render callback for the file data to be supplied to the sound converter AU
+ if (mIsAUNTUnit) {
+ mInputCallback.inputProc = AudioFileManager::FileInputProc;
+ mInputCallback.inputProcRefCon = mAudioFileManager;
+
+ OSStatus result = AudioUnitSetProperty (mPlayUnit,
+ kAudioUnitProperty_SetInputCallback,
+ kAudioUnitScope_Input,
+ mBusNumber,
+ &mInputCallback,
+ sizeof(mInputCallback));
+ THROW_RESULT("AudioUnitSetProperty")
+ }
+ mConnected = true;
+ }
+}
+
+// warning noted, now please go away ;-)
+// #warning This should redirect the calling of notification code to some other thread
+void AudioFilePlayer::DoNotification (OSStatus inStatus) const
+{
+ AudioFilePlayer* THIS = const_cast<AudioFilePlayer*>(this);
+
+ if (mNotifier) {
+ (*mNotifier) (mRefCon, inStatus);
+ }
+
+ else {
+ SDL_SetError ("Notification posted with no notifier in place");
+
+ if (inStatus == kAudioFilePlay_FileIsFinished)
+ THIS->Disconnect();
+ else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun)
+ THIS->Disconnect();
+ }
+}
+
+void AudioFilePlayer::Disconnect ()
+{
+#if DEBUG
+ printf ("Disconnect:%x,%ld, engaged=%d\n", (int)mPlayUnit, mBusNumber, (mConnected ? 1 : 0));
+#endif
+ if (mConnected)
+ {
+ mConnected = false;
+
+ if (mIsAUNTUnit) {
+ mInputCallback.inputProc = 0;
+ mInputCallback.inputProcRefCon = 0;
+ OSStatus result = AudioUnitSetProperty (mPlayUnit,
+ kAudioUnitProperty_SetInputCallback,
+ kAudioUnitScope_Input,
+ mBusNumber,
+ &mInputCallback,
+ sizeof(mInputCallback));
+ if (result)
+ SDL_SetError ("AudioUnitSetProperty:RemoveInputCallback:%ld", result);
+
+ }
+
+ mAudioFileManager->Disconnect();
+ }
+}
+
+void AudioFilePlayer::OpenFile (const FSRef *inRef, SInt64& outFileDataSize)
+{
+ OSStatus result = AudioFileOpen (inRef, fsRdPerm, 0, &mAudioFileID);
+ THROW_RESULT("AudioFileOpen")
+
+ UInt32 dataSize = sizeof(AudioStreamBasicDescription);
+ result = AudioFileGetProperty (mAudioFileID,
+ kAudioFilePropertyDataFormat,
+ &dataSize,
+ &mFileDescription);
+ THROW_RESULT("AudioFileGetProperty")
+
+ dataSize = sizeof (SInt64);
+ result = AudioFileGetProperty (mAudioFileID,
+ kAudioFilePropertyAudioDataByteCount,
+ &dataSize,
+ &outFileDataSize);
+ THROW_RESULT("AudioFileGetProperty")
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cdrom/macosx/AudioFilePlayer.h Tue Apr 15 16:33:56 2003 +0000
@@ -0,0 +1,240 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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
+*/
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// AudioFilePlayer.h
+//
+#ifndef __AudioFilePlayer_H__
+#define __AudioFilePlayer_H__
+
+#include <CoreServices/CoreServices.h>
+
+#include <AudioToolbox/AudioToolbox.h>
+#include <AudioUnit/AudioUnit.h>
+
+#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
+};
+
+
+class AudioFileManager;
+
+#pragma mark __________ AudioFilePlayer
+class AudioFilePlayer
+{
+public:
+ AudioFilePlayer (const FSRef *inFileRef);
+
+ ~AudioFilePlayer();
+
+ void SetDestination (AudioUnit &inDestUnit,
+ int inBusNumber);
+
+ void SetNotifier (AudioFilePlayNotifier inNotifier, void *inRefCon)
+ {
+ mNotifier = inNotifier;
+ mRefCon = inRefCon;
+ }
+
+ void SetStartFrame (int frame); // seek in the file
+
+ 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; }
+
+ UInt32 GetBusNumber () const { return mBusNumber; }
+
+ AudioUnit GetDestUnit () const { return mPlayUnit; }
+
+ AudioConverterRef GetAudioConverter() const { return mConverter; }
+
+#if DEBUG
+ void Print() const
+ {
+ CAShow (mAudioFileID);
+ printf ("Destination Bus:%ld\n", GetBusNumber());
+ printf ("Is 'aunt' unit:%s\n", (mIsAUNTUnit ? "true" : "false"));
+ printf ("Is Connected:%s\n", (IsConnected() ? "true" : "false"));
+ if (mConverter) CAShow (mConverter);
+ printf ("- - - - - - - - - - - - - - \n");
+ }
+#endif
+
+ const AudioStreamBasicDescription& GetFileFormat() const { return mFileDescription; }
+
+private:
+ AudioUnit mPlayUnit;
+ UInt32 mBusNumber;
+ AudioFileID mAudioFileID;
+
+ AudioUnitInputCallback mInputCallback;
+
+ AudioStreamBasicDescription mFileDescription;
+
+ bool mConnected;
+ bool mIsAUNTUnit;
+
+ AudioFileManager* mAudioFileManager;
+ AudioConverterRef mConverter;
+
+ AudioFilePlayNotifier mNotifier;
+ void* mRefCon;
+
+ int mStartFrame;
+
+#pragma mark __________ Private_Methods
+
+ void OpenFile (const FSRef *inRef, SInt64& outFileSize);
+};
+
+#pragma mark __________ AudioFileManager
+class AudioFileManager
+{
+public:
+ AudioFileManager (AudioFilePlayer& inParent, AudioFileID inFile)
+ : mParent (inParent),
+ mAudioFileID (inFile),
+ mFileBuffer (0),
+ mByteCounter (0)
+ {}
+
+ virtual ~AudioFileManager();
+
+
+ void Connect (AudioConverterRef inConverter)
+ {
+ mParentConverter = inConverter;
+ DoConnect();
+ }
+
+ // this method should NOT be called by an object of this class
+ // as it is called by the parent's Disconnect() method
+ virtual void Disconnect () {}
+
+ const AudioFileID& GetFileID() const { return mAudioFileID; }
+
+ const char* GetFileBuffer () { return mFileBuffer; }
+
+ const AudioFilePlayer& GetParent () const { return mParent; }
+
+ virtual void SetPosition (SInt64 pos) = 0; // seek/rewind in the file
+
+ virtual int GetByteCounter () { return mByteCounter; } // return actual bytes streamed to audio hardware
+
+ virtual void SetEndOfFile (SInt64 pos) = 0; // set the "EOF" (will behave just like it reached eof)
+
+protected:
+ AudioFilePlayer& mParent;
+ AudioConverterRef mParentConverter;
+ const AudioFileID mAudioFileID;
+
+ char* mFileBuffer;
+
+ OSStatus Render (AudioBuffer &ioData);
+
+ int mByteCounter;
+
+ virtual OSStatus GetFileData (void** inOutData, UInt32 *inOutDataSize) = 0;
+
+ virtual void DoConnect () = 0;
+
+ virtual void AfterRender () = 0;
+
+public:
+ static OSStatus FileInputProc (void *inRefCon,
+ AudioUnitRenderActionFlags inActionFlags,
+ const AudioTimeStamp *inTimeStamp,
+ UInt32 inBusNumber,
+ AudioBuffer *ioData);
+ static OSStatus ACInputProc (AudioConverterRef inAudioConverter,
+ UInt32* outDataSize,
+ void** outData,
+ void* inUserData);
+};
+
+
+#pragma mark __________ AudioFileReaderThread
+class AudioFileReaderThread
+ : public AudioFileManager
+{
+public:
+ const UInt32 mChunkSize;
+ SInt64 mFileLength;
+ SInt64 mReadFilePosition;
+ bool mWriteToFirstBuffer;
+ bool mFinishedReadingData;
+
+ AudioFileReaderThread (AudioFilePlayer &inParent,
+ AudioFileID &inFile,
+ SInt64 inFileLength,
+ UInt32 inChunkSize);
+
+ virtual void Disconnect ();
+
+ virtual void SetPosition (SInt64 pos); // seek/rewind in the file
+
+ virtual void SetEndOfFile (SInt64 pos); // set the "EOF" (will behave just like it reached eof)
+
+protected:
+ virtual void DoConnect ();
+
+ virtual OSStatus GetFileData (void** inOutData, UInt32 *inOutDataSize);
+
+ virtual void AfterRender ();
+
+private:
+ bool mReadFromFirstBuffer;
+ bool mLockUnsuccessful;
+ bool mIsEngaged;
+
+ int mNumTimesAskedSinceFinished;
+};
+
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cdrom/macosx/AudioFileReaderThread.cpp Tue Apr 15 16:33:56 2003 +0000
@@ -0,0 +1,419 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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
+*/
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// AudioFileReaderThread.cpp
+//
+#include "AudioFilePlayer.h"
+#include <mach/mach.h> //used for setting policy of thread
+#include "CAGuard.h"
+#include <pthread.h>
+
+#include <list>
+
+class FileReaderThread {
+public:
+ FileReaderThread ();
+
+ CAGuard& GetGuard() { return mGuard; }
+
+ void AddReader();
+
+ void RemoveReader (const AudioFileReaderThread* inItem);
+
+ // returns true if succeeded
+ bool TryNextRead (AudioFileReaderThread* inItem)
+ {
+ bool didLock = false;
+ bool succeeded = false;
+ if (mGuard.Try (didLock))
+ {
+ mFileData.push_back (inItem);
+ mGuard.Notify();
+ succeeded = true;
+
+ if (didLock)
+ mGuard.Unlock();
+ }
+
+ return succeeded;
+ }
+
+ int mThreadShouldDie;
+
+private:
+ typedef std::list<AudioFileReaderThread*> FileData;
+
+ CAGuard mGuard;
+ UInt32 mThreadPriority;
+
+ int mNumReaders;
+ FileData mFileData;
+
+
+ void ReadNextChunk ();
+
+ void StartFixedPriorityThread ();
+ static UInt32 GetThreadBasePriority (pthread_t inThread);
+
+ static void* DiskReaderEntry (void *inRefCon);
+};
+
+FileReaderThread::FileReaderThread ()
+ : mThreadPriority (62),
+ mNumReaders (0)
+{
+}
+
+void FileReaderThread::AddReader()
+{
+ if (mNumReaders == 0)
+ {
+ mThreadShouldDie = false;
+
+ StartFixedPriorityThread ();
+ }
+ mNumReaders++;
+}
+
+void FileReaderThread::RemoveReader (const AudioFileReaderThread* inItem)
+{
+ if (mNumReaders > 0)
+ {
+ CAGuard::Locker fileReadLock (mGuard);
+
+ for (FileData::iterator iter = mFileData.begin(); iter != mFileData.end(); ++iter)
+ {
+ if ((*iter) == inItem) {
+ mFileData.erase (iter);
+ }
+ }
+
+ if (--mNumReaders == 0) {
+ mThreadShouldDie = true;
+ mGuard.Notify(); // wake up thread so it will quit
+ mGuard.Wait(); // wait for thread to die
+ }
+ }
+}
+
+void FileReaderThread::StartFixedPriorityThread ()
+{
+ pthread_attr_t theThreadAttrs;
+ pthread_t pThread;
+
+ OSStatus result = pthread_attr_init(&theThreadAttrs);
+ THROW_RESULT("pthread_attr_init - Thread attributes could not be created.")
+
+ result = pthread_attr_setdetachstate(&theThreadAttrs, PTHREAD_CREATE_DETACHED);
+ THROW_RESULT("pthread_attr_setdetachstate - Thread attributes could not be detached.")
+
+ result = pthread_create (&pThread, &theThreadAttrs, DiskReaderEntry, this);
+ 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 = false; // set to true 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);
+ 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 = mThreadPriority - FileReaderThread::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);
+ THROW_RESULT("thread_policy - Couldn't set thread priority.")
+}
+
+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;
+}
+
+void *FileReaderThread::DiskReaderEntry (void *inRefCon)
+{
+ FileReaderThread *This = (FileReaderThread *)inRefCon;
+ This->ReadNextChunk();
+ #if DEBUG
+ printf ("finished with reading file\n");
+ #endif
+
+ return 0;
+}
+
+void FileReaderThread::ReadNextChunk ()
+{
+ OSStatus result;
+ UInt32 dataChunkSize;
+ AudioFileReaderThread* theItem = 0;
+
+ for (;;)
+ {
+ { // this is a scoped based lock
+ CAGuard::Locker fileReadLock (mGuard);
+
+ if (this->mThreadShouldDie) {
+
+ mGuard.Notify();
+ return;
+ }
+
+ if (mFileData.empty())
+ {
+ mGuard.Wait();
+ }
+
+ // kill thread
+ if (this->mThreadShouldDie) {
+
+ mGuard.Notify();
+ return;
+ }
+
+ theItem = mFileData.front();
+ mFileData.pop_front();
+ }
+
+ 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 = true;
+ continue;
+ }
+ // construct pointer
+ char* writePtr = const_cast<char*>(theItem->GetFileBuffer() +
+ (theItem->mWriteToFirstBuffer ? 0 : theItem->mChunkSize));
+
+/*
+ printf ("AudioFileReadBytes: theItem=%.8X fileID=%.8X pos=%.8X sz=%.8X flen=%.8X ptr=%.8X\n",
+ (unsigned int)theItem, (unsigned int)theItem->GetFileID(),
+ (unsigned int)theItem->mReadFilePosition, (unsigned int)dataChunkSize,
+ (unsigned int)theItem->mFileLength, (unsigned int)writePtr);
+*/
+ result = AudioFileReadBytes (theItem->GetFileID(),
+ false,
+ theItem->mReadFilePosition,
+ &dataChunkSize,
+ writePtr);
+ if (result) {
+ theItem->GetParent().DoNotification(result);
+ continue;
+ }
+
+ if (dataChunkSize != theItem->mChunkSize)
+ {
+ writePtr += dataChunkSize;
+
+ // can't exit yet.. we still have to pass the partial buffer back
+ memset (writePtr, 0, (theItem->mChunkSize - dataChunkSize));
+ }
+
+ theItem->mWriteToFirstBuffer = !theItem->mWriteToFirstBuffer; // switch buffers
+
+ theItem->mReadFilePosition += dataChunkSize; // increment count
+ }
+}
+
+
+static FileReaderThread sReaderThread;
+
+AudioFileReaderThread::AudioFileReaderThread (AudioFilePlayer &inParent,
+ AudioFileID &inFile,
+ SInt64 inFileLength,
+ UInt32 inChunkSize)
+ : AudioFileManager (inParent, inFile),
+ mChunkSize (inChunkSize),
+ mFileLength (inFileLength),
+ mReadFilePosition (0),
+ mWriteToFirstBuffer (false),
+ mFinishedReadingData (false),
+
+ mLockUnsuccessful (false),
+ mIsEngaged (false)
+{
+ mFileBuffer = (char*) malloc (mChunkSize * 2);
+ assert (mFileBuffer != NULL);
+}
+
+void AudioFileReaderThread::DoConnect ()
+{
+ if (!mIsEngaged)
+ {
+ //mReadFilePosition = 0;
+ mFinishedReadingData = false;
+
+ mNumTimesAskedSinceFinished = -1;
+ mLockUnsuccessful = false;
+
+ UInt32 dataChunkSize;
+
+ if ((mFileLength - mReadFilePosition) < mChunkSize)
+ dataChunkSize = mFileLength - mReadFilePosition;
+ else
+ dataChunkSize = mChunkSize;
+
+ OSStatus result = AudioFileReadBytes ( mAudioFileID,
+ false,
+ mReadFilePosition,
+ &dataChunkSize,
+ mFileBuffer);
+ THROW_RESULT("AudioFileReadBytes")
+
+ mReadFilePosition += dataChunkSize;
+
+ mWriteToFirstBuffer = false;
+ mReadFromFirstBuffer = true;
+
+ sReaderThread.AddReader();
+
+ mIsEngaged = true;
+ }
+ else
+ throw static_cast<OSStatus>(-1); //thread has already been started
+}
+
+void AudioFileReaderThread::Disconnect ()
+{
+ if (mIsEngaged)
+ {
+ sReaderThread.RemoveReader (this);
+ mIsEngaged = false;
+ }
+}
+
+OSStatus AudioFileReaderThread::GetFileData (void** inOutData, UInt32 *inOutDataSize)
+{
+ if (mFinishedReadingData)
+ {
+ ++mNumTimesAskedSinceFinished;
+ *inOutDataSize = 0;
+ *inOutData = 0;
+ return noErr;
+ }
+
+ if (mReadFromFirstBuffer == mWriteToFirstBuffer) {
+ #if DEBUG
+ printf ("* * * * * * * Can't keep up with reading file:%ld\n", mParent.GetBusNumber());
+ #endif
+
+ mParent.DoNotification (kAudioFilePlayErr_FilePlayUnderrun);
+ *inOutDataSize = 0;
+ *inOutData = 0;
+ } else {
+ *inOutDataSize = mChunkSize;
+ *inOutData = mReadFromFirstBuffer ? mFileBuffer : (mFileBuffer + mChunkSize);
+ }
+
+ mLockUnsuccessful = !sReaderThread.TryNextRead (this);
+
+ mReadFromFirstBuffer = !mReadFromFirstBuffer;
+
+ return noErr;
+}
+
+void AudioFileReaderThread::AfterRender ()
+{
+ if (mNumTimesAskedSinceFinished > 0)
+ {
+ bool didLock = false;
+ if (sReaderThread.GetGuard().Try (didLock)) {
+ mParent.DoNotification (kAudioFilePlay_FileIsFinished);
+ if (didLock)
+ sReaderThread.GetGuard().Unlock();
+ }
+ }
+
+ if (mLockUnsuccessful)
+ mLockUnsuccessful = !sReaderThread.TryNextRead (this);
+}
+
+void AudioFileReaderThread::SetPosition (SInt64 pos)
+{
+ if (pos < 0 || pos >= mFileLength) {
+ SDL_SetError ("AudioFileReaderThread::SetPosition - position invalid: %d filelen=%d\n",
+ (unsigned int)pos, (unsigned int)mFileLength);
+ pos = 0;
+ }
+
+ mReadFilePosition = pos;
+}
+
+void AudioFileReaderThread::SetEndOfFile (SInt64 pos)
+{
+ if (pos <= 0 || pos > mFileLength) {
+ SDL_SetError ("AudioFileReaderThread::SetEndOfFile - position beyond actual eof\n");
+ pos = mFileLength;
+ }
+
+ mFileLength = pos;
+}
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cdrom/macosx/CAGuard.cpp Tue Apr 15 16:33:56 2003 +0000
@@ -0,0 +1,160 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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
+*/
+/*
+ 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 <stdio.h>
+
+#define NDEBUG 1
+#include <assert.h>
+
+
+#include "CAGuard.h"
+
+//#warning Need a try-based Locker too
+//=============================================================================
+// CAGuard
+//=============================================================================
+
+CAGuard::CAGuard()
+{
+ OSStatus theError = pthread_mutex_init(&mMutex, NULL);
+ 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)
+ {
+ OSStatus theError = pthread_mutex_lock(&mMutex);
+ assert(theError == 0);
+ mOwner = pthread_self();
+ theAnswer = true;
+ }
+
+ return theAnswer;
+}
+
+void CAGuard::Unlock()
+{
+ assert(pthread_self() == mOwner);
+
+ mOwner = 0;
+ OSStatus theError = pthread_mutex_unlock(&mMutex);
+ assert(theError == 0);
+}
+
+bool CAGuard::Try (bool& outWasLocked)
+{
+ bool theAnswer = false;
+ outWasLocked = false;
+
+ if (pthread_self() == mOwner) {
+ theAnswer = true;
+ outWasLocked = false;
+ } else {
+ OSStatus theError = pthread_mutex_trylock(&mMutex);
+ if (theError == 0) {
+ mOwner = pthread_self();
+ theAnswer = true;
+ outWasLocked = true;
+ }
+ }
+
+ return theAnswer;
+}
+
+void CAGuard::Wait()
+{
+ assert(pthread_self() == mOwner);
+
+ mOwner = 0;
+
+ OSStatus theError = pthread_cond_wait(&mCondVar, &mMutex);
+ assert(theError == 0);
+ mOwner = pthread_self();
+}
+
+void CAGuard::Notify()
+{
+ OSStatus theError = pthread_cond_signal(&mCondVar);
+ assert(theError == 0);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cdrom/macosx/CAGuard.h Tue Apr 15 16:33:56 2003 +0000
@@ -0,0 +1,143 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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
+*/
+/*
+ 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 <CoreAudio/CoreAudioTypes.h>
+#include <pthread.h>
+
+
+//=============================================================================
+// 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.
+//=============================================================================
+
+class CAGuard
+{
+
+// Construction/Destruction
+public:
+ CAGuard();
+ virtual ~CAGuard();
+
+// Actions
+public:
+ virtual bool Lock();
+ virtual void Unlock();
+ virtual bool Try(bool& outWasLocked); // returns true if lock is free, false if not
+
+ virtual void Wait();
+
+ virtual void Notify();
+
+// Implementation
+protected:
+ pthread_mutex_t mMutex;
+ pthread_cond_t mCondVar;
+ pthread_t mOwner;
+
+// Helper class to manage taking and releasing recursively
+public:
+ class Locker
+ {
+
+ // 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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cdrom/macosx/CDPlayer.cpp Tue Apr 15 16:33:56 2003 +0000
@@ -0,0 +1,715 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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 "CDPlayer.h"
+#include "AudioFilePlayer.h"
+#include "CAGuard.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 bool playBackWasInit = false;
+static AudioUnit theUnit;
+static AudioFilePlayer* thePlayer = NULL;
+static CDPlayerCompletionProc completionProc = NULL;
+static pthread_mutex_t apiMutex;
+static pthread_t callbackThread;
+static pthread_mutex_t callbackMutex;
+static volatile int runCallBackThread;
+static int initMutex = SDL_TRUE;
+static SDL_CD* theCDROM;
+
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+// Prototypes
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+
+#pragma mark -- Prototypes --
+
+OSStatus CheckInit ();
+
+OSStatus MatchAUFormats (AudioUnit theUnit, UInt32 theInputBus);
+
+void FilePlayNotificationHandler (void* inRefCon, OSStatus inStatus);
+
+void* RunCallBackThread (void* inRefCon);
+
+
+#pragma mark -- Public Functions --
+
+void Lock ()
+{
+ if (initMutex) {
+
+ pthread_mutexattr_t attr;
+
+ pthread_mutexattr_init (&attr);
+ pthread_mutex_init (&apiMutex, &attr);
+ pthread_mutexattr_destroy (&attr);
+
+ initMutex = SDL_FALSE;
+ }
+
+ pthread_mutex_lock (&apiMutex);
+}
+
+void Unlock ()
+{
+ pthread_mutex_unlock (&apiMutex);
+}
+
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+// DetectAudioCDVolumes
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+
+int DetectAudioCDVolumes(FSVolumeRefNum *volumes, int numVolumes)
+{
+ int volumeIndex;
+ int cdVolumeCount = 0;
+ OSStatus result = noErr;
+
+ for (volumeIndex = 1; result == noErr || result != nsvErr; volumeIndex++)
+ {
+ FSVolumeRefNum actualVolume;
+ HFSUniStr255 volumeName;
+ FSVolumeInfo volumeInfo;
+ FSRef rootDirectory;
+
+ memset (&volumeInfo, 0, sizeof(volumeInfo));
+
+ result = FSGetVolumeInfo (kFSInvalidVolumeRefNum,
+ volumeIndex,
+ &actualVolume,
+ kFSVolInfoFSInfo,
+ &volumeInfo,
+ &volumeName,
+ &rootDirectory);
+
+ 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;
+}
+
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+// ReadTOCData
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+
+int ReadTOCData (FSVolumeRefNum theVolume, SDL_CD *theCD)
+{
+ HFSUniStr255 dataForkName;
+ OSStatus theErr;
+ SInt16 forkRefNum;
+ SInt64 forkSize;
+ Ptr forkData = 0;
+ ByteCount actualRead;
+ CFDataRef dataRef = 0;
+ CFPropertyListRef propertyListRef = 0;
+
+ FSRefParam fsRefPB;
+ FSRef tocPlistFSRef;
+
+ const char* error = "Unspecified Error";
+
+ // get stuff from .TOC.plist
+ fsRefPB.ioCompletion = NULL;
+ fsRefPB.ioNamePtr = "\p.TOC.plist";
+ fsRefPB.ioVRefNum = theVolume;
+ fsRefPB.ioDirID = 0;
+ fsRefPB.newRef = &tocPlistFSRef;
+
+ theErr = PBMakeFSRefSync (&fsRefPB);
+ if(theErr != noErr) {
+ error = "PBMakeFSRefSync";
+ 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;
+}
+
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+// ListTrackFiles
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+
+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);
+ goto bail;
+ }
+
+ 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"))) {
+
+ // Extract the track id from the filename
+ int trackID = 0, i = 0;
+ while (nameStr.unicode[i] >= '0' && nameStr.unicode[i] <= '9') {
+ 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);
+ }
+
+ result = 0;
+ bail:
+ return result;
+}
+
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+// LoadFile
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+
+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");
+ throw (-3);
+ }
+
+ thePlayer->SetDestination(theUnit, 0);
+
+ if (startFrame >= 0)
+ thePlayer->SetStartFrame (startFrame);
+
+ if (stopFrame >= 0 && stopFrame > startFrame)
+ thePlayer->SetStopFrame (stopFrame);
+
+ // we set the notifier later
+ //thePlayer->SetNotifier(FilePlayNotificationHandler, NULL);
+
+ thePlayer->Connect();
+
+ #if DEBUG_CDROM
+ thePlayer->Print();
+ fflush (stdout);
+ #endif
+ }
+ catch (...)
+ {
+ goto bail;
+ }
+
+ error = 0;
+
+ bail:
+ return error;
+}
+
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+// ReleaseFile
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+
+int ReleaseFile ()
+{
+ int error = -1;
+
+ try {
+ if (thePlayer != NULL) {
+
+ thePlayer->Disconnect();
+
+ delete thePlayer;
+
+ thePlayer = NULL;
+ }
+ }
+ catch (...)
+ {
+ goto bail;
+ }
+
+ error = 0;
+
+ bail:
+ return error;
+}
+
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+// PlayFile
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+
+int PlayFile ()
+{
+ OSStatus result = -1;
+
+ if (CheckInit () < 0)
+ goto bail;
+
+ try {
+
+ // start processing of the audio unit
+ result = AudioOutputUnitStart (theUnit);
+ THROW_RESULT("PlayFile: AudioOutputUnitStart")
+
+ }
+ catch (...)
+ {
+ goto bail;
+ }
+
+ result = 0;
+
+bail:
+ return result;
+}
+
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+// PauseFile
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+
+int PauseFile ()
+{
+ OSStatus result = -1;
+
+ if (CheckInit () < 0)
+ goto bail;
+
+ try {
+
+ // stop processing the audio unit
+ result = AudioOutputUnitStop (theUnit);
+ THROW_RESULT("PauseFile: AudioOutputUnitStop")
+ }
+ catch (...)
+ {
+ goto bail;
+ }
+
+ result = 0;
+bail:
+ return result;
+}
+
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+// SetCompletionProc
+//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
+
+void SetCompletionProc (CDPlayerCompletionProc proc, SDL_CD *cdrom)
+{
+ assert(thePlayer != NULL);
+
+ theCDROM = cdrom;
+ completionProc = proc;
+ thePlayer->SetNotifier (FilePlayNotificationHandler, cdrom);
+}
+
+
+int GetCurrentFrame ()
+{
+ int frame;
+
+ if (thePlayer == NULL)
+ frame = 0;
+ else
+ frame = thePlayer->GetCurrentFrame ();
+
+ return frame;
+}
+
+
+#pragma mark -- Private Functions --
+
+OSStatus CheckInit ()
+{
+ if (playBackWasInit)
+ return 0;
+
+ OSStatus result = noErr;
+
+
+ // Create the callback mutex
+ pthread_mutexattr_t attr;
+ pthread_mutexattr_init (&attr);
+ pthread_mutex_init (&callbackMutex, &attr);
+ pthread_mutexattr_destroy (&attr);
+ pthread_mutex_lock (&callbackMutex);
+
+ // Start callback thread
+ pthread_attr_t attr1;
+ pthread_attr_init (&attr1);
+ pthread_create (&callbackThread, &attr1, RunCallBackThread, NULL);
+ pthread_attr_destroy (&attr1);
+
+ try {
+ ComponentDescription desc;
+
+ desc.componentType = kAudioUnitComponentType;
+ desc.componentSubType = kAudioUnitSubType_Output;
+ desc.componentManufacturer = kAudioUnitID_DefaultOutput;
+ desc.componentFlags = 0;
+ desc.componentFlagsMask = 0;
+
+ Component comp = FindNextComponent (NULL, &desc);
+ if (comp == NULL) {
+ SDL_SetError ("CheckInit: FindNextComponent returned NULL");
+ throw(internalComponentErr);
+ }
+
+ result = OpenAComponent (comp, &theUnit);
+ THROW_RESULT("CheckInit: OpenAComponent")
+
+ // you need to initialize the output unit before you set it as a destination
+ result = AudioUnitInitialize (theUnit);
+ THROW_RESULT("CheckInit: AudioUnitInitialize")
+
+
+ // In this case we first want to get the output format of the OutputUnit
+ // Then we set that as the input format. Why?
+ // So that only a single conversion process is done
+ // when SetDestination is called it will get the input format of the
+ // unit its supplying data to. This defaults to 44.1K, stereo, so if
+ // the device is not that, then we lose a possibly rendering of data
+
+ result = MatchAUFormats (theUnit, 0);
+ THROW_RESULT("CheckInit: MatchAUFormats")
+
+ playBackWasInit = true;
+ }
+ catch (...)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+
+OSStatus MatchAUFormats (AudioUnit theUnit, UInt32 theInputBus)
+{
+ AudioStreamBasicDescription theDesc;
+ UInt32 size = sizeof (theDesc);
+ OSStatus result = AudioUnitGetProperty (theUnit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Output,
+ 0,
+ &theDesc,
+ &size);
+ THROW_RESULT("MatchAUFormats: AudioUnitGetProperty")
+
+ result = AudioUnitSetProperty (theUnit,
+ kAudioUnitProperty_StreamFormat,
+ kAudioUnitScope_Input,
+ theInputBus,
+ &theDesc,
+ size);
+
+ return result;
+}
+
+void FilePlayNotificationHandler(void * inRefCon, OSStatus inStatus)
+{
+ if (inStatus == kAudioFilePlay_FileIsFinished) {
+
+ // notify non-CA thread to perform the callback
+ pthread_mutex_unlock (&callbackMutex);
+
+ } 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);
+ }
+}
+
+void* RunCallBackThread (void *param)
+{
+ runCallBackThread = 1;
+
+ while (runCallBackThread) {
+
+ pthread_mutex_lock (&callbackMutex);
+
+ if (completionProc && theCDROM) {
+ #if DEBUG_CDROM
+ printf ("callback!\n");
+ #endif
+ (*completionProc)(theCDROM);
+ } else {
+ #if DEBUG_CDROM
+ printf ("callback?\n");
+ #endif
+ }
+ }
+
+ runCallBackThread = -1;
+
+ #if DEBUG_CDROM
+ printf ("thread dying now...\n");
+ #endif
+
+ return NULL;
+}
+
+}; // extern "C"
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cdrom/macosx/CDPlayer.h Tue Apr 15 16:33:56 2003 +0000
@@ -0,0 +1,69 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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
+*/
+
+#ifndef __CDPlayer__H__
+#define __CDPlayer__H__ 1
+
+#include <CoreFoundation/CoreFoundation.h>
+#include <Carbon/Carbon.h>
+
+#include <AudioUnit/AudioUnit.h>
+#include <AudioToolbox/AudioToolbox.h>
+
+#include <SDL.h>
+
+#include <string.h>
+#include <unistd.h> // for usleep
+
+#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__ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cdrom/macosx/Makefile.am Tue Apr 15 16:33:56 2003 +0000
@@ -0,0 +1,13 @@
+
+## Makefile.am for the Mac OS X cdrom driver for SDL
+
+noinst_LTLIBRARIES = libcdrom_macosx.la
+libcdrom_macosx_la_SOURCES = $(SRCS)
+
+# The SDL cdrom driver sources
+SRCS = \
+ SDL_syscdrom.c \
+ AudioFilePlayer.cpp \
+ AudioFileReaderThread.cpp \
+ CAGuard.cpp \
+ CDPlayer.cpp
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cdrom/macosx/SDL_syscdrom_c.h Tue Apr 15 16:33:56 2003 +0000
@@ -0,0 +1,132 @@
+/*
+ SDL - Simple DirectMedia Layer
+ Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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
+*/
+
+
+/***********************************************************************************
+ 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."
+
--- a/test/testcdrom.c Tue Apr 15 16:04:31 2003 +0000
+++ b/test/testcdrom.c Tue Apr 15 16:33:56 2003 +0000
@@ -64,8 +64,8 @@
trtype="unknown";
break;
}
- printf("\tTrack (index %d) %d: %d:%2.2d [%s track]\n", i,
- cdrom->track[i].id, m, s, trtype);
+ printf("\tTrack (index %d) %d: %d:%2.2d / %d [%s track]\n", i,
+ cdrom->track[i].id, m, s, cdrom->track[i].length, trtype);
}
}