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 6139c6717a1c66f
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
     1.1 --- a/configure.in	Tue Apr 15 16:04:31 2003 +0000
     1.2 +++ b/configure.in	Tue Apr 15 16:33:56 2003 +0000
     1.3 @@ -2404,8 +2404,9 @@
     1.4          fi
     1.5          # Set up files for the cdrom library
     1.6          if test x$enable_cdrom = xyes; then
     1.7 -            CDROM_SUBDIRS="$CDROM_SUBDIRS dummy"
     1.8 -            CDROM_DRIVERS="$CDROM_DRIVERS dummy/libcdrom_dummy.la"
     1.9 +            CDROM_SUBDIRS="$CDROM_SUBDIRS macosx"
    1.10 +            CDROM_DRIVERS="$CDROM_DRIVERS macosx/libcdrom_macosx.la"
    1.11 +            SYSTEM_LIBS="$SYSTEM_LIBS -framework AudioToolbox -framework AudioUnit -lstdc++"
    1.12          fi
    1.13          # Set up files for the thread library
    1.14          if test x$enable_threads = xyes; then
    1.15 @@ -2672,6 +2673,7 @@
    1.16  src/cdrom/freebsd/Makefile
    1.17  src/cdrom/linux/Makefile
    1.18  src/cdrom/macos/Makefile
    1.19 +src/cdrom/macosx/Makefile
    1.20  src/cdrom/openbsd/Makefile
    1.21  src/cdrom/qnx/Makefile
    1.22  src/cdrom/win32/Makefile
     2.1 --- a/src/cdrom/Makefile.am	Tue Apr 15 16:04:31 2003 +0000
     2.2 +++ b/src/cdrom/Makefile.am	Tue Apr 15 16:33:56 2003 +0000
     2.3 @@ -5,7 +5,19 @@
     2.4  
     2.5  # Define which subdirectories need to be built
     2.6  SUBDIRS = @CDROM_SUBDIRS@
     2.7 -DIST_SUBDIRS = aix beos bsdi dc dummy freebsd linux macos openbsd qnx win32
     2.8 +DIST_SUBDIRS = \
     2.9 +	aix \
    2.10 +	beos \
    2.11 +	bsdi \
    2.12 +	dc \
    2.13 +	dummy \
    2.14 +	freebsd \
    2.15 +	linux \
    2.16 +	macos \
    2.17 +	macosx \
    2.18 +	openbsd \
    2.19 +	qnx \
    2.20 +	win32
    2.21  
    2.22  DRIVERS = @CDROM_DRIVERS@
    2.23  
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/src/cdrom/macosx/AudioFilePlayer.cpp	Tue Apr 15 16:33:56 2003 +0000
     3.3 @@ -0,0 +1,377 @@
     3.4 +/*
     3.5 +    SDL - Simple DirectMedia Layer
     3.6 +    Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  Sam Lantinga
     3.7 +
     3.8 +    This library is free software; you can redistribute it and/or
     3.9 +    modify it under the terms of the GNU Library General Public
    3.10 +    License as published by the Free Software Foundation; either
    3.11 +    version 2 of the License, or (at your option) any later version.
    3.12 +
    3.13 +    This library is distributed in the hope that it will be useful,
    3.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    3.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    3.16 +    Library General Public License for more details.
    3.17 +
    3.18 +    You should have received a copy of the GNU Library General Public
    3.19 +    License along with this library; if not, write to the Free
    3.20 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    3.21 +
    3.22 +    Sam Lantinga
    3.23 +    slouken@libsdl.org
    3.24 +
    3.25 +    This file based on Apple sample code. We haven't changed the file name, 
    3.26 +    so if you want to see the original search for it on apple.com/developer
    3.27 +*/
    3.28 +
    3.29 +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    3.30 +//  AudioFilePlayer.cpp
    3.31 +//
    3.32 +#include "AudioFilePlayer.h"
    3.33 +
    3.34 +extern const char* AudioFilePlayerErrorStr (OSStatus error)
    3.35 +{
    3.36 +    const char *str;
    3.37 +    
    3.38 +    switch (error) {
    3.39 +    case kAudioFileUnspecifiedError:               str = "wht?"; break;
    3.40 +    case kAudioFileUnsupportedFileTypeError:       str = "typ?"; break;
    3.41 +    case kAudioFileUnsupportedDataFormatError:     str = "fmt?"; break;
    3.42 +    case kAudioFileUnsupportedPropertyError:       str = "pty?"; break;
    3.43 +    case kAudioFileBadPropertySizeError:           str = "!siz"; break;
    3.44 +    case kAudioFileNotOptimizedError:              str = "optm"; break;
    3.45 +    case kAudioFilePermissionsError:               str = "prm?"; break;
    3.46 +    case kAudioFileFormatNameUnavailableError:     str = "nme?"; break;
    3.47 +    case kAudioFileInvalidChunkError:              str = "chk?"; break;
    3.48 +    case kAudioFileDoesNotAllow64BitDataSizeError: str = "off?"; break;
    3.49 +    default: str = "error unspecified";
    3.50 +    }
    3.51 +    
    3.52 +    return str;
    3.53 +}
    3.54 +
    3.55 +void ThrowResult (OSStatus result, const char* str)
    3.56 +{
    3.57 +    SDL_SetError ("Error: %s %d (%s)",
    3.58 +                   str, result, AudioFilePlayerErrorStr(result));
    3.59 +    throw result;
    3.60 +}
    3.61 +
    3.62 +#if DEBUG
    3.63 +void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
    3.64 +{
    3.65 +    if (!inDesc) {
    3.66 +        printf ("Can't print a NULL desc!\n");
    3.67 +        return;
    3.68 +    }
    3.69 +    
    3.70 +    printf ("- - - - - - - - - - - - - - - - - - - -\n");
    3.71 +    printf ("  Sample Rate:%f\n", inDesc->mSampleRate);
    3.72 +    printf ("  Format ID:%s\n", (char*)&inDesc->mFormatID);
    3.73 +    printf ("  Format Flags:%lX\n", inDesc->mFormatFlags);
    3.74 +    printf ("  Bytes per Packet:%ld\n", inDesc->mBytesPerPacket);
    3.75 +    printf ("  Frames per Packet:%ld\n", inDesc->mFramesPerPacket);
    3.76 +    printf ("  Bytes per Frame:%ld\n", inDesc->mBytesPerFrame);
    3.77 +    printf ("  Channels per Frame:%ld\n", inDesc->mChannelsPerFrame);
    3.78 +    printf ("  Bits per Channel:%ld\n", inDesc->mBitsPerChannel);
    3.79 +    printf ("- - - - - - - - - - - - - - - - - - - -\n");
    3.80 +}
    3.81 +#endif
    3.82 +
    3.83 +OSStatus    AudioFileManager::FileInputProc (void                       *inRefCon, 
    3.84 +                                             AudioUnitRenderActionFlags inActionFlags,
    3.85 +                                             const AudioTimeStamp       *inTimeStamp, 
    3.86 +                                             UInt32                     inBusNumber, 
    3.87 +                                             AudioBuffer                *ioData)
    3.88 +{
    3.89 +    AudioFileManager* THIS = (AudioFileManager*)inRefCon;
    3.90 +    return THIS->Render(*ioData);
    3.91 +}
    3.92 +
    3.93 +OSStatus    AudioFileManager::Render (AudioBuffer &ioData)
    3.94 +{
    3.95 +    OSStatus result = AudioConverterFillBuffer(mParentConverter, 
    3.96 +                                    AudioFileManager::ACInputProc, 
    3.97 +                                    this, 
    3.98 +                                    &ioData.mDataByteSize, 
    3.99 +                                    ioData.mData);
   3.100 +    if (result) {
   3.101 +        SDL_SetError ("AudioConverterFillBuffer:%ld\n", result);
   3.102 +        mParent.DoNotification (result);
   3.103 +    } else {
   3.104 +        mByteCounter += ioData.mDataByteSize / 2;
   3.105 +        AfterRender();
   3.106 +    }
   3.107 +    return result;
   3.108 +}
   3.109 +
   3.110 +OSStatus    AudioFileManager::ACInputProc (AudioConverterRef            inAudioConverter,
   3.111 +                                            UInt32*                     outDataSize,
   3.112 +                                            void**                      outData,
   3.113 +                                            void*                       inUserData)
   3.114 +{
   3.115 +    AudioFileManager* THIS = (AudioFileManager*)inUserData;
   3.116 +    return THIS->GetFileData(outData, outDataSize);
   3.117 +}
   3.118 +
   3.119 +AudioFileManager::~AudioFileManager ()
   3.120 +{
   3.121 +    if (mFileBuffer) {
   3.122 +        free (mFileBuffer);
   3.123 +        mFileBuffer = 0;
   3.124 +    }
   3.125 +}
   3.126 +
   3.127 +AudioFilePlayer::AudioFilePlayer (const FSRef           *inFileRef)
   3.128 +    : mConnected (false),
   3.129 +      mAudioFileManager (0),
   3.130 +      mConverter (0),
   3.131 +      mNotifier (0),
   3.132 +      mStartFrame (0)
   3.133 +{
   3.134 +    SInt64 fileDataSize  = 0;
   3.135 +
   3.136 +    OpenFile (inFileRef, fileDataSize);
   3.137 +        
   3.138 +    // we want about a seconds worth of data for the buffer
   3.139 +    int secsBytes = UInt32 (mFileDescription.mSampleRate * mFileDescription.mBytesPerFrame);
   3.140 +    
   3.141 +#if DEBUG
   3.142 +    printf("File format:\n");
   3.143 +    PrintStreamDesc (&mFileDescription);
   3.144 +#endif
   3.145 +    
   3.146 +        //round to a 32K boundary
   3.147 +    //if ((secsBytes & 0xFFFF8000) > (128 * 1024))
   3.148 +        //secsBytes &= 0xFFFF8000;
   3.149 +    //else
   3.150 +        //secsBytes = (secsBytes + 0x7FFF) & 0xFFFF8000;
   3.151 +                    
   3.152 +    mAudioFileManager = new AudioFileReaderThread (*this, 
   3.153 +                                                mAudioFileID, 
   3.154 +                                                fileDataSize,
   3.155 +                                                secsBytes);
   3.156 +}
   3.157 +
   3.158 +// you can put a rate scalar here to play the file faster or slower
   3.159 +// by multiplying the same rate by the desired factor 
   3.160 +// eg fileSampleRate * 2 -> twice as fast
   3.161 +// before you create the AudioConverter
   3.162 +void    AudioFilePlayer::SetDestination (AudioUnit              &inDestUnit, 
   3.163 +                                int                             inBusNumber)
   3.164 +{
   3.165 +    if (mConnected) throw static_cast<OSStatus>(-1); //can't set dest if already engaged
   3.166 + 
   3.167 +    mPlayUnit = inDestUnit;
   3.168 +    mBusNumber = inBusNumber;
   3.169 +
   3.170 +    OSStatus result = noErr;
   3.171 +    
   3.172 +    if (mConverter) {
   3.173 +        result = AudioConverterDispose (mConverter);
   3.174 +            THROW_RESULT("AudioConverterDispose")
   3.175 +    }
   3.176 +    
   3.177 +    AudioStreamBasicDescription     destDesc;
   3.178 +    UInt32  size = sizeof (destDesc);
   3.179 +    result = AudioUnitGetProperty (inDestUnit,
   3.180 +                                   kAudioUnitProperty_StreamFormat,
   3.181 +                                   kAudioUnitScope_Input,
   3.182 +                                   inBusNumber,
   3.183 +                                   &destDesc,
   3.184 +                                   &size);
   3.185 +        THROW_RESULT("AudioUnitGetProperty")
   3.186 +
   3.187 +#if DEBUG
   3.188 +    printf("Destination format:\n");
   3.189 +    PrintStreamDesc (&destDesc);
   3.190 +#endif
   3.191 +
   3.192 +        //we can "down" cast a component instance to a component
   3.193 +    ComponentDescription desc;
   3.194 +    result = GetComponentInfo ((Component)inDestUnit, &desc, 0, 0, 0);
   3.195 +        THROW_RESULT("GetComponentInfo")
   3.196 +        
   3.197 +        // we're going to use this to know which convert routine to call
   3.198 +        // a v1 audio unit will have a type of 'aunt'
   3.199 +        // a v2 audio unit will have one of several different types.
   3.200 +    mIsAUNTUnit = (desc.componentType == kAudioUnitComponentType);
   3.201 +    
   3.202 +    if (!mIsAUNTUnit) {
   3.203 +        result = badComponentInstance;
   3.204 +        THROW_RESULT("BAD COMPONENT")
   3.205 +    }
   3.206 +
   3.207 +    
   3.208 +    // HACK - the AIFF files on CDs are in little endian order!
   3.209 +    if (mFileDescription.mFormatFlags == 0xE)
   3.210 +        mFileDescription.mFormatFlags &= ~kAudioFormatFlagIsBigEndian;
   3.211 +    
   3.212 +
   3.213 +    result = AudioConverterNew (&mFileDescription, &destDesc, &mConverter);
   3.214 +        THROW_RESULT("AudioConverterNew")
   3.215 +
   3.216 +
   3.217 +/*
   3.218 +    // if we have a mono source, we're going to copy each channel into
   3.219 +    // the destination's channel source...
   3.220 +    if (mFileDescription.mChannelsPerFrame == 1) {
   3.221 +    
   3.222 +        SInt32* channelMap = new SInt32 [destDesc.mChannelsPerFrame];
   3.223 +        for (unsigned int i = 0; i < destDesc.mChannelsPerFrame; ++i)
   3.224 +            channelMap[i] = 0; //set first channel to all output channels
   3.225 +            
   3.226 +        result = AudioConverterSetProperty(mConverter,
   3.227 +                            kAudioConverterChannelMap,
   3.228 +                            (sizeof(SInt32) * destDesc.mChannelsPerFrame),
   3.229 +                            channelMap);
   3.230 +            THROW_RESULT("AudioConverterSetProperty")
   3.231 +        
   3.232 +        delete [] channelMap;
   3.233 +    }
   3.234 +*/
   3.235 +    assert (mFileDescription.mChannelsPerFrame == 2);
   3.236 +    
   3.237 +#if 0
   3.238 +    // this uses the better quality SRC
   3.239 +    UInt32 srcID = kAudioUnitSRCAlgorithm_Polyphase;
   3.240 +    result = AudioConverterSetProperty(mConverter,
   3.241 +                    kAudioConverterSampleRateConverterAlgorithm, 
   3.242 +                    sizeof(srcID), 
   3.243 +                    &srcID);
   3.244 +        THROW_RESULT("AudioConverterSetProperty")
   3.245 +#endif
   3.246 +}
   3.247 +
   3.248 +void    AudioFilePlayer::SetStartFrame (int frame)
   3.249 +{
   3.250 +    SInt64 position = frame * 2352;
   3.251 +
   3.252 +    mStartFrame = frame;
   3.253 +    mAudioFileManager->SetPosition (position);
   3.254 +}
   3.255 +
   3.256 +    
   3.257 +int    AudioFilePlayer::GetCurrentFrame ()
   3.258 +{
   3.259 +    return mStartFrame + (mAudioFileManager->GetByteCounter() / 2352);
   3.260 +}
   3.261 +    
   3.262 +void    AudioFilePlayer::SetStopFrame (int frame)
   3.263 +{
   3.264 +    SInt64 position  = frame * 2352;
   3.265 +    
   3.266 +    mAudioFileManager->SetEndOfFile (position);
   3.267 +}
   3.268 +    
   3.269 +AudioFilePlayer::~AudioFilePlayer()
   3.270 +{
   3.271 +    Disconnect();
   3.272 +        
   3.273 +    if (mAudioFileManager) {
   3.274 +        delete mAudioFileManager;
   3.275 +        mAudioFileManager = 0;
   3.276 +    }
   3.277 +    
   3.278 +    if (mAudioFileID) {
   3.279 +        ::AudioFileClose (mAudioFileID);
   3.280 +        mAudioFileID = 0;
   3.281 +    }
   3.282 +
   3.283 +    if (mConverter) {
   3.284 +        AudioConverterDispose (mConverter);
   3.285 +        mConverter = 0;
   3.286 +    }
   3.287 +}
   3.288 +
   3.289 +void    AudioFilePlayer::Connect()
   3.290 +{
   3.291 +#if DEBUG
   3.292 +    printf ("Connect:%x,%ld, engaged=%d\n", (int)mPlayUnit, mBusNumber, (mConnected ? 1 : 0));
   3.293 +#endif
   3.294 +    if (!mConnected)
   3.295 +    {           
   3.296 +        mAudioFileManager->Connect(mConverter);
   3.297 +                
   3.298 +        // set the render callback for the file data to be supplied to the sound converter AU
   3.299 +        if (mIsAUNTUnit) {
   3.300 +            mInputCallback.inputProc = AudioFileManager::FileInputProc;
   3.301 +            mInputCallback.inputProcRefCon = mAudioFileManager;
   3.302 +
   3.303 +            OSStatus result = AudioUnitSetProperty (mPlayUnit, 
   3.304 +                                kAudioUnitProperty_SetInputCallback, 
   3.305 +                                kAudioUnitScope_Input, 
   3.306 +                                mBusNumber,
   3.307 +                                &mInputCallback, 
   3.308 +                                sizeof(mInputCallback));
   3.309 +            THROW_RESULT("AudioUnitSetProperty")
   3.310 +        }
   3.311 +        mConnected = true;
   3.312 +    }
   3.313 +}
   3.314 +
   3.315 +// warning noted, now please go away ;-)
   3.316 +// #warning This should redirect the calling of notification code to some other thread
   3.317 +void    AudioFilePlayer::DoNotification (OSStatus inStatus) const
   3.318 +{
   3.319 +    AudioFilePlayer* THIS = const_cast<AudioFilePlayer*>(this);
   3.320 +        
   3.321 +    if (mNotifier) {
   3.322 +        (*mNotifier) (mRefCon, inStatus);
   3.323 +    } 
   3.324 +    
   3.325 +    else {
   3.326 +        SDL_SetError ("Notification posted with no notifier in place");
   3.327 +        
   3.328 +        if (inStatus == kAudioFilePlay_FileIsFinished)
   3.329 +            THIS->Disconnect();
   3.330 +        else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun)
   3.331 +            THIS->Disconnect();
   3.332 +    }
   3.333 +}
   3.334 +
   3.335 +void    AudioFilePlayer::Disconnect ()
   3.336 +{
   3.337 +#if DEBUG
   3.338 +    printf ("Disconnect:%x,%ld, engaged=%d\n", (int)mPlayUnit, mBusNumber, (mConnected ? 1 : 0));
   3.339 +#endif
   3.340 +    if (mConnected)
   3.341 +    {
   3.342 +        mConnected = false;
   3.343 +            
   3.344 +        if (mIsAUNTUnit) {
   3.345 +            mInputCallback.inputProc = 0;
   3.346 +            mInputCallback.inputProcRefCon = 0;
   3.347 +            OSStatus result = AudioUnitSetProperty (mPlayUnit, 
   3.348 +                                            kAudioUnitProperty_SetInputCallback, 
   3.349 +                                            kAudioUnitScope_Input, 
   3.350 +                                            mBusNumber,
   3.351 +                                            &mInputCallback, 
   3.352 +                                            sizeof(mInputCallback));
   3.353 +            if (result) 
   3.354 +                SDL_SetError ("AudioUnitSetProperty:RemoveInputCallback:%ld", result);
   3.355 +
   3.356 +        }
   3.357 +        
   3.358 +        mAudioFileManager->Disconnect();
   3.359 +    }
   3.360 +}
   3.361 +
   3.362 +void    AudioFilePlayer::OpenFile (const FSRef *inRef, SInt64& outFileDataSize)
   3.363 +{       
   3.364 +    OSStatus result = AudioFileOpen (inRef, fsRdPerm, 0, &mAudioFileID);
   3.365 +        THROW_RESULT("AudioFileOpen")
   3.366 +        
   3.367 +    UInt32 dataSize = sizeof(AudioStreamBasicDescription);
   3.368 +    result = AudioFileGetProperty (mAudioFileID, 
   3.369 +                            kAudioFilePropertyDataFormat, 
   3.370 +                            &dataSize, 
   3.371 +                            &mFileDescription);
   3.372 +        THROW_RESULT("AudioFileGetProperty")
   3.373 +    
   3.374 +    dataSize = sizeof (SInt64);
   3.375 +    result = AudioFileGetProperty (mAudioFileID, 
   3.376 +                            kAudioFilePropertyAudioDataByteCount, 
   3.377 +                            &dataSize, 
   3.378 +                            &outFileDataSize);
   3.379 +        THROW_RESULT("AudioFileGetProperty")
   3.380 +}
   3.381 \ No newline at end of file
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/src/cdrom/macosx/AudioFilePlayer.h	Tue Apr 15 16:33:56 2003 +0000
     4.3 @@ -0,0 +1,240 @@
     4.4 +/*
     4.5 +    SDL - Simple DirectMedia Layer
     4.6 +    Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  Sam Lantinga
     4.7 +
     4.8 +    This library is free software; you can redistribute it and/or
     4.9 +    modify it under the terms of the GNU Library General Public
    4.10 +    License as published by the Free Software Foundation; either
    4.11 +    version 2 of the License, or (at your option) any later version.
    4.12 +
    4.13 +    This library is distributed in the hope that it will be useful,
    4.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    4.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    4.16 +    Library General Public License for more details.
    4.17 +
    4.18 +    You should have received a copy of the GNU Library General Public
    4.19 +    License along with this library; if not, write to the Free
    4.20 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    4.21 +
    4.22 +    Sam Lantinga
    4.23 +    slouken@libsdl.org
    4.24 +
    4.25 +    This file based on Apple sample code. We haven't changed the file name, 
    4.26 +    so if you want to see the original search for it on apple.com/developer
    4.27 +*/
    4.28 +
    4.29 +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    4.30 +//  AudioFilePlayer.h
    4.31 +//
    4.32 +#ifndef __AudioFilePlayer_H__
    4.33 +#define __AudioFilePlayer_H__
    4.34 +
    4.35 +#include <CoreServices/CoreServices.h>
    4.36 +
    4.37 +#include <AudioToolbox/AudioToolbox.h>
    4.38 +#include <AudioUnit/AudioUnit.h>
    4.39 +
    4.40 +#include "SDL_Error.h"
    4.41 +
    4.42 +const char* AudioFilePlayerErrorStr (OSStatus error);
    4.43 +
    4.44 +void ThrowResult (OSStatus result, const char *str);
    4.45 +
    4.46 +#define THROW_RESULT(str)                                       \
    4.47 +    if (result) {                                               \
    4.48 +        ThrowResult (result, str);                              \
    4.49 +    }
    4.50 +
    4.51 +typedef void (*AudioFilePlayNotifier)(void          *inRefCon,
    4.52 +                                    OSStatus        inStatus);
    4.53 +
    4.54 +enum {
    4.55 +    kAudioFilePlayErr_FilePlayUnderrun = -10000,
    4.56 +    kAudioFilePlay_FileIsFinished = -10001,
    4.57 +    kAudioFilePlay_PlayerIsUninitialized = -10002
    4.58 +};
    4.59 +
    4.60 +
    4.61 +class AudioFileManager;
    4.62 +
    4.63 +#pragma mark __________ AudioFilePlayer
    4.64 +class AudioFilePlayer
    4.65 +{
    4.66 +public:
    4.67 +    AudioFilePlayer (const FSRef    *inFileRef);
    4.68 +    
    4.69 +    ~AudioFilePlayer();
    4.70 +
    4.71 +    void            SetDestination (AudioUnit                   &inDestUnit, 
    4.72 +                                int                             inBusNumber);
    4.73 +    
    4.74 +    void            SetNotifier (AudioFilePlayNotifier inNotifier, void *inRefCon)
    4.75 +    {
    4.76 +        mNotifier = inNotifier;
    4.77 +        mRefCon = inRefCon;
    4.78 +    }
    4.79 +    
    4.80 +    void            SetStartFrame (int frame); // seek in the file
    4.81 +    
    4.82 +    int             GetCurrentFrame (); // get the current frame position
    4.83 +    
    4.84 +    void            SetStopFrame (int frame);   // set limit in the file
    4.85 +    
    4.86 +    void            Connect();
    4.87 +    
    4.88 +    void            Disconnect();
    4.89 +
    4.90 +    void            DoNotification (OSStatus inError) const;
    4.91 +    
    4.92 +    bool            IsConnected () const { return mConnected; }
    4.93 +
    4.94 +    UInt32          GetBusNumber () const { return mBusNumber; }
    4.95 +    
    4.96 +    AudioUnit       GetDestUnit () const { return mPlayUnit; }
    4.97 +    
    4.98 +    AudioConverterRef   GetAudioConverter() const { return mConverter; }
    4.99 +
   4.100 +#if DEBUG    
   4.101 +    void            Print() const 
   4.102 +    {
   4.103 +        CAShow (mAudioFileID);
   4.104 +        printf ("Destination Bus:%ld\n", GetBusNumber());
   4.105 +        printf ("Is 'aunt' unit:%s\n", (mIsAUNTUnit ? "true" : "false"));
   4.106 +        printf ("Is Connected:%s\n", (IsConnected() ? "true" : "false"));
   4.107 +        if (mConverter) CAShow (mConverter);
   4.108 +          printf ("- - - - - - - - - - - - - - \n");
   4.109 +    }
   4.110 +#endif
   4.111 +
   4.112 +    const AudioStreamBasicDescription&      GetFileFormat() const { return mFileDescription; }
   4.113 +    
   4.114 +private:
   4.115 +    AudioUnit                       mPlayUnit;
   4.116 +    UInt32                          mBusNumber;
   4.117 +    AudioFileID                     mAudioFileID;
   4.118 +    
   4.119 +    AudioUnitInputCallback          mInputCallback;
   4.120 +
   4.121 +    AudioStreamBasicDescription     mFileDescription;
   4.122 +    
   4.123 +    bool                            mConnected;
   4.124 +    bool                            mIsAUNTUnit;
   4.125 +    
   4.126 +    AudioFileManager*               mAudioFileManager;
   4.127 +    AudioConverterRef               mConverter;
   4.128 +    
   4.129 +    AudioFilePlayNotifier           mNotifier;
   4.130 +    void*                           mRefCon;
   4.131 +    
   4.132 +    int                             mStartFrame;
   4.133 +    
   4.134 +#pragma mark __________ Private_Methods
   4.135 +    
   4.136 +    void        OpenFile (const FSRef *inRef, SInt64& outFileSize);
   4.137 +};
   4.138 +
   4.139 +#pragma mark __________ AudioFileManager
   4.140 +class AudioFileManager
   4.141 +{
   4.142 +public:
   4.143 +    AudioFileManager (AudioFilePlayer& inParent, AudioFileID inFile)
   4.144 +        : mParent (inParent),
   4.145 +          mAudioFileID (inFile),
   4.146 +          mFileBuffer (0),
   4.147 +          mByteCounter (0)
   4.148 +        {}
   4.149 +    
   4.150 +    virtual ~AudioFileManager();
   4.151 +    
   4.152 +    
   4.153 +    void                Connect (AudioConverterRef inConverter) 
   4.154 +    {
   4.155 +        mParentConverter = inConverter;
   4.156 +        DoConnect();
   4.157 +    }
   4.158 +
   4.159 +        // this method should NOT be called by an object of this class
   4.160 +        // as it is called by the parent's Disconnect() method
   4.161 +    virtual void        Disconnect () {}
   4.162 +
   4.163 +    const AudioFileID&  GetFileID() const { return mAudioFileID; }
   4.164 +
   4.165 +    const char*         GetFileBuffer () { return mFileBuffer; }
   4.166 +
   4.167 +    const AudioFilePlayer&  GetParent () const { return mParent; }
   4.168 +    
   4.169 +    virtual void        SetPosition (SInt64 pos) = 0;  // seek/rewind in the file
   4.170 +    
   4.171 +    virtual int         GetByteCounter () { return mByteCounter; } // return actual bytes streamed to audio hardware
   4.172 +    
   4.173 +    virtual void        SetEndOfFile (SInt64 pos) = 0;  // set the "EOF" (will behave just like it reached eof)
   4.174 +   
   4.175 +protected:
   4.176 +    AudioFilePlayer&            mParent;
   4.177 +    AudioConverterRef           mParentConverter;
   4.178 +    const AudioFileID           mAudioFileID;
   4.179 +    
   4.180 +    char*                       mFileBuffer;
   4.181 +
   4.182 +    OSStatus            Render (AudioBuffer &ioData);
   4.183 +
   4.184 +    int                         mByteCounter;
   4.185 +    
   4.186 +    virtual OSStatus    GetFileData (void** inOutData, UInt32 *inOutDataSize) = 0;
   4.187 +    
   4.188 +    virtual void        DoConnect () = 0;
   4.189 +        
   4.190 +    virtual void        AfterRender () = 0;
   4.191 +
   4.192 +public:
   4.193 +    static OSStatus     FileInputProc (void                             *inRefCon, 
   4.194 +                                        AudioUnitRenderActionFlags      inActionFlags,
   4.195 +                                        const AudioTimeStamp            *inTimeStamp, 
   4.196 +                                        UInt32                          inBusNumber, 
   4.197 +                                        AudioBuffer                     *ioData);
   4.198 +    static OSStatus     ACInputProc (AudioConverterRef          inAudioConverter,
   4.199 +                                            UInt32*                     outDataSize,
   4.200 +                                            void**                      outData,
   4.201 +                                            void*                       inUserData);
   4.202 +};
   4.203 +
   4.204 +
   4.205 +#pragma mark __________ AudioFileReaderThread
   4.206 +class AudioFileReaderThread 
   4.207 +    : public AudioFileManager
   4.208 +{
   4.209 +public:
   4.210 +    const UInt32    mChunkSize;
   4.211 +    SInt64          mFileLength;
   4.212 +    SInt64          mReadFilePosition;
   4.213 +    bool            mWriteToFirstBuffer;
   4.214 +    bool            mFinishedReadingData;
   4.215 +    
   4.216 +    AudioFileReaderThread (AudioFilePlayer  &inParent, 
   4.217 +                            AudioFileID     &inFile, 
   4.218 +                            SInt64          inFileLength,
   4.219 +                            UInt32          inChunkSize);
   4.220 +    
   4.221 +    virtual void        Disconnect ();
   4.222 +
   4.223 +    virtual void        SetPosition (SInt64 pos);  // seek/rewind in the file
   4.224 +    
   4.225 +    virtual void        SetEndOfFile (SInt64 pos);  // set the "EOF" (will behave just like it reached eof)
   4.226 +    
   4.227 +protected:
   4.228 +    virtual void        DoConnect ();
   4.229 +
   4.230 +    virtual OSStatus    GetFileData (void** inOutData, UInt32 *inOutDataSize);
   4.231 +
   4.232 +    virtual void        AfterRender ();
   4.233 +
   4.234 +private:
   4.235 +    bool                        mReadFromFirstBuffer;
   4.236 +    bool                        mLockUnsuccessful;
   4.237 +    bool                        mIsEngaged;
   4.238 +    
   4.239 +    int                         mNumTimesAskedSinceFinished;
   4.240 +};
   4.241 +
   4.242 +
   4.243 +#endif
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/src/cdrom/macosx/AudioFileReaderThread.cpp	Tue Apr 15 16:33:56 2003 +0000
     5.3 @@ -0,0 +1,419 @@
     5.4 +/*
     5.5 +    SDL - Simple DirectMedia Layer
     5.6 +    Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  Sam Lantinga
     5.7 +
     5.8 +    This library is free software; you can redistribute it and/or
     5.9 +    modify it under the terms of the GNU Library General Public
    5.10 +    License as published by the Free Software Foundation; either
    5.11 +    version 2 of the License, or (at your option) any later version.
    5.12 +
    5.13 +    This library is distributed in the hope that it will be useful,
    5.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    5.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    5.16 +    Library General Public License for more details.
    5.17 +
    5.18 +    You should have received a copy of the GNU Library General Public
    5.19 +    License along with this library; if not, write to the Free
    5.20 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    5.21 +
    5.22 +    Sam Lantinga
    5.23 +    slouken@libsdl.org
    5.24 +
    5.25 +    This file based on Apple sample code. We haven't changed the file name, 
    5.26 +    so if you want to see the original search for it on apple.com/developer
    5.27 +*/
    5.28 +
    5.29 +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    5.30 +// AudioFileReaderThread.cpp
    5.31 +//
    5.32 +#include "AudioFilePlayer.h"
    5.33 +#include <mach/mach.h> //used for setting policy of thread
    5.34 +#include "CAGuard.h"
    5.35 +#include <pthread.h>
    5.36 +
    5.37 +#include <list>
    5.38 +
    5.39 +class FileReaderThread {
    5.40 +public:
    5.41 +	FileReaderThread ();
    5.42 +
    5.43 +	CAGuard&					GetGuard() { return mGuard; }
    5.44 +    
    5.45 +	void						AddReader();
    5.46 +	
    5.47 +	void						RemoveReader (const AudioFileReaderThread* inItem);
    5.48 +		
    5.49 +		// returns true if succeeded
    5.50 +	bool						TryNextRead (AudioFileReaderThread* inItem)
    5.51 +	{
    5.52 +		bool didLock = false;
    5.53 +		bool succeeded = false;
    5.54 +		if (mGuard.Try (didLock))
    5.55 +		{
    5.56 +			mFileData.push_back (inItem);
    5.57 +			mGuard.Notify();
    5.58 +			succeeded = true;
    5.59 +
    5.60 +			if (didLock)
    5.61 +				mGuard.Unlock();
    5.62 +		}
    5.63 +				
    5.64 +		return succeeded;
    5.65 +	}	
    5.66 +	
    5.67 +    int		mThreadShouldDie;
    5.68 +    
    5.69 +private:
    5.70 +	typedef	std::list<AudioFileReaderThread*> FileData;
    5.71 +
    5.72 +	CAGuard				mGuard;
    5.73 +	UInt32				mThreadPriority;
    5.74 +	
    5.75 +	int					mNumReaders;	
    5.76 +	FileData			mFileData;
    5.77 +
    5.78 +
    5.79 +	void 						ReadNextChunk ();
    5.80 +	
    5.81 +	void 						StartFixedPriorityThread ();
    5.82 +    static UInt32				GetThreadBasePriority (pthread_t inThread);
    5.83 +    
    5.84 +	static void*				DiskReaderEntry (void *inRefCon);
    5.85 +};
    5.86 +
    5.87 +FileReaderThread::FileReaderThread ()
    5.88 +	  : mThreadPriority (62),
    5.89 +		mNumReaders (0)
    5.90 +{
    5.91 +}
    5.92 +
    5.93 +void	FileReaderThread::AddReader()
    5.94 +{
    5.95 +	if (mNumReaders == 0)
    5.96 +	{
    5.97 +		mThreadShouldDie = false;
    5.98 +	
    5.99 +		StartFixedPriorityThread ();
   5.100 +	}
   5.101 +	mNumReaders++;
   5.102 +}
   5.103 +
   5.104 +void	FileReaderThread::RemoveReader (const AudioFileReaderThread* inItem)
   5.105 +{
   5.106 +	if (mNumReaders > 0)
   5.107 +	{
   5.108 +		CAGuard::Locker fileReadLock (mGuard);
   5.109 +
   5.110 +		for (FileData::iterator iter = mFileData.begin(); iter != mFileData.end(); ++iter)
   5.111 +		{
   5.112 +			if ((*iter) == inItem) {	
   5.113 +				mFileData.erase (iter);
   5.114 +			}
   5.115 +		}
   5.116 +		
   5.117 +		if (--mNumReaders == 0) {
   5.118 +			mThreadShouldDie = true;
   5.119 +			mGuard.Notify(); // wake up thread so it will quit
   5.120 +            mGuard.Wait();   // wait for thread to die
   5.121 +		}
   5.122 +	}	
   5.123 +}
   5.124 +
   5.125 +void 	FileReaderThread::StartFixedPriorityThread ()
   5.126 +{
   5.127 +	pthread_attr_t		theThreadAttrs;
   5.128 +	pthread_t			pThread;
   5.129 +	
   5.130 +	OSStatus result = pthread_attr_init(&theThreadAttrs);
   5.131 +		THROW_RESULT("pthread_attr_init - Thread attributes could not be created.")
   5.132 +	
   5.133 +	result = pthread_attr_setdetachstate(&theThreadAttrs, PTHREAD_CREATE_DETACHED);
   5.134 +		THROW_RESULT("pthread_attr_setdetachstate - Thread attributes could not be detached.")
   5.135 +	
   5.136 +	result = pthread_create (&pThread, &theThreadAttrs, DiskReaderEntry, this);
   5.137 +		THROW_RESULT("pthread_create - Create and start the thread.")
   5.138 +	
   5.139 +	pthread_attr_destroy(&theThreadAttrs);
   5.140 +    
   5.141 +	// we've now created the thread and started it
   5.142 +	// we'll now set the priority of the thread to the nominated priority
   5.143 +	// and we'll also make the thread fixed
   5.144 +    thread_extended_policy_data_t		theFixedPolicy;
   5.145 +    thread_precedence_policy_data_t		thePrecedencePolicy;
   5.146 +    SInt32								relativePriority;
   5.147 +    
   5.148 +    // make thread fixed
   5.149 +    theFixedPolicy.timeshare = false;	// set to true for a non-fixed thread
   5.150 +    result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT);
   5.151 +        THROW_RESULT("thread_policy - Couldn't set thread as fixed priority.")
   5.152 +    // set priority
   5.153 +    // precedency policy's "importance" value is relative to spawning thread's priority
   5.154 +    relativePriority = mThreadPriority - FileReaderThread::GetThreadBasePriority (pthread_self());
   5.155 +        
   5.156 +    thePrecedencePolicy.importance = relativePriority;
   5.157 +    result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_PRECEDENCE_POLICY, (thread_policy_t)&thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT);
   5.158 +        THROW_RESULT("thread_policy - Couldn't set thread priority.")
   5.159 +}
   5.160 +
   5.161 +UInt32	FileReaderThread::GetThreadBasePriority (pthread_t inThread)
   5.162 +{
   5.163 +    thread_basic_info_data_t			threadInfo;
   5.164 +	policy_info_data_t					thePolicyInfo;
   5.165 +	unsigned int						count;
   5.166 +    
   5.167 +    // get basic info
   5.168 +    count = THREAD_BASIC_INFO_COUNT;
   5.169 +    thread_info (pthread_mach_thread_np (inThread), THREAD_BASIC_INFO, (integer_t*)&threadInfo, &count);
   5.170 +    
   5.171 +	switch (threadInfo.policy) {
   5.172 +		case POLICY_TIMESHARE:
   5.173 +			count = POLICY_TIMESHARE_INFO_COUNT;
   5.174 +			thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_TIMESHARE_INFO, (integer_t*)&(thePolicyInfo.ts), &count);
   5.175 +			return thePolicyInfo.ts.base_priority;
   5.176 +            break;
   5.177 +            
   5.178 +        case POLICY_FIFO:
   5.179 +			count = POLICY_FIFO_INFO_COUNT;
   5.180 +			thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_FIFO_INFO, (integer_t*)&(thePolicyInfo.fifo), &count);
   5.181 +            if (thePolicyInfo.fifo.depressed) {
   5.182 +                return thePolicyInfo.fifo.depress_priority;
   5.183 +            } else {
   5.184 +                return thePolicyInfo.fifo.base_priority;
   5.185 +            }
   5.186 +            break;
   5.187 +            
   5.188 +		case POLICY_RR:
   5.189 +			count = POLICY_RR_INFO_COUNT;
   5.190 +			thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_RR_INFO, (integer_t*)&(thePolicyInfo.rr), &count);
   5.191 +			if (thePolicyInfo.rr.depressed) {
   5.192 +                return thePolicyInfo.rr.depress_priority;
   5.193 +            } else {
   5.194 +                return thePolicyInfo.rr.base_priority;
   5.195 +            }
   5.196 +            break;
   5.197 +	}
   5.198 +    
   5.199 +    return 0;
   5.200 +}
   5.201 +
   5.202 +void	*FileReaderThread::DiskReaderEntry (void *inRefCon)
   5.203 +{
   5.204 +	FileReaderThread *This = (FileReaderThread *)inRefCon;
   5.205 +	This->ReadNextChunk();
   5.206 +	#if DEBUG
   5.207 +	printf ("finished with reading file\n");
   5.208 +	#endif
   5.209 +	
   5.210 +	return 0;
   5.211 +}
   5.212 +
   5.213 +void 	FileReaderThread::ReadNextChunk ()
   5.214 +{
   5.215 +	OSStatus result;
   5.216 +	UInt32	dataChunkSize;
   5.217 +	AudioFileReaderThread* theItem = 0;
   5.218 +
   5.219 +	for (;;) 
   5.220 +	{
   5.221 +		{ // this is a scoped based lock
   5.222 +			CAGuard::Locker fileReadLock (mGuard);
   5.223 +			
   5.224 +			if (this->mThreadShouldDie) {
   5.225 +            
   5.226 +                mGuard.Notify();
   5.227 +                return;
   5.228 +            }
   5.229 +			
   5.230 +			if (mFileData.empty())
   5.231 +			{
   5.232 +				mGuard.Wait();
   5.233 +			}
   5.234 +			            
   5.235 +			// kill thread
   5.236 +			if (this->mThreadShouldDie) {
   5.237 +            
   5.238 +                mGuard.Notify();
   5.239 +                return;
   5.240 +            }
   5.241 +
   5.242 +			theItem = mFileData.front();
   5.243 +			mFileData.pop_front();
   5.244 +		}
   5.245 +	
   5.246 +		if ((theItem->mFileLength - theItem->mReadFilePosition) < theItem->mChunkSize)
   5.247 +			dataChunkSize = theItem->mFileLength - theItem->mReadFilePosition;
   5.248 +		else
   5.249 +			dataChunkSize = theItem->mChunkSize;
   5.250 +		
   5.251 +			// this is the exit condition for the thread
   5.252 +		if (dataChunkSize == 0) {
   5.253 +			theItem->mFinishedReadingData = true;
   5.254 +			continue;
   5.255 +		}
   5.256 +			// construct pointer
   5.257 +		char* writePtr = const_cast<char*>(theItem->GetFileBuffer() + 
   5.258 +								(theItem->mWriteToFirstBuffer ? 0 : theItem->mChunkSize));
   5.259 +	
   5.260 +/*
   5.261 +        printf ("AudioFileReadBytes: theItem=%.8X fileID=%.8X pos=%.8X sz=%.8X flen=%.8X ptr=%.8X\n", 
   5.262 +            (unsigned int)theItem, (unsigned int)theItem->GetFileID(),
   5.263 +            (unsigned int)theItem->mReadFilePosition, (unsigned int)dataChunkSize, 
   5.264 +            (unsigned int)theItem->mFileLength, (unsigned int)writePtr);
   5.265 +*/            
   5.266 +		result = AudioFileReadBytes (theItem->GetFileID(), 
   5.267 +									false,
   5.268 +									theItem->mReadFilePosition, 
   5.269 +									&dataChunkSize, 
   5.270 +									writePtr);
   5.271 +		if (result) {
   5.272 +			theItem->GetParent().DoNotification(result);
   5.273 +			continue;
   5.274 +		}
   5.275 +		
   5.276 +		if (dataChunkSize != theItem->mChunkSize)
   5.277 +		{
   5.278 +			writePtr += dataChunkSize;
   5.279 +
   5.280 +            // can't exit yet.. we still have to pass the partial buffer back
   5.281 +            memset (writePtr, 0, (theItem->mChunkSize - dataChunkSize));
   5.282 +        }
   5.283 +		
   5.284 +		theItem->mWriteToFirstBuffer = !theItem->mWriteToFirstBuffer;	// switch buffers
   5.285 +		
   5.286 +		theItem->mReadFilePosition += dataChunkSize;		// increment count
   5.287 +	}
   5.288 +}
   5.289 +
   5.290 +
   5.291 +static FileReaderThread sReaderThread;
   5.292 +
   5.293 +AudioFileReaderThread::AudioFileReaderThread (AudioFilePlayer	&inParent, 
   5.294 +										AudioFileID 			&inFile, 
   5.295 +										SInt64 					inFileLength,
   5.296 +										UInt32					inChunkSize)
   5.297 +	: AudioFileManager (inParent, inFile),
   5.298 +	  mChunkSize (inChunkSize),
   5.299 +	  mFileLength (inFileLength),
   5.300 +	  mReadFilePosition (0),
   5.301 +	  mWriteToFirstBuffer (false),
   5.302 +	  mFinishedReadingData (false),
   5.303 +
   5.304 +	  mLockUnsuccessful (false),
   5.305 +	  mIsEngaged (false)
   5.306 +{
   5.307 +	mFileBuffer = (char*) malloc (mChunkSize * 2);
   5.308 +    assert (mFileBuffer != NULL);
   5.309 +}
   5.310 +
   5.311 +void	AudioFileReaderThread::DoConnect ()
   5.312 +{
   5.313 +	if (!mIsEngaged)
   5.314 +	{
   5.315 +		//mReadFilePosition = 0;
   5.316 +		mFinishedReadingData = false;
   5.317 +
   5.318 +		mNumTimesAskedSinceFinished = -1;
   5.319 +		mLockUnsuccessful = false;
   5.320 +		
   5.321 +		UInt32 dataChunkSize;
   5.322 +        
   5.323 +        if ((mFileLength - mReadFilePosition) < mChunkSize)
   5.324 +			dataChunkSize = mFileLength - mReadFilePosition;
   5.325 +		else
   5.326 +			dataChunkSize = mChunkSize;
   5.327 +        
   5.328 +		OSStatus result = AudioFileReadBytes ( mAudioFileID, 
   5.329 +												false, 
   5.330 +												mReadFilePosition, 
   5.331 +												&dataChunkSize, 
   5.332 +												mFileBuffer);
   5.333 +            THROW_RESULT("AudioFileReadBytes")
   5.334 +            
   5.335 +		mReadFilePosition += dataChunkSize;
   5.336 +				
   5.337 +		mWriteToFirstBuffer = false;
   5.338 +		mReadFromFirstBuffer = true;
   5.339 +
   5.340 +		sReaderThread.AddReader();
   5.341 +		
   5.342 +		mIsEngaged = true;
   5.343 +	}
   5.344 +	else
   5.345 +		throw static_cast<OSStatus>(-1); //thread has already been started
   5.346 +}
   5.347 +
   5.348 +void	AudioFileReaderThread::Disconnect ()
   5.349 +{
   5.350 +	if (mIsEngaged) 
   5.351 +	{
   5.352 +		sReaderThread.RemoveReader (this);
   5.353 +		mIsEngaged = false;
   5.354 +	}
   5.355 +}
   5.356 +
   5.357 +OSStatus AudioFileReaderThread::GetFileData (void** inOutData, UInt32 *inOutDataSize)
   5.358 +{
   5.359 +	if (mFinishedReadingData) 
   5.360 +	{
   5.361 +		++mNumTimesAskedSinceFinished;
   5.362 +		*inOutDataSize = 0;
   5.363 +		*inOutData = 0;
   5.364 +		return noErr;
   5.365 +	}
   5.366 +	
   5.367 +	if (mReadFromFirstBuffer == mWriteToFirstBuffer) {
   5.368 +		#if DEBUG
   5.369 +		printf ("* * * * * * * Can't keep up with reading file:%ld\n", mParent.GetBusNumber());
   5.370 +		#endif
   5.371 +		
   5.372 +		mParent.DoNotification (kAudioFilePlayErr_FilePlayUnderrun);
   5.373 +		*inOutDataSize = 0;
   5.374 +		*inOutData = 0;
   5.375 +	} else {
   5.376 +		*inOutDataSize = mChunkSize;
   5.377 +		*inOutData = mReadFromFirstBuffer ? mFileBuffer : (mFileBuffer + mChunkSize);
   5.378 +	}
   5.379 +
   5.380 +	mLockUnsuccessful = !sReaderThread.TryNextRead (this);
   5.381 +	
   5.382 +	mReadFromFirstBuffer = !mReadFromFirstBuffer;
   5.383 +
   5.384 +	return noErr;
   5.385 +}
   5.386 +
   5.387 +void 	AudioFileReaderThread::AfterRender ()
   5.388 +{
   5.389 +	if (mNumTimesAskedSinceFinished > 0)
   5.390 +	{
   5.391 +		bool didLock = false;
   5.392 +		if (sReaderThread.GetGuard().Try (didLock)) {
   5.393 +			mParent.DoNotification (kAudioFilePlay_FileIsFinished);
   5.394 +			if (didLock)
   5.395 +				sReaderThread.GetGuard().Unlock();
   5.396 +		}
   5.397 +	}
   5.398 +
   5.399 +	if (mLockUnsuccessful)
   5.400 +		mLockUnsuccessful = !sReaderThread.TryNextRead (this);
   5.401 +}
   5.402 +
   5.403 +void 	AudioFileReaderThread::SetPosition (SInt64 pos)
   5.404 +{
   5.405 +    if (pos < 0 || pos >= mFileLength) {
   5.406 +        SDL_SetError ("AudioFileReaderThread::SetPosition - position invalid: %d filelen=%d\n", 
   5.407 +            (unsigned int)pos, (unsigned int)mFileLength);
   5.408 +        pos = 0;
   5.409 +    }
   5.410 +        
   5.411 +    mReadFilePosition = pos;
   5.412 +}
   5.413 +    
   5.414 +void 	AudioFileReaderThread::SetEndOfFile (SInt64 pos)
   5.415 +{
   5.416 +    if (pos <= 0 || pos > mFileLength) {
   5.417 +        SDL_SetError ("AudioFileReaderThread::SetEndOfFile - position beyond actual eof\n");
   5.418 +        pos = mFileLength;
   5.419 +    }
   5.420 +    
   5.421 +    mFileLength = pos;
   5.422 +}
   5.423 \ No newline at end of file
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/src/cdrom/macosx/CAGuard.cpp	Tue Apr 15 16:33:56 2003 +0000
     6.3 @@ -0,0 +1,160 @@
     6.4 +/*
     6.5 +    SDL - Simple DirectMedia Layer
     6.6 +    Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  Sam Lantinga
     6.7 +
     6.8 +    This library is free software; you can redistribute it and/or
     6.9 +    modify it under the terms of the GNU Library General Public
    6.10 +    License as published by the Free Software Foundation; either
    6.11 +    version 2 of the License, or (at your option) any later version.
    6.12 +
    6.13 +    This library is distributed in the hope that it will be useful,
    6.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    6.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    6.16 +    Library General Public License for more details.
    6.17 +
    6.18 +    You should have received a copy of the GNU Library General Public
    6.19 +    License along with this library; if not, write to the Free
    6.20 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    6.21 +
    6.22 +    Sam Lantinga
    6.23 +    slouken@libsdl.org
    6.24 +*/
    6.25 +/*  
    6.26 +    Note: This file hasn't been modified so technically we have to keep the disclaimer :-(
    6.27 +    
    6.28 +    Copyright:  © Copyright 2002 Apple Computer, Inc. All rights reserved.
    6.29 +
    6.30 +    Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
    6.31 +            ("Apple") in consideration of your agreement to the following terms, and your
    6.32 +            use, installation, modification or redistribution of this Apple software
    6.33 +            constitutes acceptance of these terms.  If you do not agree with these terms,
    6.34 +            please do not use, install, modify or redistribute this Apple software.
    6.35 +
    6.36 +            In consideration of your agreement to abide by the following terms, and subject
    6.37 +            to these terms, Apple grants you a personal, non-exclusive license, under AppleΥs
    6.38 +            copyrights in this original Apple software (the "Apple Software"), to use,
    6.39 +            reproduce, modify and redistribute the Apple Software, with or without
    6.40 +            modifications, in source and/or binary forms; provided that if you redistribute
    6.41 +            the Apple Software in its entirety and without modifications, you must retain
    6.42 +            this notice and the following text and disclaimers in all such redistributions of
    6.43 +            the Apple Software.  Neither the name, trademarks, service marks or logos of
    6.44 +            Apple Computer, Inc. may be used to endorse or promote products derived from the
    6.45 +            Apple Software without specific prior written permission from Apple.  Except as
    6.46 +            expressly stated in this notice, no other rights or licenses, express or implied,
    6.47 +            are granted by Apple herein, including but not limited to any patent rights that
    6.48 +            may be infringed by your derivative works or by other works in which the Apple
    6.49 +            Software may be incorporated.
    6.50 +
    6.51 +            The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
    6.52 +            WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
    6.53 +            WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    6.54 +            PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
    6.55 +            COMBINATION WITH YOUR PRODUCTS.
    6.56 +
    6.57 +            IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
    6.58 +            CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
    6.59 +            GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    6.60 +            ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
    6.61 +            OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
    6.62 +            (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
    6.63 +            ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    6.64 +*/
    6.65 +/*=============================================================================
    6.66 +    CAGuard.cp
    6.67 +
    6.68 +=============================================================================*/
    6.69 +
    6.70 +//=============================================================================
    6.71 +//  Includes
    6.72 +//=============================================================================
    6.73 +
    6.74 +#include <stdio.h>
    6.75 +
    6.76 +#define NDEBUG 1
    6.77 +#include <assert.h>
    6.78 +
    6.79 +
    6.80 +#include "CAGuard.h"
    6.81 +
    6.82 +//#warning      Need a try-based Locker too
    6.83 +//=============================================================================
    6.84 +//  CAGuard
    6.85 +//=============================================================================
    6.86 +
    6.87 +CAGuard::CAGuard()
    6.88 +{
    6.89 +    OSStatus theError = pthread_mutex_init(&mMutex, NULL);
    6.90 +    assert(theError == 0);
    6.91 +    
    6.92 +    theError = pthread_cond_init(&mCondVar, NULL);
    6.93 +    assert(theError == 0);
    6.94 +    
    6.95 +    mOwner = 0;
    6.96 +}
    6.97 +
    6.98 +CAGuard::~CAGuard()
    6.99 +{
   6.100 +    pthread_mutex_destroy(&mMutex);
   6.101 +    pthread_cond_destroy(&mCondVar);
   6.102 +}
   6.103 +
   6.104 +bool    CAGuard::Lock()
   6.105 +{
   6.106 +    bool theAnswer = false;
   6.107 +    
   6.108 +    if(pthread_self() != mOwner)
   6.109 +    {
   6.110 +        OSStatus theError = pthread_mutex_lock(&mMutex);
   6.111 +        assert(theError == 0);
   6.112 +        mOwner = pthread_self();
   6.113 +        theAnswer = true;
   6.114 +    }
   6.115 +
   6.116 +    return theAnswer;
   6.117 +}
   6.118 +
   6.119 +void    CAGuard::Unlock()
   6.120 +{
   6.121 +    assert(pthread_self() == mOwner);
   6.122 +
   6.123 +    mOwner = 0;
   6.124 +    OSStatus theError = pthread_mutex_unlock(&mMutex);
   6.125 +    assert(theError == 0);
   6.126 +}
   6.127 +
   6.128 +bool    CAGuard::Try (bool& outWasLocked)
   6.129 +{
   6.130 +    bool theAnswer = false;
   6.131 +    outWasLocked = false;
   6.132 +    
   6.133 +    if (pthread_self() == mOwner) {
   6.134 +        theAnswer = true;
   6.135 +        outWasLocked = false;
   6.136 +    } else {
   6.137 +        OSStatus theError = pthread_mutex_trylock(&mMutex);
   6.138 +        if (theError == 0) {
   6.139 +            mOwner = pthread_self();
   6.140 +            theAnswer = true;
   6.141 +            outWasLocked = true;
   6.142 +        }
   6.143 +    }
   6.144 +    
   6.145 +    return theAnswer;
   6.146 +}
   6.147 +
   6.148 +void    CAGuard::Wait()
   6.149 +{
   6.150 +    assert(pthread_self() == mOwner);
   6.151 +
   6.152 +    mOwner = 0;
   6.153 +
   6.154 +    OSStatus theError = pthread_cond_wait(&mCondVar, &mMutex);
   6.155 +    assert(theError == 0);
   6.156 +    mOwner = pthread_self();
   6.157 +}
   6.158 +
   6.159 +void    CAGuard::Notify()
   6.160 +{
   6.161 +    OSStatus theError = pthread_cond_signal(&mCondVar);
   6.162 +    assert(theError == 0);
   6.163 +}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/src/cdrom/macosx/CAGuard.h	Tue Apr 15 16:33:56 2003 +0000
     7.3 @@ -0,0 +1,143 @@
     7.4 +/*
     7.5 +    SDL - Simple DirectMedia Layer
     7.6 +    Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  Sam Lantinga
     7.7 +
     7.8 +    This library is free software; you can redistribute it and/or
     7.9 +    modify it under the terms of the GNU Library General Public
    7.10 +    License as published by the Free Software Foundation; either
    7.11 +    version 2 of the License, or (at your option) any later version.
    7.12 +
    7.13 +    This library is distributed in the hope that it will be useful,
    7.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    7.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    7.16 +    Library General Public License for more details.
    7.17 +
    7.18 +    You should have received a copy of the GNU Library General Public
    7.19 +    License along with this library; if not, write to the Free
    7.20 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    7.21 +
    7.22 +    Sam Lantinga
    7.23 +    slouken@libsdl.org
    7.24 +*/
    7.25 +/*  
    7.26 +    Note: This file hasn't been modified so technically we have to keep the disclaimer :-(
    7.27 +
    7.28 +
    7.29 +    Copyright:  © Copyright 2002 Apple Computer, Inc. All rights reserved.
    7.30 +
    7.31 +    Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
    7.32 +            ("Apple") in consideration of your agreement to the following terms, and your
    7.33 +            use, installation, modification or redistribution of this Apple software
    7.34 +            constitutes acceptance of these terms.  If you do not agree with these terms,
    7.35 +            please do not use, install, modify or redistribute this Apple software.
    7.36 +
    7.37 +            In consideration of your agreement to abide by the following terms, and subject
    7.38 +            to these terms, Apple grants you a personal, non-exclusive license, under AppleΥs
    7.39 +            copyrights in this original Apple software (the "Apple Software"), to use,
    7.40 +            reproduce, modify and redistribute the Apple Software, with or without
    7.41 +            modifications, in source and/or binary forms; provided that if you redistribute
    7.42 +            the Apple Software in its entirety and without modifications, you must retain
    7.43 +            this notice and the following text and disclaimers in all such redistributions of
    7.44 +            the Apple Software.  Neither the name, trademarks, service marks or logos of
    7.45 +            Apple Computer, Inc. may be used to endorse or promote products derived from the
    7.46 +            Apple Software without specific prior written permission from Apple.  Except as
    7.47 +            expressly stated in this notice, no other rights or licenses, express or implied,
    7.48 +            are granted by Apple herein, including but not limited to any patent rights that
    7.49 +            may be infringed by your derivative works or by other works in which the Apple
    7.50 +            Software may be incorporated.
    7.51 +
    7.52 +            The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
    7.53 +            WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
    7.54 +            WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
    7.55 +            PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
    7.56 +            COMBINATION WITH YOUR PRODUCTS.
    7.57 +
    7.58 +            IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
    7.59 +            CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
    7.60 +            GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
    7.61 +            ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
    7.62 +            OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
    7.63 +            (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
    7.64 +            ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    7.65 +*/
    7.66 +/*=============================================================================
    7.67 +    CAGuard.h
    7.68 +
    7.69 +=============================================================================*/
    7.70 +#if !defined(__CAGuard_h__)
    7.71 +#define __CAGuard_h__
    7.72 +
    7.73 +//=============================================================================
    7.74 +//  Includes
    7.75 +//=============================================================================
    7.76 +
    7.77 +#include <CoreAudio/CoreAudioTypes.h>
    7.78 +#include <pthread.h>
    7.79 +
    7.80 +
    7.81 +//=============================================================================
    7.82 +//  CAGuard
    7.83 +//
    7.84 +//  This is your typical mutex with signalling implemented via pthreads.
    7.85 +//  Lock() will return true if and only if the guard is locked on that call.
    7.86 +//  A thread that already has the guard will receive 'false' if it locks it
    7.87 +//  again. Use of the stack-based CAGuard::Locker class is highly recommended
    7.88 +//  to properly manage the recursive nesting. The Wait calls with timeouts
    7.89 +//  will return true if and only if the timeout period expired. They will
    7.90 +//  return false if they receive notification any other way.
    7.91 +//=============================================================================
    7.92 +
    7.93 +class   CAGuard
    7.94 +{
    7.95 +
    7.96 +//  Construction/Destruction
    7.97 +public:
    7.98 +                    CAGuard();
    7.99 +    virtual         ~CAGuard();
   7.100 +
   7.101 +//  Actions
   7.102 +public:
   7.103 +    virtual bool    Lock();
   7.104 +    virtual void    Unlock();
   7.105 +    virtual bool    Try(bool& outWasLocked);    // returns true if lock is free, false if not
   7.106 +        
   7.107 +    virtual void    Wait();
   7.108 +    
   7.109 +    virtual void    Notify();
   7.110 +
   7.111 +//  Implementation
   7.112 +protected:
   7.113 +    pthread_mutex_t mMutex;
   7.114 +    pthread_cond_t  mCondVar;
   7.115 +    pthread_t       mOwner;
   7.116 +    
   7.117 +//  Helper class to manage taking and releasing recursively
   7.118 +public:
   7.119 +    class           Locker
   7.120 +    {
   7.121 +    
   7.122 +    //  Construction/Destruction
   7.123 +    public:
   7.124 +                    Locker(CAGuard& inGuard) : mGuard(inGuard), mNeedsRelease(false) { mNeedsRelease = mGuard.Lock(); }
   7.125 +                    ~Locker() { if(mNeedsRelease) { mGuard.Unlock(); } }
   7.126 +    
   7.127 +    private:
   7.128 +                    Locker(const Locker&);
   7.129 +        Locker&     operator=(const Locker&);
   7.130 +    
   7.131 +    //  Actions
   7.132 +    public:
   7.133 +        void        Wait() { mGuard.Wait(); }
   7.134 +        
   7.135 +        void        Notify() { mGuard.Notify(); }
   7.136 +
   7.137 +    //  Implementation
   7.138 +    private:
   7.139 +        CAGuard&    mGuard;
   7.140 +        bool        mNeedsRelease;
   7.141 +    
   7.142 +    };
   7.143 +
   7.144 +};
   7.145 +
   7.146 +#endif
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/src/cdrom/macosx/CDPlayer.cpp	Tue Apr 15 16:33:56 2003 +0000
     8.3 @@ -0,0 +1,715 @@
     8.4 +/*
     8.5 +    SDL - Simple DirectMedia Layer
     8.6 +    Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  Sam Lantinga
     8.7 +
     8.8 +    This library is free software; you can redistribute it and/or
     8.9 +    modify it under the terms of the GNU Library General Public
    8.10 +    License as published by the Free Software Foundation; either
    8.11 +    version 2 of the License, or (at your option) any later version.
    8.12 +
    8.13 +    This library is distributed in the hope that it will be useful,
    8.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    8.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    8.16 +    Library General Public License for more details.
    8.17 +
    8.18 +    You should have received a copy of the GNU Library General Public
    8.19 +    License along with this library; if not, write to the Free
    8.20 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    8.21 +
    8.22 +    Sam Lantinga
    8.23 +    slouken@libsdl.org
    8.24 +*/
    8.25 +
    8.26 +#include "CDPlayer.h"
    8.27 +#include "AudioFilePlayer.h"
    8.28 +#include "CAGuard.h"
    8.29 +
    8.30 +// we're exporting these functions into C land for SDL_syscdrom.c
    8.31 +extern "C" {
    8.32 +
    8.33 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
    8.34 +//  Constants
    8.35 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
    8.36 +
    8.37 +#define kAudioCDFilesystemID   (UInt16)(('J' << 8) | 'H') // 'JH'; this avoids compiler warning
    8.38 +
    8.39 +// XML PList keys
    8.40 +#define kRawTOCDataString           "Format 0x02 TOC Data"
    8.41 +#define kSessionsString             "Sessions"
    8.42 +#define kSessionTypeString          "Session Type"
    8.43 +#define kTrackArrayString           "Track Array"
    8.44 +#define kFirstTrackInSessionString      "First Track"
    8.45 +#define kLastTrackInSessionString       "Last Track"
    8.46 +#define kLeadoutBlockString         "Leadout Block"
    8.47 +#define kDataKeyString              "Data"
    8.48 +#define kPointKeyString             "Point"
    8.49 +#define kSessionNumberKeyString         "Session Number"
    8.50 +#define kStartBlockKeyString            "Start Block"   
    8.51 +    
    8.52 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
    8.53 +//  Globals
    8.54 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
    8.55 +
    8.56 +#pragma mark -- Globals --
    8.57 +
    8.58 +static bool             playBackWasInit = false;
    8.59 +static AudioUnit        theUnit;
    8.60 +static AudioFilePlayer* thePlayer = NULL;
    8.61 +static CDPlayerCompletionProc   completionProc = NULL;
    8.62 +static pthread_mutex_t  apiMutex;
    8.63 +static pthread_t        callbackThread;
    8.64 +static pthread_mutex_t  callbackMutex;
    8.65 +static volatile  int    runCallBackThread;
    8.66 +static int              initMutex = SDL_TRUE;
    8.67 +static SDL_CD*          theCDROM;
    8.68 +
    8.69 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
    8.70 +//  Prototypes
    8.71 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
    8.72 +
    8.73 +#pragma mark -- Prototypes --
    8.74 +
    8.75 +OSStatus CheckInit ();
    8.76 +
    8.77 +OSStatus MatchAUFormats (AudioUnit theUnit, UInt32 theInputBus);
    8.78 +
    8.79 +void     FilePlayNotificationHandler (void* inRefCon, OSStatus inStatus);
    8.80 +
    8.81 +void*    RunCallBackThread (void* inRefCon);
    8.82 +
    8.83 +
    8.84 +#pragma mark -- Public Functions --
    8.85 +
    8.86 +void     Lock ()
    8.87 +{
    8.88 +    if (initMutex) {
    8.89 +    
    8.90 +        pthread_mutexattr_t attr;
    8.91 +        
    8.92 +        pthread_mutexattr_init (&attr);
    8.93 +        pthread_mutex_init (&apiMutex, &attr);
    8.94 +        pthread_mutexattr_destroy (&attr);
    8.95 +        
    8.96 +        initMutex = SDL_FALSE;
    8.97 +    }
    8.98 +    
    8.99 +    pthread_mutex_lock (&apiMutex);
   8.100 +}
   8.101 +
   8.102 +void     Unlock ()
   8.103 +{
   8.104 +    pthread_mutex_unlock (&apiMutex);
   8.105 +}
   8.106 +
   8.107 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   8.108 +//  DetectAudioCDVolumes
   8.109 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   8.110 +
   8.111 +int DetectAudioCDVolumes(FSVolumeRefNum *volumes, int numVolumes)
   8.112 +{
   8.113 +    int volumeIndex;
   8.114 +    int cdVolumeCount = 0;
   8.115 +    OSStatus result = noErr;
   8.116 +    
   8.117 +    for (volumeIndex = 1; result == noErr || result != nsvErr; volumeIndex++)
   8.118 +    {
   8.119 +        FSVolumeRefNum  actualVolume;
   8.120 +        HFSUniStr255    volumeName;
   8.121 +        FSVolumeInfo    volumeInfo;
   8.122 +        FSRef           rootDirectory;
   8.123 +        
   8.124 +        memset (&volumeInfo, 0, sizeof(volumeInfo));
   8.125 +        
   8.126 +        result = FSGetVolumeInfo (kFSInvalidVolumeRefNum,
   8.127 +                                  volumeIndex,
   8.128 +                                  &actualVolume,
   8.129 +                                  kFSVolInfoFSInfo,
   8.130 +                                  &volumeInfo,
   8.131 +                                  &volumeName,
   8.132 +                                  &rootDirectory); 
   8.133 +         
   8.134 +        if (result == noErr)
   8.135 +        {
   8.136 +            if (volumeInfo.filesystemID == kAudioCDFilesystemID) // It's an audio CD
   8.137 +            {                
   8.138 +                if (volumes != NULL && cdVolumeCount < numVolumes)
   8.139 +                    volumes[cdVolumeCount] = actualVolume;
   8.140 +            
   8.141 +                cdVolumeCount++;
   8.142 +            }
   8.143 +        }
   8.144 +        else 
   8.145 +        {
   8.146 +            // I'm commenting this out because it seems to be harmless
   8.147 +            //SDL_SetError ("DetectAudioCDVolumes: FSGetVolumeInfo returned %d", result);
   8.148 +        }
   8.149 +    }
   8.150 +        
   8.151 +    return cdVolumeCount;
   8.152 +}
   8.153 +
   8.154 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   8.155 +//  ReadTOCData
   8.156 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   8.157 +
   8.158 +int ReadTOCData (FSVolumeRefNum theVolume, SDL_CD *theCD)
   8.159 +{
   8.160 +    HFSUniStr255      dataForkName;
   8.161 +    OSStatus          theErr;
   8.162 +    SInt16            forkRefNum;
   8.163 +    SInt64            forkSize;
   8.164 +    Ptr               forkData = 0;
   8.165 +    ByteCount         actualRead;
   8.166 +    CFDataRef         dataRef = 0;
   8.167 +    CFPropertyListRef propertyListRef = 0;
   8.168 +
   8.169 +    FSRefParam      fsRefPB;
   8.170 +    FSRef           tocPlistFSRef;
   8.171 +    
   8.172 +    const char* error = "Unspecified Error";
   8.173 +    
   8.174 +    // get stuff from .TOC.plist                                                   
   8.175 +    fsRefPB.ioCompletion = NULL;
   8.176 +    fsRefPB.ioNamePtr = "\p.TOC.plist";
   8.177 +    fsRefPB.ioVRefNum = theVolume;
   8.178 +    fsRefPB.ioDirID = 0;
   8.179 +    fsRefPB.newRef = &tocPlistFSRef;
   8.180 +    
   8.181 +    theErr = PBMakeFSRefSync (&fsRefPB);
   8.182 +    if(theErr != noErr) {
   8.183 +        error = "PBMakeFSRefSync";
   8.184 +        goto bail;
   8.185 +    }
   8.186 +    
   8.187 +    // Load and parse the TOC XML data
   8.188 +
   8.189 +    theErr = FSGetDataForkName (&dataForkName);
   8.190 +    if (theErr != noErr) {
   8.191 +        error = "FSGetDataForkName";
   8.192 +        goto bail;
   8.193 +    }
   8.194 +    
   8.195 +    theErr = FSOpenFork (&tocPlistFSRef, dataForkName.length, dataForkName.unicode, fsRdPerm, &forkRefNum);
   8.196 +    if (theErr != noErr) {
   8.197 +        error = "FSOpenFork";
   8.198 +        goto bail;
   8.199 +    }
   8.200 +    
   8.201 +    theErr = FSGetForkSize (forkRefNum, &forkSize);
   8.202 +    if (theErr != noErr) {
   8.203 +        error = "FSGetForkSize";
   8.204 +        goto bail;
   8.205 +    }
   8.206 +    
   8.207 +    // Allocate some memory for the XML data
   8.208 +    forkData = NewPtr (forkSize);
   8.209 +    if(forkData == NULL) {
   8.210 +        error = "NewPtr";
   8.211 +        goto bail;
   8.212 +    }
   8.213 +    
   8.214 +    theErr = FSReadFork (forkRefNum, fsFromStart, 0 /* offset location */, forkSize, forkData, &actualRead);
   8.215 +    if(theErr != noErr) {
   8.216 +        error = "FSReadFork";
   8.217 +        goto bail;
   8.218 +    }
   8.219 +    
   8.220 +    dataRef = CFDataCreate (kCFAllocatorDefault, (UInt8 *)forkData, forkSize);
   8.221 +    if(dataRef == 0) {
   8.222 +        error = "CFDataCreate";
   8.223 +        goto bail;
   8.224 +    }
   8.225 +
   8.226 +    propertyListRef = CFPropertyListCreateFromXMLData (kCFAllocatorDefault,
   8.227 +                                                       dataRef,
   8.228 +                                                       kCFPropertyListImmutable,
   8.229 +                                                       NULL);
   8.230 +    if (propertyListRef == NULL) {
   8.231 +        error = "CFPropertyListCreateFromXMLData";
   8.232 +        goto bail;
   8.233 +    }
   8.234 +
   8.235 +    // Now we got the Property List in memory. Parse it.
   8.236 +    
   8.237 +    // First, make sure the root item is a CFDictionary. If not, release and bail.
   8.238 +    if(CFGetTypeID(propertyListRef)== CFDictionaryGetTypeID())
   8.239 +    {
   8.240 +        CFDictionaryRef dictRef = (CFDictionaryRef)propertyListRef;
   8.241 +        
   8.242 +        CFDataRef   theRawTOCDataRef;
   8.243 +        CFArrayRef  theSessionArrayRef;
   8.244 +        CFIndex     numSessions;
   8.245 +        CFIndex     index;
   8.246 +        
   8.247 +        // This is how we get the Raw TOC Data
   8.248 +        theRawTOCDataRef = (CFDataRef)CFDictionaryGetValue (dictRef, CFSTR(kRawTOCDataString));
   8.249 +        
   8.250 +        // Get the session array info.
   8.251 +        theSessionArrayRef = (CFArrayRef)CFDictionaryGetValue (dictRef, CFSTR(kSessionsString));
   8.252 +        
   8.253 +        // Find out how many sessions there are.
   8.254 +        numSessions = CFArrayGetCount (theSessionArrayRef);
   8.255 +        
   8.256 +        // Initialize the total number of tracks to 0
   8.257 +        theCD->numtracks = 0;
   8.258 +        
   8.259 +        // Iterate over all sessions, collecting the track data
   8.260 +        for(index = 0; index < numSessions; index++)
   8.261 +        {
   8.262 +            CFDictionaryRef theSessionDict;
   8.263 +            CFNumberRef     leadoutBlock;
   8.264 +            CFArrayRef      trackArray;
   8.265 +            CFIndex         numTracks;
   8.266 +            CFIndex         trackIndex;
   8.267 +            UInt32          value = 0;
   8.268 +            
   8.269 +            theSessionDict      = (CFDictionaryRef) CFArrayGetValueAtIndex (theSessionArrayRef, index);
   8.270 +            leadoutBlock        = (CFNumberRef) CFDictionaryGetValue (theSessionDict, CFSTR(kLeadoutBlockString));
   8.271 +            
   8.272 +            trackArray = (CFArrayRef)CFDictionaryGetValue (theSessionDict, CFSTR(kTrackArrayString));
   8.273 +            
   8.274 +            numTracks = CFArrayGetCount (trackArray);
   8.275 +
   8.276 +            for(trackIndex = 0; trackIndex < numTracks; trackIndex++) {
   8.277 +                    
   8.278 +                CFDictionaryRef theTrackDict;
   8.279 +                CFNumberRef     trackNumber;
   8.280 +                CFNumberRef     sessionNumber;
   8.281 +                CFNumberRef     startBlock;
   8.282 +                CFBooleanRef    isDataTrack;
   8.283 +                UInt32          value;
   8.284 +                
   8.285 +                theTrackDict  = (CFDictionaryRef) CFArrayGetValueAtIndex (trackArray, trackIndex);
   8.286 +                
   8.287 +                trackNumber   = (CFNumberRef)  CFDictionaryGetValue (theTrackDict, CFSTR(kPointKeyString));
   8.288 +                sessionNumber = (CFNumberRef)  CFDictionaryGetValue (theTrackDict, CFSTR(kSessionNumberKeyString));
   8.289 +                startBlock    = (CFNumberRef)  CFDictionaryGetValue (theTrackDict, CFSTR(kStartBlockKeyString));
   8.290 +                isDataTrack   = (CFBooleanRef) CFDictionaryGetValue (theTrackDict, CFSTR(kDataKeyString));
   8.291 +                                                        
   8.292 +                // Fill in the SDL_CD struct
   8.293 +                int idx = theCD->numtracks++;
   8.294 +
   8.295 +                CFNumberGetValue (trackNumber, kCFNumberSInt32Type, &value);
   8.296 +                theCD->track[idx].id = value;
   8.297 +                
   8.298 +                CFNumberGetValue (startBlock, kCFNumberSInt32Type, &value);
   8.299 +                theCD->track[idx].offset = value;
   8.300 +
   8.301 +                theCD->track[idx].type = (isDataTrack == kCFBooleanTrue) ? SDL_DATA_TRACK : SDL_AUDIO_TRACK;
   8.302 +
   8.303 +                // Since the track lengths are not stored in .TOC.plist we compute them.
   8.304 +                if (trackIndex > 0) {
   8.305 +                    theCD->track[idx-1].length = theCD->track[idx].offset - theCD->track[idx-1].offset;
   8.306 +                }
   8.307 +            }
   8.308 +            
   8.309 +            // Compute the length of the last track
   8.310 +            CFNumberGetValue (leadoutBlock, kCFNumberSInt32Type, &value);
   8.311 +            
   8.312 +            theCD->track[theCD->numtracks-1].length = 
   8.313 +                value - theCD->track[theCD->numtracks-1].offset;
   8.314 +
   8.315 +            // Set offset to leadout track
   8.316 +            theCD->track[theCD->numtracks].offset = value;
   8.317 +        }
   8.318 +    
   8.319 +    }
   8.320 +
   8.321 +    theErr = 0;
   8.322 +    goto cleanup;
   8.323 +bail:
   8.324 +    SDL_SetError ("ReadTOCData: %s returned %d", error, theErr);
   8.325 +    theErr = -1;
   8.326 +cleanup:
   8.327 +
   8.328 +    if (propertyListRef != NULL)
   8.329 +        CFRelease(propertyListRef);
   8.330 +    if (dataRef != NULL)
   8.331 +        CFRelease(dataRef);
   8.332 +    if (forkData != NULL)
   8.333 +        DisposePtr(forkData);
   8.334 +        
   8.335 +    FSCloseFork (forkRefNum);
   8.336 +
   8.337 +    return theErr;
   8.338 +}
   8.339 +
   8.340 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   8.341 +//  ListTrackFiles
   8.342 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   8.343 +
   8.344 +int ListTrackFiles (FSVolumeRefNum theVolume, FSRef *trackFiles, int numTracks)
   8.345 +{
   8.346 +    OSStatus        result = -1;
   8.347 +    FSIterator      iterator;
   8.348 +    ItemCount       actualObjects;
   8.349 +    FSRef           rootDirectory;
   8.350 +    FSRef           ref;
   8.351 +    HFSUniStr255    nameStr;
   8.352 +    
   8.353 +    result = FSGetVolumeInfo (theVolume,
   8.354 +                              0,
   8.355 +                              NULL,
   8.356 +                              kFSVolInfoFSInfo,
   8.357 +                              NULL,
   8.358 +                              NULL,
   8.359 +                              &rootDirectory); 
   8.360 +                                 
   8.361 +    if (result != noErr) {
   8.362 +        SDL_SetError ("ListTrackFiles: FSGetVolumeInfo returned %d", result);
   8.363 +        goto bail;
   8.364 +    }
   8.365 +
   8.366 +    result = FSOpenIterator (&rootDirectory, kFSIterateFlat, &iterator);
   8.367 +    if (result == noErr) {
   8.368 +        do
   8.369 +        {
   8.370 +            result = FSGetCatalogInfoBulk (iterator, 1, &actualObjects,
   8.371 +                                           NULL, kFSCatInfoNone, NULL, &ref, NULL, &nameStr);
   8.372 +            if (result == noErr) {
   8.373 +                
   8.374 +                CFStringRef  name;
   8.375 +                name = CFStringCreateWithCharacters (NULL, nameStr.unicode, nameStr.length);
   8.376 +                
   8.377 +                // Look for .aiff extension
   8.378 +                if (CFStringHasSuffix (name, CFSTR(".aiff"))) {
   8.379 +                    
   8.380 +                    // Extract the track id from the filename
   8.381 +                    int trackID = 0, i = 0;
   8.382 +                    while (nameStr.unicode[i] >= '0' && nameStr.unicode[i] <= '9') {
   8.383 +                        trackID = 10 * trackID +(nameStr.unicode[i] - '0');
   8.384 +                        i++;
   8.385 +                    }
   8.386 +                    
   8.387 +                    #if DEBUG_CDROM
   8.388 +                    printf("Found AIFF for track %d: '%s'\n", trackID, 
   8.389 +                    CFStringGetCStringPtr (name, CFStringGetSystemEncoding()));
   8.390 +                    #endif
   8.391 +                    
   8.392 +                    // Track ID's start at 1, but we want to start at 0
   8.393 +                    trackID--;
   8.394 +                    
   8.395 +                    assert(0 <= trackID && trackID <= SDL_MAX_TRACKS);
   8.396 +                    
   8.397 +                    if (trackID < numTracks)
   8.398 +                        memcpy (&trackFiles[trackID], &ref, sizeof(FSRef));
   8.399 +                }
   8.400 +                CFRelease (name);
   8.401 +            }
   8.402 +        } while(noErr == result);
   8.403 +        FSCloseIterator (iterator);
   8.404 +    }
   8.405 +    
   8.406 +    result = 0;
   8.407 +  bail:   
   8.408 +    return result;
   8.409 +}
   8.410 +
   8.411 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   8.412 +//  LoadFile
   8.413 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   8.414 +
   8.415 +int LoadFile (const FSRef *ref, int startFrame, int stopFrame)
   8.416 +{
   8.417 +    int error = -1;
   8.418 +    
   8.419 +    if (CheckInit () < 0)
   8.420 +        goto bail;
   8.421 +    
   8.422 +    // release any currently playing file
   8.423 +    if (ReleaseFile () < 0)
   8.424 +        goto bail;
   8.425 +    
   8.426 +    #if DEBUG_CDROM
   8.427 +    printf ("LoadFile: %d %d\n", startFrame, stopFrame);
   8.428 +    #endif
   8.429 +    
   8.430 +    try {
   8.431 +    
   8.432 +        // create a new player, and attach to the audio unit
   8.433 +        
   8.434 +        thePlayer = new AudioFilePlayer(ref);
   8.435 +        if (thePlayer == NULL) {
   8.436 +            SDL_SetError ("LoadFile: Could not create player");
   8.437 +            throw (-3);
   8.438 +        }
   8.439 +            
   8.440 +        thePlayer->SetDestination(theUnit, 0);
   8.441 +        
   8.442 +        if (startFrame >= 0)
   8.443 +            thePlayer->SetStartFrame (startFrame);
   8.444 +        
   8.445 +        if (stopFrame >= 0 && stopFrame > startFrame)
   8.446 +            thePlayer->SetStopFrame (stopFrame);
   8.447 +        
   8.448 +        // we set the notifier later
   8.449 +        //thePlayer->SetNotifier(FilePlayNotificationHandler, NULL);
   8.450 +            
   8.451 +        thePlayer->Connect();
   8.452 +    
   8.453 +        #if DEBUG_CDROM
   8.454 +        thePlayer->Print();
   8.455 +        fflush (stdout);
   8.456 +        #endif
   8.457 +    }
   8.458 +    catch (...)
   8.459 +    {
   8.460 +        goto bail;
   8.461 +    }
   8.462 +        
   8.463 +    error = 0;
   8.464 +
   8.465 +    bail:
   8.466 +    return error;
   8.467 +}
   8.468 +
   8.469 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   8.470 +//  ReleaseFile
   8.471 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   8.472 +
   8.473 +int ReleaseFile ()
   8.474 +{
   8.475 +    int error = -1;
   8.476 +        
   8.477 +    try {
   8.478 +        if (thePlayer != NULL) {
   8.479 +            
   8.480 +            thePlayer->Disconnect();
   8.481 +            
   8.482 +            delete thePlayer;
   8.483 +            
   8.484 +            thePlayer = NULL;
   8.485 +        }
   8.486 +    }
   8.487 +    catch (...)
   8.488 +    {
   8.489 +        goto bail;
   8.490 +    }
   8.491 +    
   8.492 +    error = 0;
   8.493 +    
   8.494 +  bail:
   8.495 +    return error;
   8.496 +}
   8.497 +
   8.498 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   8.499 +//  PlayFile
   8.500 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   8.501 +
   8.502 +int PlayFile ()
   8.503 +{
   8.504 +    OSStatus result = -1;
   8.505 +    
   8.506 +    if (CheckInit () < 0)
   8.507 +        goto bail;
   8.508 +        
   8.509 +    try {
   8.510 +    
   8.511 +        // start processing of the audio unit
   8.512 +        result = AudioOutputUnitStart (theUnit);
   8.513 +            THROW_RESULT("PlayFile: AudioOutputUnitStart")    
   8.514 +        
   8.515 +    }
   8.516 +    catch (...)
   8.517 +    {
   8.518 +        goto bail;
   8.519 +    }
   8.520 +    
   8.521 +    result = 0;
   8.522 +    
   8.523 +bail:
   8.524 +    return result;
   8.525 +}
   8.526 +
   8.527 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   8.528 +//  PauseFile
   8.529 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   8.530 +
   8.531 +int PauseFile ()
   8.532 +{
   8.533 +    OSStatus result = -1;
   8.534 +    
   8.535 +    if (CheckInit () < 0)
   8.536 +        goto bail;
   8.537 +            
   8.538 +    try {
   8.539 +    
   8.540 +        // stop processing the audio unit
   8.541 +        result = AudioOutputUnitStop (theUnit);
   8.542 +            THROW_RESULT("PauseFile: AudioOutputUnitStop")
   8.543 +    }
   8.544 +    catch (...)
   8.545 +    {
   8.546 +        goto bail;
   8.547 +    }
   8.548 +    
   8.549 +    result = 0;
   8.550 +bail:
   8.551 +    return result;
   8.552 +}
   8.553 +
   8.554 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   8.555 +//  SetCompletionProc
   8.556 +//ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   8.557 +
   8.558 +void SetCompletionProc (CDPlayerCompletionProc proc, SDL_CD *cdrom)
   8.559 +{
   8.560 +    assert(thePlayer != NULL);
   8.561 +
   8.562 +    theCDROM = cdrom;
   8.563 +    completionProc = proc;
   8.564 +    thePlayer->SetNotifier (FilePlayNotificationHandler, cdrom);
   8.565 +}
   8.566 +
   8.567 +
   8.568 +int GetCurrentFrame ()
   8.569 +{    
   8.570 +    int frame;
   8.571 +    
   8.572 +    if (thePlayer == NULL)
   8.573 +        frame = 0;
   8.574 +    else
   8.575 +        frame = thePlayer->GetCurrentFrame ();
   8.576 +        
   8.577 +    return frame; 
   8.578 +}
   8.579 +
   8.580 +
   8.581 +#pragma mark -- Private Functions --
   8.582 +
   8.583 +OSStatus CheckInit ()
   8.584 +{    
   8.585 +    if (playBackWasInit)
   8.586 +        return 0;
   8.587 +    
   8.588 +    OSStatus result = noErr;
   8.589 +    
   8.590 +        
   8.591 +    // Create the callback mutex
   8.592 +    pthread_mutexattr_t attr;
   8.593 +    pthread_mutexattr_init (&attr);
   8.594 +    pthread_mutex_init (&callbackMutex, &attr);
   8.595 +    pthread_mutexattr_destroy (&attr);
   8.596 +    pthread_mutex_lock (&callbackMutex);
   8.597 +        
   8.598 +    // Start callback thread
   8.599 +    pthread_attr_t attr1;
   8.600 +    pthread_attr_init (&attr1);        
   8.601 +    pthread_create (&callbackThread, &attr1, RunCallBackThread, NULL);
   8.602 +    pthread_attr_destroy (&attr1);
   8.603 +
   8.604 +    try {
   8.605 +        ComponentDescription desc;
   8.606 +    
   8.607 +        desc.componentType = kAudioUnitComponentType;
   8.608 +        desc.componentSubType = kAudioUnitSubType_Output;
   8.609 +        desc.componentManufacturer = kAudioUnitID_DefaultOutput;
   8.610 +        desc.componentFlags = 0;
   8.611 +        desc.componentFlagsMask = 0;
   8.612 +        
   8.613 +        Component comp = FindNextComponent (NULL, &desc);
   8.614 +        if (comp == NULL) {
   8.615 +            SDL_SetError ("CheckInit: FindNextComponent returned NULL");
   8.616 +            throw(internalComponentErr);
   8.617 +        }
   8.618 +        
   8.619 +        result = OpenAComponent (comp, &theUnit);
   8.620 +            THROW_RESULT("CheckInit: OpenAComponent")
   8.621 +                    
   8.622 +        // you need to initialize the output unit before you set it as a destination
   8.623 +        result = AudioUnitInitialize (theUnit);
   8.624 +            THROW_RESULT("CheckInit: AudioUnitInitialize")
   8.625 +        
   8.626 +                    
   8.627 +        // In this case we first want to get the output format of the OutputUnit
   8.628 +        // Then we set that as the input format. Why?
   8.629 +        // So that only a single conversion process is done
   8.630 +        // when SetDestination is called it will get the input format of the
   8.631 +        // unit its supplying data to. This defaults to 44.1K, stereo, so if
   8.632 +        // the device is not that, then we lose a possibly rendering of data
   8.633 +        
   8.634 +        result = MatchAUFormats (theUnit, 0);
   8.635 +            THROW_RESULT("CheckInit: MatchAUFormats")
   8.636 +    
   8.637 +        playBackWasInit = true;
   8.638 +    }
   8.639 +    catch (...)
   8.640 +    {
   8.641 +        return -1;
   8.642 +    }
   8.643 +    
   8.644 +    return 0;
   8.645 +}
   8.646 +
   8.647 +
   8.648 +OSStatus MatchAUFormats (AudioUnit theUnit, UInt32 theInputBus)
   8.649 +{
   8.650 +    AudioStreamBasicDescription theDesc;
   8.651 +    UInt32 size = sizeof (theDesc);
   8.652 +    OSStatus result = AudioUnitGetProperty (theUnit,
   8.653 +                                            kAudioUnitProperty_StreamFormat,
   8.654 +                                            kAudioUnitScope_Output,
   8.655 +                                            0,
   8.656 +                                            &theDesc,
   8.657 +                                            &size);
   8.658 +        THROW_RESULT("MatchAUFormats: AudioUnitGetProperty")
   8.659 +
   8.660 +    result = AudioUnitSetProperty (theUnit,
   8.661 +                                   kAudioUnitProperty_StreamFormat,
   8.662 +                                   kAudioUnitScope_Input,
   8.663 +                                   theInputBus,
   8.664 +                                   &theDesc,
   8.665 +                                   size);
   8.666 +    
   8.667 +    return result;
   8.668 +}
   8.669 +
   8.670 +void FilePlayNotificationHandler(void * inRefCon, OSStatus inStatus)
   8.671 +{
   8.672 +    if (inStatus == kAudioFilePlay_FileIsFinished) {
   8.673 +    
   8.674 +        // notify non-CA thread to perform the callback
   8.675 +        pthread_mutex_unlock (&callbackMutex);
   8.676 +        
   8.677 +    } else if (inStatus == kAudioFilePlayErr_FilePlayUnderrun) {
   8.678 +    
   8.679 +        SDL_SetError ("CDPlayer Notification: buffer underrun");
   8.680 +    } else if (inStatus == kAudioFilePlay_PlayerIsUninitialized) {
   8.681 +    
   8.682 +        SDL_SetError ("CDPlayer Notification: player is uninitialized");
   8.683 +    } else {
   8.684 +        
   8.685 +        SDL_SetError ("CDPlayer Notification: unknown error %ld", inStatus);
   8.686 +    }
   8.687 +}
   8.688 +
   8.689 +void* RunCallBackThread (void *param)
   8.690 +{
   8.691 +    runCallBackThread = 1;
   8.692 +    
   8.693 +    while (runCallBackThread) {
   8.694 +    
   8.695 +        pthread_mutex_lock (&callbackMutex);
   8.696 +
   8.697 +        if (completionProc && theCDROM) {
   8.698 +            #if DEBUG_CDROM
   8.699 +            printf ("callback!\n");
   8.700 +            #endif
   8.701 +            (*completionProc)(theCDROM);
   8.702 +        } else {
   8.703 +            #if DEBUG_CDROM
   8.704 +            printf ("callback?\n");
   8.705 +            #endif
   8.706 +        }
   8.707 +    }
   8.708 +    
   8.709 +    runCallBackThread = -1;
   8.710 +    
   8.711 +    #if DEBUG_CDROM
   8.712 +    printf ("thread dying now...\n");
   8.713 +    #endif
   8.714 +    
   8.715 +    return NULL;
   8.716 +}
   8.717 +
   8.718 +}; // extern "C"
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/src/cdrom/macosx/CDPlayer.h	Tue Apr 15 16:33:56 2003 +0000
     9.3 @@ -0,0 +1,69 @@
     9.4 +/*
     9.5 +    SDL - Simple DirectMedia Layer
     9.6 +    Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  Sam Lantinga
     9.7 +
     9.8 +    This library is free software; you can redistribute it and/or
     9.9 +    modify it under the terms of the GNU Library General Public
    9.10 +    License as published by the Free Software Foundation; either
    9.11 +    version 2 of the License, or (at your option) any later version.
    9.12 +
    9.13 +    This library is distributed in the hope that it will be useful,
    9.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
    9.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    9.16 +    Library General Public License for more details.
    9.17 +
    9.18 +    You should have received a copy of the GNU Library General Public
    9.19 +    License along with this library; if not, write to the Free
    9.20 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    9.21 +
    9.22 +    Sam Lantinga
    9.23 +    slouken@libsdl.org
    9.24 +*/
    9.25 +
    9.26 +#ifndef __CDPlayer__H__
    9.27 +#define __CDPlayer__H__ 1
    9.28 +
    9.29 +#include <CoreFoundation/CoreFoundation.h>
    9.30 +#include <Carbon/Carbon.h>
    9.31 +
    9.32 +#include <AudioUnit/AudioUnit.h>
    9.33 +#include <AudioToolbox/AudioToolbox.h>
    9.34 +
    9.35 +#include <SDL.h>
    9.36 +
    9.37 +#include <string.h>
    9.38 +#include <unistd.h> // for usleep
    9.39 +
    9.40 +#ifdef __cplusplus
    9.41 +extern "C" {
    9.42 +#endif
    9.43 +
    9.44 +typedef void (*CDPlayerCompletionProc)(SDL_CD *cdrom) ;
    9.45 +
    9.46 +void     Lock ();
    9.47 +
    9.48 +void     Unlock();
    9.49 +
    9.50 +int      LoadFile (const FSRef *ref, int startFrame, int endFrame); // pass -1 to do nothing
    9.51 +
    9.52 +int      ReleaseFile ();
    9.53 +
    9.54 +int      PlayFile  ();
    9.55 +
    9.56 +int      PauseFile ();
    9.57 +
    9.58 +void     SetCompletionProc (CDPlayerCompletionProc proc, SDL_CD *cdrom);
    9.59 +
    9.60 +int      ReadTOCData (FSVolumeRefNum theVolume, SDL_CD *theCD);
    9.61 +
    9.62 +int      ListTrackFiles (FSVolumeRefNum theVolume, FSRef *trackFiles, int numTracks);
    9.63 +
    9.64 +int      DetectAudioCDVolumes (FSVolumeRefNum *volumes, int numVolumes);
    9.65 +
    9.66 +int      GetCurrentFrame ();
    9.67 +
    9.68 +#ifdef __cplusplus
    9.69 +};
    9.70 +#endif
    9.71 +
    9.72 +#endif /* __CD_Player__H__ */
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/src/cdrom/macosx/Makefile.am	Tue Apr 15 16:33:56 2003 +0000
    10.3 @@ -0,0 +1,13 @@
    10.4 +
    10.5 +## Makefile.am for the Mac OS X cdrom driver for SDL
    10.6 +
    10.7 +noinst_LTLIBRARIES = libcdrom_macosx.la
    10.8 +libcdrom_macosx_la_SOURCES = $(SRCS)
    10.9 +
   10.10 +# The SDL cdrom driver sources
   10.11 +SRCS = \
   10.12 +	SDL_syscdrom.c \
   10.13 +	AudioFilePlayer.cpp \
   10.14 +	AudioFileReaderThread.cpp \
   10.15 +	CAGuard.cpp \
   10.16 +	CDPlayer.cpp
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/src/cdrom/macosx/SDL_syscdrom_c.h	Tue Apr 15 16:33:56 2003 +0000
    11.3 @@ -0,0 +1,132 @@
    11.4 +/*
    11.5 +    SDL - Simple DirectMedia Layer
    11.6 +    Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  Sam Lantinga
    11.7 +
    11.8 +    This library is free software; you can redistribute it and/or
    11.9 +    modify it under the terms of the GNU Library General Public
   11.10 +    License as published by the Free Software Foundation; either
   11.11 +    version 2 of the License, or (at your option) any later version.
   11.12 +
   11.13 +    This library is distributed in the hope that it will be useful,
   11.14 +    but WITHOUT ANY WARRANTY; without even the implied warranty of
   11.15 +    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   11.16 +    Library General Public License for more details.
   11.17 +
   11.18 +    You should have received a copy of the GNU Library General Public
   11.19 +    License along with this library; if not, write to the Free
   11.20 +    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   11.21 +
   11.22 +    Sam Lantinga
   11.23 +    slouken@libsdl.org
   11.24 +*/
   11.25 +
   11.26 +
   11.27 +/***********************************************************************************
   11.28 + Implementation Notes
   11.29 + *********************
   11.30 +
   11.31 +    This code has several limitations currently (all of which are proabaly fixable):
   11.32 +    
   11.33 +    1. A CD-ROM device is inferred from a mounted cdfs volume, so device 0 is
   11.34 +       not necessarily the first CD-ROM device on the system. (Somewhat easy to fix
   11.35 +       by useing the device name from the volume id's to reorder the volumes)
   11.36 +       
   11.37 +    2. You can only open and control 1 CD-ROM device at a time. (Challenging to fix,
   11.38 +       due to extensive code restructuring)
   11.39 +    
   11.40 +    3. The status reported by SDL_CDStatus only changes to from CD_PLAYING to CD_STOPPED in
   11.41 +       1-second intervals (because the audio is buffered in 1-second chunks) If
   11.42 +       the audio data is less than 1 second, the remainder is filled with silence.
   11.43 +       
   11.44 +       If you need to play sequences back-to-back that are less that 1 second long,
   11.45 +       use the frame position to determine when to play the next sequence, instead
   11.46 +       of SDL_CDStatus.
   11.47 +       
   11.48 +       This may be possible to fix with a clever usage of the AudioUnit API.
   11.49 +       
   11.50 +    4. When new volumes are inserted, our volume information is not updated. The only way
   11.51 +       to refresh this information is to reinit the CD-ROM subsystem of SDL. To fix this,
   11.52 +       one would probably have to fix point 1 above first, then figure out how to register
   11.53 +       for a notification when new media is mounted in order to perform an automatic
   11.54 +       rescan for cdfs volumes.
   11.55 +    
   11.56 +    
   11.57 +    
   11.58 +    So, here comes a description of how this all works.
   11.59 +    
   11.60 +        < Initializing >
   11.61 +        
   11.62 +        To get things rolling, we have to locate mounted volumes that contain
   11.63 +        audio (since nearly all Macs don't have analog audio-in on the sound card).
   11.64 +        That's easy, since these volumes have a flag that indicates this special
   11.65 +        filesystem. See DetectAudioCDVolumes() in CDPlayer.cpp for this code.
   11.66 +        
   11.67 +        Next, we parse the invisible .TOC.plist in the root of the volume, which gets us
   11.68 +        the track information (number, offset, length, leadout, etc). See ReadTOCData() in
   11.69 +        CDPlayer.cpp for the skinny on this.
   11.70 +        
   11.71 +        
   11.72 +        < The Playback Loop >
   11.73 +        
   11.74 +        Now come the tricky parts. Let's start with basic audio playback. When a frame
   11.75 +        range to play is requested, we must first find the .aiff files on the volume, 
   11.76 +        hopefully in the right order. Since these files all begin with a number "1 Audio Track", 
   11.77 +        etc, this is used to determine the correct track order.
   11.78 +        
   11.79 +        Once all files are determined, we have to find what file corresponds to the start
   11.80 +        and length parameter to SDL_SYS_CDPlay(). Again, this is quite simple by walking the
   11.81 +        cdrom's track list. At this point, we also save the offset to the next track and frames
   11.82 +        remaining, if we're going to have to play another file after the first one. See
   11.83 +        GetFileForOffset() for this code.
   11.84 +        
   11.85 +        At this point we have all info needed to start playback, so we hand off to the LoadFile()
   11.86 +        function, which proceeds to do its magic and plays back the file.
   11.87 +        
   11.88 +        When the file is finished playing, CompletionProc() is invoked, at which time we can
   11.89 +        play the next file if the previously saved next track and frames remaining
   11.90 +        indicates that we should. 
   11.91 +        
   11.92 +        
   11.93 +        < Magic >
   11.94 +        
   11.95 +        OK, so it's not really magic, but since I don't fully understand all the hidden details it
   11.96 +        seems like it to me ;-) The API's involved are the AudioUnit and AudioFile API's. These
   11.97 +        appear to be an extension of CoreAudio for creating modular playback and f/x entities.
   11.98 +        The important thing is that CPU usage is very low and reliability is very high. You'd
   11.99 +        be hard-pressed to find a way to stutter the playback with other CPU-intensive tasks.
  11.100 +    
  11.101 +        One part of this magic is that it uses multiple threads, which carries the usual potential
  11.102 +        for disaster if not handled carefully. Playback currently requires 4 additional threads:
  11.103 +            1. The coreaudio runloop thread
  11.104 +            2. The coreaudio device i/o thread
  11.105 +            3. The file streaming thread
  11.106 +            4. The notification/callback thread
  11.107 +        
  11.108 +        The first 2 threads are necessary evil - CoreAudio creates this no matter what the situation
  11.109 +        is (even the SDL sound implementation creates theses suckers). The last two are are created
  11.110 +        by us.
  11.111 +        
  11.112 +        The file is streamed from disk using a threaded double-buffer approach. 
  11.113 +        This way, the high latency operation of reading from disk can be performed without interrupting
  11.114 +        the real-time device thread (which amounts to avoiding dropouts). The device thread grabs the
  11.115 +        buffer that isn't being read and sends it to the CoreAudio mixer where it eventually gets 
  11.116 +        to the sound card.
  11.117 +        
  11.118 +        The device thread posts a notification when the file streaming thread is out of data. This
  11.119 +        notification must be handled in a separate thread to avoid potential deadlock in the
  11.120 +        device thread. That's where the notification thread comes in. This thread is signaled
  11.121 +        whenever a notification needs to be processed, so another file can be played back if need be.
  11.122 +        
  11.123 +        The API in CDPlayer.cpp contains synchronization because otherwise both the notification thread
  11.124 +        and main thread (or another other thread using the SDL CD api) can potentially call it at the same time.
  11.125 +    
  11.126 +************************************************************************************/
  11.127 +
  11.128 +
  11.129 +#include "SDL_cdrom.h"
  11.130 +#include "SDL_syscdrom.h"
  11.131 +
  11.132 +#include "CDPlayer.h"
  11.133 +
  11.134 +#define kErrorFakeDevice "Error: Cannot proceed since we're faking a CD-ROM device. Reinit the CD-ROM subsystem to scan for new volumes."
  11.135 +
    12.1 --- a/test/testcdrom.c	Tue Apr 15 16:04:31 2003 +0000
    12.2 +++ b/test/testcdrom.c	Tue Apr 15 16:33:56 2003 +0000
    12.3 @@ -64,8 +64,8 @@
    12.4  			trtype="unknown";
    12.5  			break;
    12.6  		}
    12.7 -		printf("\tTrack (index %d) %d: %d:%2.2d [%s track]\n", i,
    12.8 -					cdrom->track[i].id, m, s, trtype);
    12.9 +		printf("\tTrack (index %d) %d: %d:%2.2d / %d [%s track]\n", i,
   12.10 +					cdrom->track[i].id, m, s, cdrom->track[i].length, trtype);
   12.11  	}
   12.12  }
   12.13