Added MacOS X CD-ROM audio support (thanks Max and Darrell)
authorSam Lantinga <slouken@libsdl.org>
Tue, 15 Apr 2003 16:33:56 +0000
changeset 613 9c6717a1c66f
parent 612 0648505b1f8b
child 614 0b4c3f5ff63d
Added MacOS X CD-ROM audio support (thanks Max and Darrell)
configure.in
src/cdrom/Makefile.am
src/cdrom/macosx/AudioFilePlayer.cpp
src/cdrom/macosx/AudioFilePlayer.h
src/cdrom/macosx/AudioFileReaderThread.cpp
src/cdrom/macosx/CAGuard.cpp
src/cdrom/macosx/CAGuard.h
src/cdrom/macosx/CDPlayer.cpp
src/cdrom/macosx/CDPlayer.h
src/cdrom/macosx/Makefile.am
src/cdrom/macosx/SDL_syscdrom_c.h
test/testcdrom.c
--- 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);
 	}
 }