src/cdrom/macosx/AudioFilePlayer.c
changeset 1143 71a2648acc75
child 1336 3692456e7b0f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/cdrom/macosx/AudioFilePlayer.c	Thu Sep 22 08:48:16 2005 +0000
@@ -0,0 +1,352 @@
+/*
+    SDL - Simple DirectMedia Layer
+    Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002  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
+
+    This file based on Apple sample code. We haven't changed the file name, 
+    so if you want to see the original search for it on apple.com/developer
+*/
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//  AudioFilePlayer.cpp
+//
+#include "AudioFilePlayer.h"
+
+/*
+void ThrowResult (OSStatus result, const char* str)
+{
+    SDL_SetError ("Error: %s %d", str, result);
+    throw result;
+}
+*/
+
+#if DEBUG
+static void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
+{
+    if (!inDesc) {
+        printf ("Can't print a NULL desc!\n");
+        return;
+    }
+    
+    printf ("- - - - - - - - - - - - - - - - - - - -\n");
+    printf ("  Sample Rate:%f\n", inDesc->mSampleRate);
+    printf ("  Format ID:%s\n", (char*)&inDesc->mFormatID);
+    printf ("  Format Flags:%lX\n", inDesc->mFormatFlags);
+    printf ("  Bytes per Packet:%ld\n", inDesc->mBytesPerPacket);
+    printf ("  Frames per Packet:%ld\n", inDesc->mFramesPerPacket);
+    printf ("  Bytes per Frame:%ld\n", inDesc->mBytesPerFrame);
+    printf ("  Channels per Frame:%ld\n", inDesc->mChannelsPerFrame);
+    printf ("  Bits per Channel:%ld\n", inDesc->mBitsPerChannel);
+    printf ("- - - - - - - - - - - - - - - - - - - -\n");
+}
+#endif
+
+
+static int AudioFilePlayer_SetDestination (AudioFilePlayer *afp, AudioUnit  *inDestUnit)
+{
+    //if (afp->mConnected) throw static_cast<OSStatus>(-1); //can't set dest if already engaged
+    if (afp->mConnected)
+        return 0 ;
+
+    memcpy(&afp->mPlayUnit, inDestUnit, sizeof (afp->mPlayUnit));
+
+    OSStatus result = noErr;
+    
+
+        //we can "down" cast a component instance to a component
+    ComponentDescription desc;
+    result = GetComponentInfo ((Component)*inDestUnit, &desc, 0, 0, 0);
+    if (result) return 0; //THROW_RESULT("GetComponentInfo")
+        
+        // we're going to use this to know which convert routine to call
+        // a v1 audio unit will have a type of 'aunt'
+        // a v2 audio unit will have one of several different types.
+    if (desc.componentType != kAudioUnitComponentType) {
+        result = badComponentInstance;
+        //THROW_RESULT("BAD COMPONENT")
+        if (result) return 0;
+    }
+
+    /* Set the input format of the audio unit. */
+    result = AudioUnitSetProperty (*inDestUnit,
+                               kAudioUnitProperty_StreamFormat,
+                               kAudioUnitScope_Input,
+                               0,
+                               &afp->mFileDescription,
+                               sizeof (afp->mFileDescription));
+        //THROW_RESULT("AudioUnitSetProperty")
+    if (result) return 0;
+    return 1;
+}
+
+static void AudioFilePlayer_SetNotifier(AudioFilePlayer *afp, AudioFilePlayNotifier inNotifier, void *inRefCon)
+{
+    afp->mNotifier = inNotifier;
+    afp->mRefCon = inRefCon;
+}
+
+static int AudioFilePlayer_IsConnected(AudioFilePlayer *afp)
+{
+    return afp->mConnected;
+}
+
+static AudioUnit AudioFilePlayer_GetDestUnit(AudioFilePlayer *afp)
+{
+   return afp->mPlayUnit;
+}
+
+static void AudioFilePlayer_Print(AudioFilePlayer *afp)
+{
+#if DEBUG    
+    printf ("Is Connected:%s\n", (IsConnected() ? "true" : "false"));
+    printf ("- - - - - - - - - - - - - - \n");
+#endif
+}
+
+static void    AudioFilePlayer_SetStartFrame (AudioFilePlayer *afp, int frame)
+{
+    SInt64 position = frame * 2352;
+
+    afp->mStartFrame = frame;
+    afp->mAudioFileManager->SetPosition (afp->mAudioFileManager, position);
+}
+
+    
+static int    AudioFilePlayer_GetCurrentFrame (AudioFilePlayer *afp)
+{
+    return afp->mStartFrame + (afp->mAudioFileManager->GetByteCounter(afp->mAudioFileManager) / 2352);
+}
+    
+static void    AudioFilePlayer_SetStopFrame (AudioFilePlayer *afp, int frame)
+{
+    SInt64 position  = frame * 2352;
+    
+    afp->mAudioFileManager->SetEndOfFile (afp->mAudioFileManager, position);
+}
+    
+void delete_AudioFilePlayer(AudioFilePlayer *afp)
+{
+    if (afp != NULL)
+    {
+        afp->Disconnect(afp);
+        
+        if (afp->mAudioFileManager) {
+            delete_AudioFileManager(afp->mAudioFileManager);
+            afp->mAudioFileManager = 0;
+        }
+    
+        if (afp->mForkRefNum) {
+            FSClose (afp->mForkRefNum);
+            afp->mForkRefNum = 0;
+        }
+        free(afp);
+    }
+}
+
+static int    AudioFilePlayer_Connect(AudioFilePlayer *afp)
+{
+#if DEBUG
+    printf ("Connect:%x, engaged=%d\n", (int)afp->mPlayUnit, (afp->mConnected ? 1 : 0));
+#endif
+    if (!afp->mConnected)
+    {           
+        if (!afp->mAudioFileManager->DoConnect(afp->mAudioFileManager))
+            return 0;
+
+        // set the render callback for the file data to be supplied to the sound converter AU
+        afp->mInputCallback.inputProc = afp->mAudioFileManager->FileInputProc;
+        afp->mInputCallback.inputProcRefCon = afp->mAudioFileManager;
+
+        OSStatus result = AudioUnitSetProperty (afp->mPlayUnit, 
+                            kAudioUnitProperty_SetInputCallback, 
+                            kAudioUnitScope_Input, 
+                            0,
+                            &afp->mInputCallback, 
+                            sizeof(afp->mInputCallback));
+        if (result) return 0;  //THROW_RESULT("AudioUnitSetProperty")
+        afp->mConnected = 1;
+    }
+
+    return 1;
+}
+
+// warning noted, now please go away ;-)
+// #warning This should redirect the calling of notification code to some other thread
+static void    AudioFilePlayer_DoNotification (AudioFilePlayer *afp, OSStatus inStatus)
+{
+    if (afp->mNotifier) {
+        (*afp->mNotifier) (afp->mRefCon, inStatus);
+    } else {
+        SDL_SetError ("Notification posted with no notifier in place");
+        
+        if (inStatus == kAudioFilePlay_FileIsFinished)
+            afp->Disconnect(afp);
+        else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun)
+            afp->Disconnect(afp);
+    }
+}
+
+static void    AudioFilePlayer_Disconnect (AudioFilePlayer *afp)
+{
+#if DEBUG
+    printf ("Disconnect:%x,%ld, engaged=%d\n", (int)afp->mPlayUnit, 0, (afp->mConnected ? 1 : 0));
+#endif
+    if (afp->mConnected)
+    {
+        afp->mConnected = 0;
+            
+        afp->mInputCallback.inputProc = 0;
+        afp->mInputCallback.inputProcRefCon = 0;
+        OSStatus result = AudioUnitSetProperty (afp->mPlayUnit, 
+                                        kAudioUnitProperty_SetInputCallback, 
+                                        kAudioUnitScope_Input, 
+                                        0,
+                                        &afp->mInputCallback, 
+                                        sizeof(afp->mInputCallback));
+        if (result) 
+            SDL_SetError ("AudioUnitSetProperty:RemoveInputCallback:%ld", result);
+
+        afp->mAudioFileManager->Disconnect(afp->mAudioFileManager);
+    }
+}
+
+typedef struct {
+    UInt32 offset;
+    UInt32 blockSize;
+} SSNDData;
+
+static int    AudioFilePlayer_OpenFile (AudioFilePlayer *afp, const FSRef *inRef, SInt64 *outFileDataSize)
+{
+    ContainerChunk chunkHeader;
+    ChunkHeader chunk;
+    SSNDData ssndData;
+
+    OSErr result;
+    HFSUniStr255 dfName;
+    ByteCount actual;
+    SInt64 offset;
+
+    // Open the data fork of the input file
+    result = FSGetDataForkName(&dfName);
+       if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSGetDataForkName")
+
+    result = FSOpenFork(inRef, dfName.length, dfName.unicode, fsRdPerm, &afp->mForkRefNum);
+       if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSOpenFork")
+ 
+    // Read the file header, and check if it's indeed an AIFC file
+    result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(chunkHeader), &chunkHeader, &actual);
+       if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")
+
+    if (chunkHeader.ckID != 'FORM') {
+        result = -1;
+        if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): chunk id is not 'FORM'");
+    }
+
+    if (chunkHeader.formType != 'AIFC') {
+        result = -1;
+        if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): file format is not 'AIFC'");
+    }
+
+    // Search for the SSND chunk. We ignore all compression etc. information
+    // in other chunks. Of course that is kind of evil, but for now we are lazy
+    // and rely on the cdfs to always give us the same fixed format.
+    // TODO: Parse the COMM chunk we currently skip to fill in mFileDescription.
+    offset = 0;
+    do {
+        result = FSReadFork(afp->mForkRefNum, fsFromMark, offset, sizeof(chunk), &chunk, &actual);
+           if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")
+            
+        // Skip the chunk data
+        offset = chunk.ckSize;
+    } while (chunk.ckID != 'SSND');
+
+    // Read the header of the SSND chunk. After this, we are positioned right
+    // at the start of the audio data.
+    result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(ssndData), &ssndData, &actual);
+       if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")
+
+    result = FSSetForkPosition(afp->mForkRefNum, fsFromMark, ssndData.offset);
+       if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSSetForkPosition")
+
+    // Data size
+    *outFileDataSize = chunk.ckSize - ssndData.offset - 8;
+
+    // File format
+    afp->mFileDescription.mSampleRate = 44100;
+    afp->mFileDescription.mFormatID = kAudioFormatLinearPCM;
+    afp->mFileDescription.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger;
+    afp->mFileDescription.mBytesPerPacket = 4;
+    afp->mFileDescription.mFramesPerPacket = 1;
+    afp->mFileDescription.mBytesPerFrame = 4;
+    afp->mFileDescription.mChannelsPerFrame = 2;
+    afp->mFileDescription.mBitsPerChannel = 16;
+
+    return 1;
+}
+
+AudioFilePlayer *new_AudioFilePlayer (const FSRef *inFileRef)
+{
+    SInt64 fileDataSize  = 0;
+
+    AudioFilePlayer *afp = (AudioFilePlayer *) malloc(sizeof (AudioFilePlayer));
+    if (afp == NULL)
+        return NULL;
+    memset(afp, '\0', sizeof (*afp));
+
+    #define SET_AUDIOFILEPLAYER_METHOD(m) afp->m = AudioFilePlayer_##m
+    SET_AUDIOFILEPLAYER_METHOD(SetDestination);
+    SET_AUDIOFILEPLAYER_METHOD(SetNotifier);
+    SET_AUDIOFILEPLAYER_METHOD(SetStartFrame);
+    SET_AUDIOFILEPLAYER_METHOD(GetCurrentFrame);
+    SET_AUDIOFILEPLAYER_METHOD(SetStopFrame);
+    SET_AUDIOFILEPLAYER_METHOD(Connect);
+    SET_AUDIOFILEPLAYER_METHOD(Disconnect);
+    SET_AUDIOFILEPLAYER_METHOD(DoNotification);
+    SET_AUDIOFILEPLAYER_METHOD(IsConnected);
+    SET_AUDIOFILEPLAYER_METHOD(GetDestUnit);
+    SET_AUDIOFILEPLAYER_METHOD(Print);
+    SET_AUDIOFILEPLAYER_METHOD(OpenFile);
+    #undef SET_AUDIOFILEPLAYER_METHOD
+
+    if (!afp->OpenFile (afp, inFileRef, &fileDataSize))
+    {
+        free(afp);
+        return NULL;
+    }
+        
+    // we want about 4 seconds worth of data for the buffer
+    int bytesPerSecond = (UInt32) (4 * afp->mFileDescription.mSampleRate * afp->mFileDescription.mBytesPerFrame);
+    
+#if DEBUG
+    printf("File format:\n");
+    PrintStreamDesc (&afp->mFileDescription);
+#endif
+    
+    afp->mAudioFileManager = new_AudioFileManager(afp, afp->mForkRefNum,
+                                                  fileDataSize,
+                                                  bytesPerSecond);
+    if (afp->mAudioFileManager == NULL)
+    {
+        delete_AudioFilePlayer(afp);
+        return NULL;
+    }
+
+    return afp;
+}
+