src/cdrom/macosx/CDPlayer.cpp
author Sam Lantinga
Sun, 04 Jan 2004 18:50:26 +0000
changeset 771 336603031bab
parent 768 de1b2c3063b9
child 775 08b7fc2b5225
permissions -rw-r--r--
Fixed track detection on MacOS X 10.1
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 #include "CDPlayer.h"
    24 #include "AudioFilePlayer.h"
    25 #include "CAGuard.h"
    26 
    27 // we're exporting these functions into C land for SDL_syscdrom.c
    28 extern "C" {
    29 
    30 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
    31 //  Constants
    32 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
    33 
    34 #define kAudioCDFilesystemID   (UInt16)(('J' << 8) | 'H') // 'JH'; this avoids compiler warning
    35 
    36 // XML PList keys
    37 #define kRawTOCDataString           "Format 0x02 TOC Data"
    38 #define kSessionsString             "Sessions"
    39 #define kSessionTypeString          "Session Type"
    40 #define kTrackArrayString           "Track Array"
    41 #define kFirstTrackInSessionString      "First Track"
    42 #define kLastTrackInSessionString       "Last Track"
    43 #define kLeadoutBlockString         "Leadout Block"
    44 #define kDataKeyString              "Data"
    45 #define kPointKeyString             "Point"
    46 #define kSessionNumberKeyString         "Session Number"
    47 #define kStartBlockKeyString            "Start Block"   
    48     
    49 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
    50 //  Globals
    51 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
    52 
    53 #pragma mark -- Globals --
    54 
    55 static bool             playBackWasInit = false;
    56 static AudioUnit        theUnit;
    57 static AudioFilePlayer* thePlayer = NULL;
    58 static CDPlayerCompletionProc   completionProc = NULL;
    59 static pthread_mutex_t  apiMutex;
    60 static pthread_t        callbackThread;
    61 static pthread_mutex_t  callbackMutex;
    62 static volatile  int    runCallBackThread;
    63 static int              initMutex = SDL_TRUE;
    64 static SDL_CD*          theCDROM;
    65 
    66 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
    67 //  Prototypes
    68 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
    69 
    70 #pragma mark -- Prototypes --
    71 
    72 OSStatus CheckInit ();
    73 
    74 OSStatus MatchAUFormats (AudioUnit theUnit, UInt32 theInputBus);
    75 
    76 void     FilePlayNotificationHandler (void* inRefCon, OSStatus inStatus);
    77 
    78 void*    RunCallBackThread (void* inRefCon);
    79 
    80 
    81 #pragma mark -- Public Functions --
    82 
    83 void     Lock ()
    84 {
    85     if (initMutex) {
    86     
    87         pthread_mutexattr_t attr;
    88         
    89         pthread_mutexattr_init (&attr);
    90         pthread_mutex_init (&apiMutex, &attr);
    91         pthread_mutexattr_destroy (&attr);
    92         
    93         initMutex = SDL_FALSE;
    94     }
    95     
    96     pthread_mutex_lock (&apiMutex);
    97 }
    98 
    99 void     Unlock ()
   100 {
   101     pthread_mutex_unlock (&apiMutex);
   102 }
   103 
   104 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   105 //  DetectAudioCDVolumes
   106 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   107 
   108 int DetectAudioCDVolumes(FSVolumeRefNum *volumes, int numVolumes)
   109 {
   110     int volumeIndex;
   111     int cdVolumeCount = 0;
   112     OSStatus result = noErr;
   113     
   114     for (volumeIndex = 1; result == noErr || result != nsvErr; volumeIndex++)
   115     {
   116         FSVolumeRefNum  actualVolume;
   117         FSVolumeInfo    volumeInfo;
   118         
   119         memset (&volumeInfo, 0, sizeof(volumeInfo));
   120         
   121         result = FSGetVolumeInfo (kFSInvalidVolumeRefNum,
   122                                   volumeIndex,
   123                                   &actualVolume,
   124                                   kFSVolInfoFSInfo,
   125                                   &volumeInfo,
   126                                   NULL,
   127                                   NULL); 
   128          
   129         if (result == noErr)
   130         {
   131             if (volumeInfo.filesystemID == kAudioCDFilesystemID) // It's an audio CD
   132             {
   133                 if (volumes != NULL && cdVolumeCount < numVolumes)
   134                     volumes[cdVolumeCount] = actualVolume;
   135             
   136                 cdVolumeCount++;
   137             }
   138         }
   139         else 
   140         {
   141             // I'm commenting this out because it seems to be harmless
   142             //SDL_SetError ("DetectAudioCDVolumes: FSGetVolumeInfo returned %d", result);
   143         }
   144     }
   145         
   146     return cdVolumeCount;
   147 }
   148 
   149 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   150 //  ReadTOCData
   151 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   152 
   153 int ReadTOCData (FSVolumeRefNum theVolume, SDL_CD *theCD)
   154 {
   155     HFSUniStr255      dataForkName;
   156     OSStatus          theErr;
   157     SInt16            forkRefNum;
   158     SInt64            forkSize;
   159     Ptr               forkData = 0;
   160     ByteCount         actualRead;
   161     CFDataRef         dataRef = 0;
   162     CFPropertyListRef propertyListRef = 0;
   163 
   164     FSRefParam      fsRefPB;
   165     FSRef           tocPlistFSRef;
   166     
   167     const char* error = "Unspecified Error";
   168     
   169     // get stuff from .TOC.plist                                                   
   170     fsRefPB.ioCompletion = NULL;
   171     fsRefPB.ioNamePtr = "\p.TOC.plist";
   172     fsRefPB.ioVRefNum = theVolume;
   173     fsRefPB.ioDirID = 0;
   174     fsRefPB.newRef = &tocPlistFSRef;
   175     
   176     theErr = PBMakeFSRefSync (&fsRefPB);
   177     if(theErr != noErr) {
   178         error = "PBMakeFSRefSync";
   179         goto bail;
   180     }
   181     
   182     // Load and parse the TOC XML data
   183 
   184     theErr = FSGetDataForkName (&dataForkName);
   185     if (theErr != noErr) {
   186         error = "FSGetDataForkName";
   187         goto bail;
   188     }
   189     
   190     theErr = FSOpenFork (&tocPlistFSRef, dataForkName.length, dataForkName.unicode, fsRdPerm, &forkRefNum);
   191     if (theErr != noErr) {
   192         error = "FSOpenFork";
   193         goto bail;
   194     }
   195     
   196     theErr = FSGetForkSize (forkRefNum, &forkSize);
   197     if (theErr != noErr) {
   198         error = "FSGetForkSize";
   199         goto bail;
   200     }
   201     
   202     // Allocate some memory for the XML data
   203     forkData = NewPtr (forkSize);
   204     if(forkData == NULL) {
   205         error = "NewPtr";
   206         goto bail;
   207     }
   208     
   209     theErr = FSReadFork (forkRefNum, fsFromStart, 0 /* offset location */, forkSize, forkData, &actualRead);
   210     if(theErr != noErr) {
   211         error = "FSReadFork";
   212         goto bail;
   213     }
   214     
   215     dataRef = CFDataCreate (kCFAllocatorDefault, (UInt8 *)forkData, forkSize);
   216     if(dataRef == 0) {
   217         error = "CFDataCreate";
   218         goto bail;
   219     }
   220 
   221     propertyListRef = CFPropertyListCreateFromXMLData (kCFAllocatorDefault,
   222                                                        dataRef,
   223                                                        kCFPropertyListImmutable,
   224                                                        NULL);
   225     if (propertyListRef == NULL) {
   226         error = "CFPropertyListCreateFromXMLData";
   227         goto bail;
   228     }
   229 
   230     // Now we got the Property List in memory. Parse it.
   231     
   232     // First, make sure the root item is a CFDictionary. If not, release and bail.
   233     if(CFGetTypeID(propertyListRef)== CFDictionaryGetTypeID())
   234     {
   235         CFDictionaryRef dictRef = (CFDictionaryRef)propertyListRef;
   236         
   237         CFDataRef   theRawTOCDataRef;
   238         CFArrayRef  theSessionArrayRef;
   239         CFIndex     numSessions;
   240         CFIndex     index;
   241         
   242         // This is how we get the Raw TOC Data
   243         theRawTOCDataRef = (CFDataRef)CFDictionaryGetValue (dictRef, CFSTR(kRawTOCDataString));
   244         
   245         // Get the session array info.
   246         theSessionArrayRef = (CFArrayRef)CFDictionaryGetValue (dictRef, CFSTR(kSessionsString));
   247         
   248         // Find out how many sessions there are.
   249         numSessions = CFArrayGetCount (theSessionArrayRef);
   250         
   251         // Initialize the total number of tracks to 0
   252         theCD->numtracks = 0;
   253         
   254         // Iterate over all sessions, collecting the track data
   255         for(index = 0; index < numSessions; index++)
   256         {
   257             CFDictionaryRef theSessionDict;
   258             CFNumberRef     leadoutBlock;
   259             CFArrayRef      trackArray;
   260             CFIndex         numTracks;
   261             CFIndex         trackIndex;
   262             UInt32          value = 0;
   263             
   264             theSessionDict      = (CFDictionaryRef) CFArrayGetValueAtIndex (theSessionArrayRef, index);
   265             leadoutBlock        = (CFNumberRef) CFDictionaryGetValue (theSessionDict, CFSTR(kLeadoutBlockString));
   266             
   267             trackArray = (CFArrayRef)CFDictionaryGetValue (theSessionDict, CFSTR(kTrackArrayString));
   268             
   269             numTracks = CFArrayGetCount (trackArray);
   270 
   271             for(trackIndex = 0; trackIndex < numTracks; trackIndex++) {
   272                     
   273                 CFDictionaryRef theTrackDict;
   274                 CFNumberRef     trackNumber;
   275                 CFNumberRef     sessionNumber;
   276                 CFNumberRef     startBlock;
   277                 CFBooleanRef    isDataTrack;
   278                 UInt32          value;
   279                 
   280                 theTrackDict  = (CFDictionaryRef) CFArrayGetValueAtIndex (trackArray, trackIndex);
   281                 
   282                 trackNumber   = (CFNumberRef)  CFDictionaryGetValue (theTrackDict, CFSTR(kPointKeyString));
   283                 sessionNumber = (CFNumberRef)  CFDictionaryGetValue (theTrackDict, CFSTR(kSessionNumberKeyString));
   284                 startBlock    = (CFNumberRef)  CFDictionaryGetValue (theTrackDict, CFSTR(kStartBlockKeyString));
   285                 isDataTrack   = (CFBooleanRef) CFDictionaryGetValue (theTrackDict, CFSTR(kDataKeyString));
   286                                                         
   287                 // Fill in the SDL_CD struct
   288                 int idx = theCD->numtracks++;
   289 
   290                 CFNumberGetValue (trackNumber, kCFNumberSInt32Type, &value);
   291                 theCD->track[idx].id = value;
   292                 
   293                 CFNumberGetValue (startBlock, kCFNumberSInt32Type, &value);
   294                 theCD->track[idx].offset = value;
   295 
   296                 theCD->track[idx].type = (isDataTrack == kCFBooleanTrue) ? SDL_DATA_TRACK : SDL_AUDIO_TRACK;
   297 
   298                 // Since the track lengths are not stored in .TOC.plist we compute them.
   299                 if (trackIndex > 0) {
   300                     theCD->track[idx-1].length = theCD->track[idx].offset - theCD->track[idx-1].offset;
   301                 }
   302             }
   303             
   304             // Compute the length of the last track
   305             CFNumberGetValue (leadoutBlock, kCFNumberSInt32Type, &value);
   306             
   307             theCD->track[theCD->numtracks-1].length = 
   308                 value - theCD->track[theCD->numtracks-1].offset;
   309 
   310             // Set offset to leadout track
   311             theCD->track[theCD->numtracks].offset = value;
   312         }
   313     
   314     }
   315 
   316     theErr = 0;
   317     goto cleanup;
   318 bail:
   319     SDL_SetError ("ReadTOCData: %s returned %d", error, theErr);
   320     theErr = -1;
   321 cleanup:
   322 
   323     if (propertyListRef != NULL)
   324         CFRelease(propertyListRef);
   325     if (dataRef != NULL)
   326         CFRelease(dataRef);
   327     if (forkData != NULL)
   328         DisposePtr(forkData);
   329         
   330     FSCloseFork (forkRefNum);
   331 
   332     return theErr;
   333 }
   334 
   335 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   336 //  ListTrackFiles
   337 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   338 
   339 int ListTrackFiles (FSVolumeRefNum theVolume, FSRef *trackFiles, int numTracks)
   340 {
   341     OSStatus        result = -1;
   342     FSIterator      iterator;
   343     ItemCount       actualObjects;
   344     FSRef           rootDirectory;
   345     FSRef           ref;
   346     HFSUniStr255    nameStr;
   347     
   348     result = FSGetVolumeInfo (theVolume,
   349                               0,
   350                               NULL,
   351                               kFSVolInfoFSInfo,
   352                               NULL,
   353                               NULL,
   354                               &rootDirectory); 
   355                                  
   356     if (result != noErr) {
   357         SDL_SetError ("ListTrackFiles: FSGetVolumeInfo returned %d", result);
   358         goto bail;
   359     }
   360 
   361     result = FSOpenIterator (&rootDirectory, kFSIterateFlat, &iterator);
   362     if (result == noErr) {
   363         do
   364         {
   365             result = FSGetCatalogInfoBulk (iterator, 1, &actualObjects,
   366                                            NULL, kFSCatInfoNone, NULL, &ref, NULL, &nameStr);
   367             if (result == noErr) {
   368                 
   369                 CFStringRef  name;
   370                 name = CFStringCreateWithCharacters (NULL, nameStr.unicode, nameStr.length);
   371                 
   372                 // Look for .aiff extension
   373                 if (CFStringHasSuffix (name, CFSTR(".aiff")) ||
   374                     CFStringHasSuffix (name, CFSTR(".cdda"))) {
   375                     
   376                     // Extract the track id from the filename
   377                     int trackID = 0, i = 0;
   378                     while (i < nameStr.length && !isdigit(nameStr.unicode[i])) {
   379                         ++i;
   380                     }
   381                     while (i < nameStr.length && isdigit(nameStr.unicode[i])) {
   382                         trackID = 10 * trackID +(nameStr.unicode[i] - '0');
   383                         ++i;
   384                     }
   385 
   386                     #if DEBUG_CDROM
   387                     printf("Found AIFF for track %d: '%s'\n", trackID, 
   388                     CFStringGetCStringPtr (name, CFStringGetSystemEncoding()));
   389                     #endif
   390                     
   391                     // Track ID's start at 1, but we want to start at 0
   392                     trackID--;
   393                     
   394                     assert(0 <= trackID && trackID <= SDL_MAX_TRACKS);
   395                     
   396                     if (trackID < numTracks)
   397                         memcpy (&trackFiles[trackID], &ref, sizeof(FSRef));
   398                 }
   399                 CFRelease (name);
   400             }
   401         } while(noErr == result);
   402         FSCloseIterator (iterator);
   403     }
   404     
   405     result = 0;
   406   bail:   
   407     return result;
   408 }
   409 
   410 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   411 //  LoadFile
   412 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   413 
   414 int LoadFile (const FSRef *ref, int startFrame, int stopFrame)
   415 {
   416     int error = -1;
   417     
   418     if (CheckInit () < 0)
   419         goto bail;
   420     
   421     // release any currently playing file
   422     if (ReleaseFile () < 0)
   423         goto bail;
   424     
   425     #if DEBUG_CDROM
   426     printf ("LoadFile: %d %d\n", startFrame, stopFrame);
   427     #endif
   428     
   429     try {
   430     
   431         // create a new player, and attach to the audio unit
   432         
   433         thePlayer = new AudioFilePlayer(ref);
   434         if (thePlayer == NULL) {
   435             SDL_SetError ("LoadFile: Could not create player");
   436             throw (-3);
   437         }
   438             
   439         thePlayer->SetDestination(theUnit, 0);
   440         
   441         if (startFrame >= 0)
   442             thePlayer->SetStartFrame (startFrame);
   443         
   444         if (stopFrame >= 0 && stopFrame > startFrame)
   445             thePlayer->SetStopFrame (stopFrame);
   446         
   447         // we set the notifier later
   448         //thePlayer->SetNotifier(FilePlayNotificationHandler, NULL);
   449             
   450         thePlayer->Connect();
   451     
   452         #if DEBUG_CDROM
   453         thePlayer->Print();
   454         fflush (stdout);
   455         #endif
   456     }
   457     catch (...)
   458     {
   459         goto bail;
   460     }
   461         
   462     error = 0;
   463 
   464     bail:
   465     return error;
   466 }
   467 
   468 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   469 //  ReleaseFile
   470 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   471 
   472 int ReleaseFile ()
   473 {
   474     int error = -1;
   475         
   476     try {
   477         if (thePlayer != NULL) {
   478             
   479             thePlayer->Disconnect();
   480             
   481             delete thePlayer;
   482             
   483             thePlayer = NULL;
   484         }
   485     }
   486     catch (...)
   487     {
   488         goto bail;
   489     }
   490     
   491     error = 0;
   492     
   493   bail:
   494     return error;
   495 }
   496 
   497 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   498 //  PlayFile
   499 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   500 
   501 int PlayFile ()
   502 {
   503     OSStatus result = -1;
   504     
   505     if (CheckInit () < 0)
   506         goto bail;
   507         
   508     try {
   509     
   510         // start processing of the audio unit
   511         result = AudioOutputUnitStart (theUnit);
   512             THROW_RESULT("PlayFile: AudioOutputUnitStart")    
   513         
   514     }
   515     catch (...)
   516     {
   517         goto bail;
   518     }
   519     
   520     result = 0;
   521     
   522 bail:
   523     return result;
   524 }
   525 
   526 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   527 //  PauseFile
   528 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   529 
   530 int PauseFile ()
   531 {
   532     OSStatus result = -1;
   533     
   534     if (CheckInit () < 0)
   535         goto bail;
   536             
   537     try {
   538     
   539         // stop processing the audio unit
   540         result = AudioOutputUnitStop (theUnit);
   541             THROW_RESULT("PauseFile: AudioOutputUnitStop")
   542     }
   543     catch (...)
   544     {
   545         goto bail;
   546     }
   547     
   548     result = 0;
   549 bail:
   550     return result;
   551 }
   552 
   553 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   554 //  SetCompletionProc
   555 //ΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡΡ
   556 
   557 void SetCompletionProc (CDPlayerCompletionProc proc, SDL_CD *cdrom)
   558 {
   559     assert(thePlayer != NULL);
   560 
   561     theCDROM = cdrom;
   562     completionProc = proc;
   563     thePlayer->SetNotifier (FilePlayNotificationHandler, cdrom);
   564 }
   565 
   566 
   567 int GetCurrentFrame ()
   568 {    
   569     int frame;
   570     
   571     if (thePlayer == NULL)
   572         frame = 0;
   573     else
   574         frame = thePlayer->GetCurrentFrame ();
   575         
   576     return frame; 
   577 }
   578 
   579 
   580 #pragma mark -- Private Functions --
   581 
   582 OSStatus CheckInit ()
   583 {    
   584     if (playBackWasInit)
   585         return 0;
   586     
   587     OSStatus result = noErr;
   588     
   589         
   590     // Create the callback mutex
   591     pthread_mutexattr_t attr;
   592     pthread_mutexattr_init (&attr);
   593     pthread_mutex_init (&callbackMutex, &attr);
   594     pthread_mutexattr_destroy (&attr);
   595     pthread_mutex_lock (&callbackMutex);
   596         
   597     // Start callback thread
   598     pthread_attr_t attr1;
   599     pthread_attr_init (&attr1);        
   600     pthread_create (&callbackThread, &attr1, RunCallBackThread, NULL);
   601     pthread_attr_destroy (&attr1);
   602 
   603     try {
   604         ComponentDescription desc;
   605     
   606         desc.componentType = kAudioUnitComponentType;
   607         desc.componentSubType = kAudioUnitSubType_Output;
   608         desc.componentManufacturer = kAudioUnitID_DefaultOutput;
   609         desc.componentFlags = 0;
   610         desc.componentFlagsMask = 0;
   611         
   612         Component comp = FindNextComponent (NULL, &desc);
   613         if (comp == NULL) {
   614             SDL_SetError ("CheckInit: FindNextComponent returned NULL");
   615             throw(internalComponentErr);
   616         }
   617         
   618         result = OpenAComponent (comp, &theUnit);
   619             THROW_RESULT("CheckInit: OpenAComponent")
   620                     
   621         // you need to initialize the output unit before you set it as a destination
   622         result = AudioUnitInitialize (theUnit);
   623             THROW_RESULT("CheckInit: AudioUnitInitialize")
   624         
   625                     
   626         // In this case we first want to get the output format of the OutputUnit
   627         // Then we set that as the input format. Why?
   628         // So that only a single conversion process is done
   629         // when SetDestination is called it will get the input format of the
   630         // unit its supplying data to. This defaults to 44.1K, stereo, so if
   631         // the device is not that, then we lose a possibly rendering of data
   632         
   633         result = MatchAUFormats (theUnit, 0);
   634             THROW_RESULT("CheckInit: MatchAUFormats")
   635     
   636         playBackWasInit = true;
   637     }
   638     catch (...)
   639     {
   640         return -1;
   641     }
   642     
   643     return 0;
   644 }
   645 
   646 
   647 OSStatus MatchAUFormats (AudioUnit theUnit, UInt32 theInputBus)
   648 {
   649     AudioStreamBasicDescription theDesc;
   650     UInt32 size = sizeof (theDesc);
   651     OSStatus result = AudioUnitGetProperty (theUnit,
   652                                             kAudioUnitProperty_StreamFormat,
   653                                             kAudioUnitScope_Output,
   654                                             0,
   655                                             &theDesc,
   656                                             &size);
   657         THROW_RESULT("MatchAUFormats: AudioUnitGetProperty")
   658 
   659     result = AudioUnitSetProperty (theUnit,
   660                                    kAudioUnitProperty_StreamFormat,
   661                                    kAudioUnitScope_Input,
   662                                    theInputBus,
   663                                    &theDesc,
   664                                    size);
   665     
   666     return result;
   667 }
   668 
   669 void FilePlayNotificationHandler(void * inRefCon, OSStatus inStatus)
   670 {
   671     if (inStatus == kAudioFilePlay_FileIsFinished) {
   672     
   673         // notify non-CA thread to perform the callback
   674         pthread_mutex_unlock (&callbackMutex);
   675         
   676     } else if (inStatus == kAudioFilePlayErr_FilePlayUnderrun) {
   677     
   678         SDL_SetError ("CDPlayer Notification: buffer underrun");
   679     } else if (inStatus == kAudioFilePlay_PlayerIsUninitialized) {
   680     
   681         SDL_SetError ("CDPlayer Notification: player is uninitialized");
   682     } else {
   683         
   684         SDL_SetError ("CDPlayer Notification: unknown error %ld", inStatus);
   685     }
   686 }
   687 
   688 void* RunCallBackThread (void *param)
   689 {
   690     runCallBackThread = 1;
   691     
   692     while (runCallBackThread) {
   693     
   694         pthread_mutex_lock (&callbackMutex);
   695 
   696         if (completionProc && theCDROM) {
   697             #if DEBUG_CDROM
   698             printf ("callback!\n");
   699             #endif
   700             (*completionProc)(theCDROM);
   701         } else {
   702             #if DEBUG_CDROM
   703             printf ("callback?\n");
   704             #endif
   705         }
   706     }
   707     
   708     runCallBackThread = -1;
   709     
   710     #if DEBUG_CDROM
   711     printf ("thread dying now...\n");
   712     #endif
   713     
   714     return NULL;
   715 }
   716 
   717 }; // extern "C"