Home | History | Annotate | Download | only in macosx
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2012 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 (at) libsdl.org
     21 
     22     This file based on Apple sample code. We haven't changed the file name,
     23     so if you want to see the original search for it on apple.com/developer
     24 */
     25 #include "SDL_config.h"
     26 #include "SDL_endian.h"
     27 
     28 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     29     AudioFilePlayer.cpp
     30 */
     31 #include "AudioFilePlayer.h"
     32 
     33 /*
     34 void ThrowResult (OSStatus result, const char* str)
     35 {
     36     SDL_SetError ("Error: %s %d", str, result);
     37     throw result;
     38 }
     39 */
     40 
     41 #if DEBUG
     42 static void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
     43 {
     44     if (!inDesc) {
     45         printf ("Can't print a NULL desc!\n");
     46         return;
     47     }
     48 
     49     printf ("- - - - - - - - - - - - - - - - - - - -\n");
     50     printf ("  Sample Rate:%f\n", inDesc->mSampleRate);
     51     printf ("  Format ID:%s\n", (char*)&inDesc->mFormatID);
     52     printf ("  Format Flags:%lX\n", inDesc->mFormatFlags);
     53     printf ("  Bytes per Packet:%ld\n", inDesc->mBytesPerPacket);
     54     printf ("  Frames per Packet:%ld\n", inDesc->mFramesPerPacket);
     55     printf ("  Bytes per Frame:%ld\n", inDesc->mBytesPerFrame);
     56     printf ("  Channels per Frame:%ld\n", inDesc->mChannelsPerFrame);
     57     printf ("  Bits per Channel:%ld\n", inDesc->mBitsPerChannel);
     58     printf ("- - - - - - - - - - - - - - - - - - - -\n");
     59 }
     60 #endif
     61 
     62 
     63 static int AudioFilePlayer_SetDestination (AudioFilePlayer *afp, AudioUnit  *inDestUnit)
     64 {
     65     /*if (afp->mConnected) throw static_cast<OSStatus>(-1);*/ /* can't set dest if already engaged */
     66     if (afp->mConnected)
     67         return 0 ;
     68 
     69     SDL_memcpy(&afp->mPlayUnit, inDestUnit, sizeof (afp->mPlayUnit));
     70 
     71     OSStatus result = noErr;
     72 
     73 
     74         /* we can "down" cast a component instance to a component */
     75     ComponentDescription desc;
     76     result = GetComponentInfo ((Component)*inDestUnit, &desc, 0, 0, 0);
     77     if (result) return 0; /*THROW_RESULT("GetComponentInfo")*/
     78 
     79         /* we're going to use this to know which convert routine to call
     80            a v1 audio unit will have a type of 'aunt'
     81            a v2 audio unit will have one of several different types. */
     82     if (desc.componentType != kAudioUnitType_Output) {
     83         result = badComponentInstance;
     84         /*THROW_RESULT("BAD COMPONENT")*/
     85         if (result) return 0;
     86     }
     87 
     88     /* Set the input format of the audio unit. */
     89     result = AudioUnitSetProperty (*inDestUnit,
     90                                kAudioUnitProperty_StreamFormat,
     91                                kAudioUnitScope_Input,
     92                                0,
     93                                &afp->mFileDescription,
     94                                sizeof (afp->mFileDescription));
     95         /*THROW_RESULT("AudioUnitSetProperty")*/
     96     if (result) return 0;
     97     return 1;
     98 }
     99 
    100 static void AudioFilePlayer_SetNotifier(AudioFilePlayer *afp, AudioFilePlayNotifier inNotifier, void *inRefCon)
    101 {
    102     afp->mNotifier = inNotifier;
    103     afp->mRefCon = inRefCon;
    104 }
    105 
    106 static int AudioFilePlayer_IsConnected(AudioFilePlayer *afp)
    107 {
    108     return afp->mConnected;
    109 }
    110 
    111 static AudioUnit AudioFilePlayer_GetDestUnit(AudioFilePlayer *afp)
    112 {
    113    return afp->mPlayUnit;
    114 }
    115 
    116 static void AudioFilePlayer_Print(AudioFilePlayer *afp)
    117 {
    118 #if DEBUG
    119     printf ("Is Connected:%s\n", (IsConnected() ? "true" : "false"));
    120     printf ("- - - - - - - - - - - - - - \n");
    121 #endif
    122 }
    123 
    124 static void    AudioFilePlayer_SetStartFrame (AudioFilePlayer *afp, int frame)
    125 {
    126     SInt64 position = frame * 2352;
    127 
    128     afp->mStartFrame = frame;
    129     afp->mAudioFileManager->SetPosition (afp->mAudioFileManager, position);
    130 }
    131 
    132 
    133 static int    AudioFilePlayer_GetCurrentFrame (AudioFilePlayer *afp)
    134 {
    135     return afp->mStartFrame + (afp->mAudioFileManager->GetByteCounter(afp->mAudioFileManager) / 2352);
    136 }
    137 
    138 static void    AudioFilePlayer_SetStopFrame (AudioFilePlayer *afp, int frame)
    139 {
    140     SInt64 position  = frame * 2352;
    141 
    142     afp->mAudioFileManager->SetEndOfFile (afp->mAudioFileManager, position);
    143 }
    144 
    145 void delete_AudioFilePlayer(AudioFilePlayer *afp)
    146 {
    147     if (afp != NULL)
    148     {
    149         afp->Disconnect(afp);
    150 
    151         if (afp->mAudioFileManager) {
    152             delete_AudioFileManager(afp->mAudioFileManager);
    153             afp->mAudioFileManager = 0;
    154         }
    155 
    156         if (afp->mForkRefNum) {
    157             FSCloseFork (afp->mForkRefNum);
    158             afp->mForkRefNum = 0;
    159         }
    160         SDL_free(afp);
    161     }
    162 }
    163 
    164 static int    AudioFilePlayer_Connect(AudioFilePlayer *afp)
    165 {
    166 #if DEBUG
    167     printf ("Connect:%x, engaged=%d\n", (int)afp->mPlayUnit, (afp->mConnected ? 1 : 0));
    168 #endif
    169     if (!afp->mConnected)
    170     {
    171         if (!afp->mAudioFileManager->DoConnect(afp->mAudioFileManager))
    172             return 0;
    173 
    174         /* set the render callback for the file data to be supplied to the sound converter AU */
    175         afp->mInputCallback.inputProc = afp->mAudioFileManager->FileInputProc;
    176         afp->mInputCallback.inputProcRefCon = afp->mAudioFileManager;
    177 
    178         OSStatus result = AudioUnitSetProperty (afp->mPlayUnit,
    179                             kAudioUnitProperty_SetRenderCallback,
    180                             kAudioUnitScope_Input,
    181                             0,
    182                             &afp->mInputCallback,
    183                             sizeof(afp->mInputCallback));
    184         if (result) return 0;  /*THROW_RESULT("AudioUnitSetProperty")*/
    185         afp->mConnected = 1;
    186     }
    187 
    188     return 1;
    189 }
    190 
    191 /* warning noted, now please go away ;-) */
    192 /* #warning This should redirect the calling of notification code to some other thread */
    193 static void    AudioFilePlayer_DoNotification (AudioFilePlayer *afp, OSStatus inStatus)
    194 {
    195     if (afp->mNotifier) {
    196         (*afp->mNotifier) (afp->mRefCon, inStatus);
    197     } else {
    198         SDL_SetError ("Notification posted with no notifier in place");
    199 
    200         if (inStatus == kAudioFilePlay_FileIsFinished)
    201             afp->Disconnect(afp);
    202         else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun)
    203             afp->Disconnect(afp);
    204     }
    205 }
    206 
    207 static void    AudioFilePlayer_Disconnect (AudioFilePlayer *afp)
    208 {
    209 #if DEBUG
    210     printf ("Disconnect:%x,%ld, engaged=%d\n", (int)afp->mPlayUnit, 0, (afp->mConnected ? 1 : 0));
    211 #endif
    212     if (afp->mConnected)
    213     {
    214         afp->mConnected = 0;
    215 
    216         afp->mInputCallback.inputProc = 0;
    217         afp->mInputCallback.inputProcRefCon = 0;
    218         OSStatus result = AudioUnitSetProperty (afp->mPlayUnit,
    219                                         kAudioUnitProperty_SetRenderCallback,
    220                                         kAudioUnitScope_Input,
    221                                         0,
    222                                         &afp->mInputCallback,
    223                                         sizeof(afp->mInputCallback));
    224         if (result)
    225             SDL_SetError ("AudioUnitSetProperty:RemoveInputCallback:%ld", result);
    226 
    227         afp->mAudioFileManager->Disconnect(afp->mAudioFileManager);
    228     }
    229 }
    230 
    231 typedef struct {
    232     UInt32 offset;
    233     UInt32 blockSize;
    234 } SSNDData;
    235 
    236 static int    AudioFilePlayer_OpenFile (AudioFilePlayer *afp, const FSRef *inRef, SInt64 *outFileDataSize)
    237 {
    238     ContainerChunk chunkHeader;
    239     ChunkHeader chunk;
    240     SSNDData ssndData;
    241 
    242     OSErr result;
    243     HFSUniStr255 dfName;
    244     ByteCount actual;
    245     SInt64 offset;
    246 
    247     /* Open the data fork of the input file */
    248     result = FSGetDataForkName(&dfName);
    249        if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSGetDataForkName")*/
    250 
    251     result = FSOpenFork(inRef, dfName.length, dfName.unicode, fsRdPerm, &afp->mForkRefNum);
    252        if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSOpenFork")*/
    253 
    254     /* Read the file header, and check if it's indeed an AIFC file */
    255     result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(chunkHeader), &chunkHeader, &actual);
    256        if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/
    257 
    258     if (SDL_SwapBE32(chunkHeader.ckID) != 'FORM') {
    259         result = -1;
    260         if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): chunk id is not 'FORM'");*/
    261     }
    262 
    263     if (SDL_SwapBE32(chunkHeader.formType) != 'AIFC') {
    264         result = -1;
    265         if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): file format is not 'AIFC'");*/
    266     }
    267 
    268     /* Search for the SSND chunk. We ignore all compression etc. information
    269        in other chunks. Of course that is kind of evil, but for now we are lazy
    270        and rely on the cdfs to always give us the same fixed format.
    271        TODO: Parse the COMM chunk we currently skip to fill in mFileDescription.
    272     */
    273     offset = 0;
    274     do {
    275         result = FSReadFork(afp->mForkRefNum, fsFromMark, offset, sizeof(chunk), &chunk, &actual);
    276         if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/
    277 
    278         chunk.ckID = SDL_SwapBE32(chunk.ckID);
    279         chunk.ckSize = SDL_SwapBE32(chunk.ckSize);
    280 
    281         /* Skip the chunk data */
    282         offset = chunk.ckSize;
    283     } while (chunk.ckID != 'SSND');
    284 
    285     /* Read the header of the SSND chunk. After this, we are positioned right
    286        at the start of the audio data. */
    287     result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(ssndData), &ssndData, &actual);
    288     if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")*/
    289 
    290     ssndData.offset = SDL_SwapBE32(ssndData.offset);
    291 
    292     result = FSSetForkPosition(afp->mForkRefNum, fsFromMark, ssndData.offset);
    293     if (result) return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSSetForkPosition")*/
    294 
    295     /* Data size */
    296     *outFileDataSize = chunk.ckSize - ssndData.offset - 8;
    297 
    298     /* File format */
    299     afp->mFileDescription.mSampleRate = 44100;
    300     afp->mFileDescription.mFormatID = kAudioFormatLinearPCM;
    301     afp->mFileDescription.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger;
    302     afp->mFileDescription.mBytesPerPacket = 4;
    303     afp->mFileDescription.mFramesPerPacket = 1;
    304     afp->mFileDescription.mBytesPerFrame = 4;
    305     afp->mFileDescription.mChannelsPerFrame = 2;
    306     afp->mFileDescription.mBitsPerChannel = 16;
    307 
    308     return 1;
    309 }
    310 
    311 AudioFilePlayer *new_AudioFilePlayer (const FSRef *inFileRef)
    312 {
    313     SInt64 fileDataSize  = 0;
    314 
    315     AudioFilePlayer *afp = (AudioFilePlayer *) SDL_malloc(sizeof (AudioFilePlayer));
    316     if (afp == NULL)
    317         return NULL;
    318     SDL_memset(afp, '\0', sizeof (*afp));
    319 
    320     #define SET_AUDIOFILEPLAYER_METHOD(m) afp->m = AudioFilePlayer_##m
    321     SET_AUDIOFILEPLAYER_METHOD(SetDestination);
    322     SET_AUDIOFILEPLAYER_METHOD(SetNotifier);
    323     SET_AUDIOFILEPLAYER_METHOD(SetStartFrame);
    324     SET_AUDIOFILEPLAYER_METHOD(GetCurrentFrame);
    325     SET_AUDIOFILEPLAYER_METHOD(SetStopFrame);
    326     SET_AUDIOFILEPLAYER_METHOD(Connect);
    327     SET_AUDIOFILEPLAYER_METHOD(Disconnect);
    328     SET_AUDIOFILEPLAYER_METHOD(DoNotification);
    329     SET_AUDIOFILEPLAYER_METHOD(IsConnected);
    330     SET_AUDIOFILEPLAYER_METHOD(GetDestUnit);
    331     SET_AUDIOFILEPLAYER_METHOD(Print);
    332     SET_AUDIOFILEPLAYER_METHOD(OpenFile);
    333     #undef SET_AUDIOFILEPLAYER_METHOD
    334 
    335     if (!afp->OpenFile (afp, inFileRef, &fileDataSize))
    336     {
    337         SDL_free(afp);
    338         return NULL;
    339     }
    340 
    341     /* we want about 4 seconds worth of data for the buffer */
    342     int bytesPerSecond = (UInt32) (4 * afp->mFileDescription.mSampleRate * afp->mFileDescription.mBytesPerFrame);
    343 
    344 #if DEBUG
    345     printf("File format:\n");
    346     PrintStreamDesc (&afp->mFileDescription);
    347 #endif
    348 
    349     afp->mAudioFileManager = new_AudioFileManager(afp, afp->mForkRefNum,
    350                                                   fileDataSize,
    351                                                   bytesPerSecond);
    352     if (afp->mAudioFileManager == NULL)
    353     {
    354         delete_AudioFilePlayer(afp);
    355         return NULL;
    356     }
    357 
    358     return afp;
    359 }
    360 
    361