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