Mercurial > sdl-ios-xcode
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" |