src/cdrom/macosx/AudioFilePlayer.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 09 May 2006 07:52:04 +0000
changeset 1793 4d66375c2012
parent 1487 dc6b59e925a2
child 1662 782fd950bd46
child 1895 c121d94672cb
child 4159 a1b03ba2fcd0
permissions -rw-r--r--
Date: Mon, 8 May 2006 14:19:30 -0700
From: Bob Ippolito
Subject: SDL trunk (r2346) and Mac OS X

As for all the Carbon warnings.. the two File Manager ones should be
easy to get rid of, the QuickDraw ones won't be so easy since that
requires actual refactoring.

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