diff options
Diffstat (limited to 'distrib/sdl-1.2.15/src/cdrom/macosx/CDPlayer.c')
-rw-r--r-- | distrib/sdl-1.2.15/src/cdrom/macosx/CDPlayer.c | 636 |
1 files changed, 636 insertions, 0 deletions
diff --git a/distrib/sdl-1.2.15/src/cdrom/macosx/CDPlayer.c b/distrib/sdl-1.2.15/src/cdrom/macosx/CDPlayer.c new file mode 100644 index 0000000..beb87cd --- /dev/null +++ b/distrib/sdl-1.2.15/src/cdrom/macosx/CDPlayer.c @@ -0,0 +1,636 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997-2012 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@libsdl.org +*/ +#include "SDL_config.h" + +#include "CDPlayer.h" +#include "AudioFilePlayer.h" +#include "SDLOSXCAGuard.h" + +/* we're exporting these functions into C land for SDL_syscdrom.c */ +/*extern "C" {*/ + +/*/////////////////////////////////////////////////////////////////////////// + Constants + //////////////////////////////////////////////////////////////////////////*/ + +#define kAudioCDFilesystemID (UInt16)(('J' << 8) | 'H') /* 'JH'; this avoids compiler warning */ + +/* XML PList keys */ +#define kRawTOCDataString "Format 0x02 TOC Data" +#define kSessionsString "Sessions" +#define kSessionTypeString "Session Type" +#define kTrackArrayString "Track Array" +#define kFirstTrackInSessionString "First Track" +#define kLastTrackInSessionString "Last Track" +#define kLeadoutBlockString "Leadout Block" +#define kDataKeyString "Data" +#define kPointKeyString "Point" +#define kSessionNumberKeyString "Session Number" +#define kStartBlockKeyString "Start Block" + +/*/////////////////////////////////////////////////////////////////////////// + Globals + //////////////////////////////////////////////////////////////////////////*/ + +#pragma mark -- Globals -- + +static int playBackWasInit = 0; +static AudioUnit theUnit; +static AudioFilePlayer* thePlayer = NULL; +static CDPlayerCompletionProc completionProc = NULL; +static SDL_mutex *apiMutex = NULL; +static SDL_sem *callbackSem; +static SDL_CD* theCDROM; + +/*/////////////////////////////////////////////////////////////////////////// + Prototypes + //////////////////////////////////////////////////////////////////////////*/ + +#pragma mark -- Prototypes -- + +static OSStatus CheckInit (); + +static void FilePlayNotificationHandler (void* inRefCon, OSStatus inStatus); + +static int RunCallBackThread (void* inRefCon); + + +#pragma mark -- Public Functions -- + +void Lock () +{ + if (!apiMutex) { + apiMutex = SDL_CreateMutex(); + } + SDL_mutexP(apiMutex); +} + +void Unlock () +{ + SDL_mutexV(apiMutex); +} + +int DetectAudioCDVolumes(FSVolumeRefNum *volumes, int numVolumes) +{ + int volumeIndex; + int cdVolumeCount = 0; + OSStatus result = noErr; + + for (volumeIndex = 1; result == noErr || result != nsvErr; volumeIndex++) + { + FSVolumeRefNum actualVolume; + FSVolumeInfo volumeInfo; + + memset (&volumeInfo, 0, sizeof(volumeInfo)); + + result = FSGetVolumeInfo (kFSInvalidVolumeRefNum, + volumeIndex, + &actualVolume, + kFSVolInfoFSInfo, + &volumeInfo, + NULL, + NULL); + + if (result == noErr) + { + if (volumeInfo.filesystemID == kAudioCDFilesystemID) /* It's an audio CD */ + { + if (volumes != NULL && cdVolumeCount < numVolumes) + volumes[cdVolumeCount] = actualVolume; + + cdVolumeCount++; + } + } + else + { + /* I'm commenting this out because it seems to be harmless */ + /*SDL_SetError ("DetectAudioCDVolumes: FSGetVolumeInfo returned %d", result);*/ + } + } + + return cdVolumeCount; +} + +int ReadTOCData (FSVolumeRefNum theVolume, SDL_CD *theCD) +{ + HFSUniStr255 dataForkName; + OSStatus theErr; + FSIORefNum forkRefNum; + SInt64 forkSize; + Ptr forkData = 0; + ByteCount actualRead; + CFDataRef dataRef = 0; + CFPropertyListRef propertyListRef = 0; + FSRefParam fsRefPB; + FSRef tocPlistFSRef; + FSRef rootRef; + const char* error = "Unspecified Error"; + const UniChar uniName[] = { '.','T','O','C','.','p','l','i','s','t' }; + + theErr = FSGetVolumeInfo(theVolume, 0, 0, kFSVolInfoNone, 0, 0, &rootRef); + if(theErr != noErr) { + error = "FSGetVolumeInfo"; + goto bail; + } + + SDL_memset(&fsRefPB, '\0', sizeof (fsRefPB)); + + /* get stuff from .TOC.plist */ + fsRefPB.ref = &rootRef; + fsRefPB.newRef = &tocPlistFSRef; + fsRefPB.nameLength = sizeof (uniName) / sizeof (uniName[0]); + fsRefPB.name = uniName; + fsRefPB.textEncodingHint = kTextEncodingUnknown; + + theErr = PBMakeFSRefUnicodeSync (&fsRefPB); + if(theErr != noErr) { + error = "PBMakeFSRefUnicodeSync"; + goto bail; + } + + /* Load and parse the TOC XML data */ + + theErr = FSGetDataForkName (&dataForkName); + if (theErr != noErr) { + error = "FSGetDataForkName"; + goto bail; + } + + theErr = FSOpenFork (&tocPlistFSRef, dataForkName.length, dataForkName.unicode, fsRdPerm, &forkRefNum); + if (theErr != noErr) { + error = "FSOpenFork"; + goto bail; + } + + theErr = FSGetForkSize (forkRefNum, &forkSize); + if (theErr != noErr) { + error = "FSGetForkSize"; + goto bail; + } + + /* Allocate some memory for the XML data */ + forkData = NewPtr (forkSize); + if(forkData == NULL) { + error = "NewPtr"; + goto bail; + } + + theErr = FSReadFork (forkRefNum, fsFromStart, 0 /* offset location */, forkSize, forkData, &actualRead); + if(theErr != noErr) { + error = "FSReadFork"; + goto bail; + } + + dataRef = CFDataCreate (kCFAllocatorDefault, (UInt8 *)forkData, forkSize); + if(dataRef == 0) { + error = "CFDataCreate"; + goto bail; + } + + propertyListRef = CFPropertyListCreateFromXMLData (kCFAllocatorDefault, + dataRef, + kCFPropertyListImmutable, + NULL); + if (propertyListRef == NULL) { + error = "CFPropertyListCreateFromXMLData"; + goto bail; + } + + /* Now we got the Property List in memory. Parse it. */ + + /* First, make sure the root item is a CFDictionary. If not, release and bail. */ + if(CFGetTypeID(propertyListRef)== CFDictionaryGetTypeID()) + { + CFDictionaryRef dictRef = (CFDictionaryRef)propertyListRef; + + CFDataRef theRawTOCDataRef; + CFArrayRef theSessionArrayRef; + CFIndex numSessions; + CFIndex index; + + /* This is how we get the Raw TOC Data */ + theRawTOCDataRef = (CFDataRef)CFDictionaryGetValue (dictRef, CFSTR(kRawTOCDataString)); + + /* Get the session array info. */ + theSessionArrayRef = (CFArrayRef)CFDictionaryGetValue (dictRef, CFSTR(kSessionsString)); + + /* Find out how many sessions there are. */ + numSessions = CFArrayGetCount (theSessionArrayRef); + + /* Initialize the total number of tracks to 0 */ + theCD->numtracks = 0; + + /* Iterate over all sessions, collecting the track data */ + for(index = 0; index < numSessions; index++) + { + CFDictionaryRef theSessionDict; + CFNumberRef leadoutBlock; + CFArrayRef trackArray; + CFIndex numTracks; + CFIndex trackIndex; + UInt32 value = 0; + + theSessionDict = (CFDictionaryRef) CFArrayGetValueAtIndex (theSessionArrayRef, index); + leadoutBlock = (CFNumberRef) CFDictionaryGetValue (theSessionDict, CFSTR(kLeadoutBlockString)); + + trackArray = (CFArrayRef)CFDictionaryGetValue (theSessionDict, CFSTR(kTrackArrayString)); + + numTracks = CFArrayGetCount (trackArray); + + for(trackIndex = 0; trackIndex < numTracks; trackIndex++) { + + CFDictionaryRef theTrackDict; + CFNumberRef trackNumber; + CFNumberRef sessionNumber; + CFNumberRef startBlock; + CFBooleanRef isDataTrack; + UInt32 value; + + theTrackDict = (CFDictionaryRef) CFArrayGetValueAtIndex (trackArray, trackIndex); + + trackNumber = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kPointKeyString)); + sessionNumber = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kSessionNumberKeyString)); + startBlock = (CFNumberRef) CFDictionaryGetValue (theTrackDict, CFSTR(kStartBlockKeyString)); + isDataTrack = (CFBooleanRef) CFDictionaryGetValue (theTrackDict, CFSTR(kDataKeyString)); + + /* Fill in the SDL_CD struct */ + int idx = theCD->numtracks++; + + CFNumberGetValue (trackNumber, kCFNumberSInt32Type, &value); + theCD->track[idx].id = value; + + CFNumberGetValue (startBlock, kCFNumberSInt32Type, &value); + theCD->track[idx].offset = value; + + theCD->track[idx].type = (isDataTrack == kCFBooleanTrue) ? SDL_DATA_TRACK : SDL_AUDIO_TRACK; + + /* Since the track lengths are not stored in .TOC.plist we compute them. */ + if (trackIndex > 0) { + theCD->track[idx-1].length = theCD->track[idx].offset - theCD->track[idx-1].offset; + } + } + + /* Compute the length of the last track */ + CFNumberGetValue (leadoutBlock, kCFNumberSInt32Type, &value); + + theCD->track[theCD->numtracks-1].length = + value - theCD->track[theCD->numtracks-1].offset; + + /* Set offset to leadout track */ + theCD->track[theCD->numtracks].offset = value; + } + + } + + theErr = 0; + goto cleanup; +bail: + SDL_SetError ("ReadTOCData: %s returned %d", error, theErr); + theErr = -1; +cleanup: + + if (propertyListRef != NULL) + CFRelease(propertyListRef); + if (dataRef != NULL) + CFRelease(dataRef); + if (forkData != NULL) + DisposePtr(forkData); + + FSCloseFork (forkRefNum); + + return theErr; +} + +int ListTrackFiles (FSVolumeRefNum theVolume, FSRef *trackFiles, int numTracks) +{ + OSStatus result = -1; + FSIterator iterator; + ItemCount actualObjects; + FSRef rootDirectory; + FSRef ref; + HFSUniStr255 nameStr; + + result = FSGetVolumeInfo (theVolume, + 0, + NULL, + kFSVolInfoFSInfo, + NULL, + NULL, + &rootDirectory); + + if (result != noErr) { + SDL_SetError ("ListTrackFiles: FSGetVolumeInfo returned %d", result); + return result; + } + + result = FSOpenIterator (&rootDirectory, kFSIterateFlat, &iterator); + if (result == noErr) { + do + { + result = FSGetCatalogInfoBulk (iterator, 1, &actualObjects, + NULL, kFSCatInfoNone, NULL, &ref, NULL, &nameStr); + if (result == noErr) { + + CFStringRef name; + name = CFStringCreateWithCharacters (NULL, nameStr.unicode, nameStr.length); + + /* Look for .aiff extension */ + if (CFStringHasSuffix (name, CFSTR(".aiff")) || + CFStringHasSuffix (name, CFSTR(".cdda"))) { + + /* Extract the track id from the filename */ + int trackID = 0, i = 0; + while (i < nameStr.length && !isdigit(nameStr.unicode[i])) { + ++i; + } + while (i < nameStr.length && isdigit(nameStr.unicode[i])) { + trackID = 10 * trackID +(nameStr.unicode[i] - '0'); + ++i; + } + + #if DEBUG_CDROM + printf("Found AIFF for track %d: '%s'\n", trackID, + CFStringGetCStringPtr (name, CFStringGetSystemEncoding())); + #endif + + /* Track ID's start at 1, but we want to start at 0 */ + trackID--; + + assert(0 <= trackID && trackID <= SDL_MAX_TRACKS); + + if (trackID < numTracks) + memcpy (&trackFiles[trackID], &ref, sizeof(FSRef)); + } + CFRelease (name); + } + } while(noErr == result); + FSCloseIterator (iterator); + } + + return 0; +} + +int LoadFile (const FSRef *ref, int startFrame, int stopFrame) +{ + int error = -1; + + if (CheckInit () < 0) + goto bail; + + /* release any currently playing file */ + if (ReleaseFile () < 0) + goto bail; + + #if DEBUG_CDROM + printf ("LoadFile: %d %d\n", startFrame, stopFrame); + #endif + + /*try {*/ + + /* create a new player, and attach to the audio unit */ + + thePlayer = new_AudioFilePlayer(ref); + if (thePlayer == NULL) { + SDL_SetError ("LoadFile: Could not create player"); + return -3; /*throw (-3);*/ + } + + if (!thePlayer->SetDestination(thePlayer, &theUnit)) + goto bail; + + if (startFrame >= 0) + thePlayer->SetStartFrame (thePlayer, startFrame); + + if (stopFrame >= 0 && stopFrame > startFrame) + thePlayer->SetStopFrame (thePlayer, stopFrame); + + /* we set the notifier later */ + /*thePlayer->SetNotifier(thePlayer, FilePlayNotificationHandler, NULL);*/ + + if (!thePlayer->Connect(thePlayer)) + goto bail; + + #if DEBUG_CDROM + thePlayer->Print(thePlayer); + fflush (stdout); + #endif + /*} + catch (...) + { + goto bail; + }*/ + + error = 0; + + bail: + return error; +} + +int ReleaseFile () +{ + int error = -1; + + /* (Don't see any way that the original C++ code could throw here.) --ryan. */ + /*try {*/ + if (thePlayer != NULL) { + + thePlayer->Disconnect(thePlayer); + + delete_AudioFilePlayer(thePlayer); + + thePlayer = NULL; + } + /*} + catch (...) + { + goto bail; + }*/ + + error = 0; + +/* bail: */ + return error; +} + +int PlayFile () +{ + OSStatus result = -1; + + if (CheckInit () < 0) + goto bail; + + /*try {*/ + + // start processing of the audio unit + result = AudioOutputUnitStart (theUnit); + if (result) goto bail; //THROW_RESULT("PlayFile: AudioOutputUnitStart") + + /*} + catch (...) + { + goto bail; + }*/ + + result = 0; + +bail: + return result; +} + +int PauseFile () +{ + OSStatus result = -1; + + if (CheckInit () < 0) + goto bail; + + /*try {*/ + + /* stop processing the audio unit */ + result = AudioOutputUnitStop (theUnit); + if (result) goto bail; /*THROW_RESULT("PauseFile: AudioOutputUnitStop")*/ + /*} + catch (...) + { + goto bail; + }*/ + + result = 0; +bail: + return result; +} + +void SetCompletionProc (CDPlayerCompletionProc proc, SDL_CD *cdrom) +{ + assert(thePlayer != NULL); + + theCDROM = cdrom; + completionProc = proc; + thePlayer->SetNotifier (thePlayer, FilePlayNotificationHandler, cdrom); +} + +int GetCurrentFrame () +{ + int frame; + + if (thePlayer == NULL) + frame = 0; + else + frame = thePlayer->GetCurrentFrame (thePlayer); + + return frame; +} + + +#pragma mark -- Private Functions -- + +static OSStatus CheckInit () +{ + if (playBackWasInit) + return 0; + + OSStatus result = noErr; + + /* Create the callback semaphore */ + callbackSem = SDL_CreateSemaphore(0); + + /* Start callback thread */ + SDL_CreateThread(RunCallBackThread, NULL); + + { /*try {*/ + ComponentDescription desc; + + desc.componentType = kAudioUnitType_Output; + desc.componentSubType = kAudioUnitSubType_DefaultOutput; + desc.componentManufacturer = kAudioUnitManufacturer_Apple; + desc.componentFlags = 0; + desc.componentFlagsMask = 0; + + Component comp = FindNextComponent (NULL, &desc); + if (comp == NULL) { + SDL_SetError ("CheckInit: FindNextComponent returned NULL"); + if (result) return -1; //throw(internalComponentErr); + } + + result = OpenAComponent (comp, &theUnit); + if (result) return -1; //THROW_RESULT("CheckInit: OpenAComponent") + + // you need to initialize the output unit before you set it as a destination + result = AudioUnitInitialize (theUnit); + if (result) return -1; //THROW_RESULT("CheckInit: AudioUnitInitialize") + + + playBackWasInit = true; + } + /*catch (...) + { + return -1; + }*/ + + return 0; +} + +static void FilePlayNotificationHandler(void * inRefCon, OSStatus inStatus) +{ + if (inStatus == kAudioFilePlay_FileIsFinished) { + + /* notify non-CA thread to perform the callback */ + SDL_SemPost(callbackSem); + + } else if (inStatus == kAudioFilePlayErr_FilePlayUnderrun) { + + SDL_SetError ("CDPlayer Notification: buffer underrun"); + } else if (inStatus == kAudioFilePlay_PlayerIsUninitialized) { + + SDL_SetError ("CDPlayer Notification: player is uninitialized"); + } else { + + SDL_SetError ("CDPlayer Notification: unknown error %ld", inStatus); + } +} + +static int RunCallBackThread (void *param) +{ + for (;;) { + + SDL_SemWait(callbackSem); + + if (completionProc && theCDROM) { + #if DEBUG_CDROM + printf ("callback!\n"); + #endif + (*completionProc)(theCDROM); + } else { + #if DEBUG_CDROM + printf ("callback?\n"); + #endif + } + } + + #if DEBUG_CDROM + printf ("thread dying now...\n"); + #endif + + return 0; +} + +/*}; // extern "C" */ |