comparison src/cdrom/macosx/CDPlayer.cpp @ 613:9c6717a1c66f

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