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 
     27 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     28    AudioFileManager.cpp
     29 */
     30 #include "AudioFilePlayer.h"
     31 #include <mach/mach.h> /* used for setting policy of thread */
     32 #include "SDLOSXCAGuard.h"
     33 #include <pthread.h>
     34 
     35 /*#include <list>*/
     36 
     37 /*typedef void *FileData;*/
     38 typedef struct S_FileData
     39 {
     40     AudioFileManager *obj;
     41     struct S_FileData *next;
     42 } FileData;
     43 
     44 
     45 typedef struct S_FileReaderThread {
     46 /*public:*/
     47     SDLOSXCAGuard*                    (*GetGuard)(struct S_FileReaderThread *frt);
     48     void                        (*AddReader)(struct S_FileReaderThread *frt);
     49     void                        (*RemoveReader)(struct S_FileReaderThread *frt, AudioFileManager* inItem);
     50     int                         (*TryNextRead)(struct S_FileReaderThread *frt, AudioFileManager* inItem);
     51 
     52     int     mThreadShouldDie;
     53 
     54 /*private:*/
     55     /*typedef std::list<AudioFileManager*> FileData;*/
     56 
     57     SDLOSXCAGuard             *mGuard;
     58     UInt32              mThreadPriority;
     59 
     60     int                 mNumReaders;
     61     FileData            *mFileData;
     62 
     63 
     64     void                        (*ReadNextChunk)(struct S_FileReaderThread *frt);
     65     int                         (*StartFixedPriorityThread)(struct S_FileReaderThread *frt);
     66     /*static*/
     67     UInt32               (*GetThreadBasePriority)(pthread_t inThread);
     68     /*static*/
     69     void*                (*DiskReaderEntry)(void *inRefCon);
     70 } FileReaderThread;
     71 
     72 
     73 static SDLOSXCAGuard* FileReaderThread_GetGuard(FileReaderThread *frt)
     74 {
     75     return frt->mGuard;
     76 }
     77 
     78 /* returns 1 if succeeded */
     79 static int FileReaderThread_TryNextRead (FileReaderThread *frt, AudioFileManager* inItem)
     80 {
     81     int didLock = 0;
     82     int succeeded = 0;
     83     if (frt->mGuard->Try(frt->mGuard, &didLock))
     84     {
     85         /*frt->mFileData.push_back (inItem);*/
     86         /* !!! FIXME: this could be faster with a "tail" member. --ryan. */
     87         FileData *i = frt->mFileData;
     88         FileData *prev = NULL;
     89 
     90         FileData *newfd = (FileData *) SDL_malloc(sizeof (FileData));
     91         newfd->obj = inItem;
     92         newfd->next = NULL;
     93 
     94         while (i != NULL) { prev = i; i = i->next; }
     95         if (prev == NULL)
     96             frt->mFileData = newfd;
     97         else
     98             prev->next = newfd;
     99 
    100         frt->mGuard->Notify(frt->mGuard);
    101         succeeded = 1;
    102 
    103         if (didLock)
    104             frt->mGuard->Unlock(frt->mGuard);
    105     }
    106 
    107     return succeeded;
    108 }
    109 
    110 static void    FileReaderThread_AddReader(FileReaderThread *frt)
    111 {
    112     if (frt->mNumReaders == 0)
    113     {
    114         frt->mThreadShouldDie = 0;
    115         frt->StartFixedPriorityThread (frt);
    116     }
    117     frt->mNumReaders++;
    118 }
    119 
    120 static void    FileReaderThread_RemoveReader (FileReaderThread *frt, AudioFileManager* inItem)
    121 {
    122     if (frt->mNumReaders > 0)
    123     {
    124         int bNeedsRelease = frt->mGuard->Lock(frt->mGuard);
    125 
    126         /*frt->mFileData.remove (inItem);*/
    127         FileData *i = frt->mFileData;
    128         FileData *prev = NULL;
    129         while (i != NULL)
    130         {
    131             FileData *next = i->next;
    132             if (i->obj != inItem)
    133                 prev = i;
    134             else
    135             {
    136                 if (prev == NULL)
    137                     frt->mFileData = next;
    138                 else
    139                     prev->next = next;
    140                 SDL_free(i);
    141             }
    142             i = next;
    143         }
    144 
    145         if (--frt->mNumReaders == 0) {
    146             frt->mThreadShouldDie = 1;
    147             frt->mGuard->Notify(frt->mGuard); /* wake up thread so it will quit */
    148             frt->mGuard->Wait(frt->mGuard);   /* wait for thread to die */
    149         }
    150 
    151         if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
    152     }
    153 }
    154 
    155 static int    FileReaderThread_StartFixedPriorityThread (FileReaderThread *frt)
    156 {
    157     pthread_attr_t      theThreadAttrs;
    158     pthread_t           pThread;
    159 
    160     OSStatus result = pthread_attr_init(&theThreadAttrs);
    161         if (result) return 0; /*THROW_RESULT("pthread_attr_init - Thread attributes could not be created.")*/
    162 
    163     result = pthread_attr_setdetachstate(&theThreadAttrs, PTHREAD_CREATE_DETACHED);
    164         if (result) return 0; /*THROW_RESULT("pthread_attr_setdetachstate - Thread attributes could not be detached.")*/
    165 
    166     result = pthread_create (&pThread, &theThreadAttrs, frt->DiskReaderEntry, frt);
    167         if (result) return 0; /*THROW_RESULT("pthread_create - Create and start the thread.")*/
    168 
    169     pthread_attr_destroy(&theThreadAttrs);
    170 
    171     /* we've now created the thread and started it
    172        we'll now set the priority of the thread to the nominated priority
    173        and we'll also make the thread fixed */
    174     thread_extended_policy_data_t       theFixedPolicy;
    175     thread_precedence_policy_data_t     thePrecedencePolicy;
    176     SInt32                              relativePriority;
    177 
    178     /* make thread fixed */
    179     theFixedPolicy.timeshare = 0;   /* set to 1 for a non-fixed thread */
    180     result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT);
    181         if (result) return 0; /*THROW_RESULT("thread_policy - Couldn't set thread as fixed priority.")*/
    182     /* set priority */
    183     /* precedency policy's "importance" value is relative to spawning thread's priority */
    184     relativePriority = frt->mThreadPriority - frt->GetThreadBasePriority(pthread_self());
    185 
    186     thePrecedencePolicy.importance = relativePriority;
    187     result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_PRECEDENCE_POLICY, (thread_policy_t)&thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT);
    188         if (result) return 0; /*THROW_RESULT("thread_policy - Couldn't set thread priority.")*/
    189 
    190     return 1;
    191 }
    192 
    193 static UInt32  FileReaderThread_GetThreadBasePriority (pthread_t inThread)
    194 {
    195     thread_basic_info_data_t            threadInfo;
    196     policy_info_data_t                  thePolicyInfo;
    197     unsigned int                        count;
    198 
    199     /* get basic info */
    200     count = THREAD_BASIC_INFO_COUNT;
    201     thread_info (pthread_mach_thread_np (inThread), THREAD_BASIC_INFO, (integer_t*)&threadInfo, &count);
    202 
    203     switch (threadInfo.policy) {
    204         case POLICY_TIMESHARE:
    205             count = POLICY_TIMESHARE_INFO_COUNT;
    206             thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_TIMESHARE_INFO, (integer_t*)&(thePolicyInfo.ts), &count);
    207             return thePolicyInfo.ts.base_priority;
    208             break;
    209 
    210         case POLICY_FIFO:
    211             count = POLICY_FIFO_INFO_COUNT;
    212             thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_FIFO_INFO, (integer_t*)&(thePolicyInfo.fifo), &count);
    213             if (thePolicyInfo.fifo.depressed) {
    214                 return thePolicyInfo.fifo.depress_priority;
    215             } else {
    216                 return thePolicyInfo.fifo.base_priority;
    217             }
    218             break;
    219 
    220         case POLICY_RR:
    221             count = POLICY_RR_INFO_COUNT;
    222             thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_RR_INFO, (integer_t*)&(thePolicyInfo.rr), &count);
    223             if (thePolicyInfo.rr.depressed) {
    224                 return thePolicyInfo.rr.depress_priority;
    225             } else {
    226                 return thePolicyInfo.rr.base_priority;
    227             }
    228             break;
    229     }
    230 
    231     return 0;
    232 }
    233 
    234 static void    *FileReaderThread_DiskReaderEntry (void *inRefCon)
    235 {
    236     FileReaderThread *frt = (FileReaderThread *)inRefCon;
    237     frt->ReadNextChunk(frt);
    238     #if DEBUG
    239     printf ("finished with reading file\n");
    240     #endif
    241 
    242     return 0;
    243 }
    244 
    245 static void    FileReaderThread_ReadNextChunk (FileReaderThread *frt)
    246 {
    247     OSStatus result;
    248     ByteCount dataChunkSize;
    249     AudioFileManager* theItem = 0;
    250 
    251     for (;;)
    252     {
    253         { /* this is a scoped based lock */
    254             int bNeedsRelease = frt->mGuard->Lock(frt->mGuard);
    255 
    256             if (frt->mThreadShouldDie) {
    257                 frt->mGuard->Notify(frt->mGuard);
    258                 if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
    259                 return;
    260             }
    261 
    262             /*if (frt->mFileData.empty())*/
    263             if (frt->mFileData == NULL)
    264             {
    265                 frt->mGuard->Wait(frt->mGuard);
    266             }
    267 
    268             /* kill thread */
    269             if (frt->mThreadShouldDie) {
    270 
    271                 frt->mGuard->Notify(frt->mGuard);
    272                 if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
    273                 return;
    274             }
    275 
    276             /*theItem = frt->mFileData.front();*/
    277             /*frt->mFileData.pop_front();*/
    278             theItem = NULL;
    279             if (frt->mFileData != NULL)
    280             {
    281                 FileData *next = frt->mFileData->next;
    282                 theItem = frt->mFileData->obj;
    283                 SDL_free(frt->mFileData);
    284                 frt->mFileData = next;
    285             }
    286 
    287             if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
    288         }
    289 
    290         if ((theItem->mFileLength - theItem->mReadFilePosition) < theItem->mChunkSize)
    291             dataChunkSize = theItem->mFileLength - theItem->mReadFilePosition;
    292         else
    293             dataChunkSize = theItem->mChunkSize;
    294 
    295             /* this is the exit condition for the thread */
    296         if (dataChunkSize <= 0) {
    297             theItem->mFinishedReadingData = 1;
    298             continue;
    299         }
    300             /* construct pointer */
    301         char* writePtr = (char *) (theItem->GetFileBuffer(theItem) +
    302                                 (theItem->mWriteToFirstBuffer ? 0 : theItem->mChunkSize));
    303 
    304             /* read data */
    305         result = theItem->Read(theItem, writePtr, &dataChunkSize);
    306         if (result != noErr && result != eofErr) {
    307             AudioFilePlayer *afp = (AudioFilePlayer *) theItem->GetParent(theItem);
    308             afp->DoNotification(afp, result);
    309             continue;
    310         }
    311 
    312         if (dataChunkSize != theItem->mChunkSize)
    313         {
    314             writePtr += dataChunkSize;
    315 
    316             /* can't exit yet.. we still have to pass the partial buffer back */
    317             SDL_memset(writePtr, 0, (theItem->mChunkSize - dataChunkSize));
    318         }
    319 
    320         theItem->mWriteToFirstBuffer = !theItem->mWriteToFirstBuffer;   /* switch buffers */
    321 
    322         if (result == eofErr)
    323             theItem->mReadFilePosition = theItem->mFileLength;
    324         else
    325             theItem->mReadFilePosition += dataChunkSize;        /* increment count */
    326     }
    327 }
    328 
    329 void delete_FileReaderThread(FileReaderThread *frt)
    330 {
    331     if (frt != NULL)
    332     {
    333         delete_SDLOSXCAGuard(frt->mGuard);
    334         SDL_free(frt);
    335     }
    336 }
    337 
    338 FileReaderThread *new_FileReaderThread ()
    339 {
    340     FileReaderThread *frt = (FileReaderThread *) SDL_malloc(sizeof (FileReaderThread));
    341     if (frt == NULL)
    342         return NULL;
    343     SDL_memset(frt, '\0', sizeof (*frt));
    344 
    345     frt->mGuard = new_SDLOSXCAGuard();
    346     if (frt->mGuard == NULL)
    347     {
    348         SDL_free(frt);
    349         return NULL;
    350     }
    351 
    352     #define SET_FILEREADERTHREAD_METHOD(m) frt->m = FileReaderThread_##m
    353     SET_FILEREADERTHREAD_METHOD(GetGuard);
    354     SET_FILEREADERTHREAD_METHOD(AddReader);
    355     SET_FILEREADERTHREAD_METHOD(RemoveReader);
    356     SET_FILEREADERTHREAD_METHOD(TryNextRead);
    357     SET_FILEREADERTHREAD_METHOD(ReadNextChunk);
    358     SET_FILEREADERTHREAD_METHOD(StartFixedPriorityThread);
    359     SET_FILEREADERTHREAD_METHOD(GetThreadBasePriority);
    360     SET_FILEREADERTHREAD_METHOD(DiskReaderEntry);
    361     #undef SET_FILEREADERTHREAD_METHOD
    362 
    363     frt->mThreadPriority = 62;
    364     return frt;
    365 }
    366 
    367 
    368 static FileReaderThread *sReaderThread;
    369 
    370 
    371 static int    AudioFileManager_DoConnect (AudioFileManager *afm)
    372 {
    373     if (!afm->mIsEngaged)
    374     {
    375         OSStatus result;
    376 
    377         /*afm->mReadFilePosition = 0;*/
    378         afm->mFinishedReadingData = 0;
    379 
    380         afm->mNumTimesAskedSinceFinished = 0;
    381         afm->mLockUnsuccessful = 0;
    382 
    383         ByteCount dataChunkSize;
    384 
    385         if ((afm->mFileLength - afm->mReadFilePosition) < afm->mChunkSize)
    386             dataChunkSize = afm->mFileLength - afm->mReadFilePosition;
    387         else
    388             dataChunkSize = afm->mChunkSize;
    389 
    390         result = afm->Read(afm, afm->mFileBuffer, &dataChunkSize);
    391            if (result) return 0; /*THROW_RESULT("AudioFileManager::DoConnect(): Read")*/
    392 
    393         afm->mReadFilePosition += dataChunkSize;
    394 
    395         afm->mWriteToFirstBuffer = 0;
    396         afm->mReadFromFirstBuffer = 1;
    397 
    398         sReaderThread->AddReader(sReaderThread);
    399 
    400         afm->mIsEngaged = 1;
    401     }
    402     /*
    403     else
    404         throw static_cast<OSStatus>(-1); */ /* thread has already been started */
    405 
    406     return 1;
    407 }
    408 
    409 static void    AudioFileManager_Disconnect (AudioFileManager *afm)
    410 {
    411     if (afm->mIsEngaged)
    412     {
    413         sReaderThread->RemoveReader (sReaderThread, afm);
    414         afm->mIsEngaged = 0;
    415     }
    416 }
    417 
    418 static OSStatus AudioFileManager_Read(AudioFileManager *afm, char *buffer, ByteCount *len)
    419 {
    420     return FSReadFork (afm->mForkRefNum,
    421                        fsFromStart,
    422                        afm->mReadFilePosition + afm->mAudioDataOffset,
    423                        *len,
    424                        buffer,
    425                        len);
    426 }
    427 
    428 static OSStatus AudioFileManager_GetFileData (AudioFileManager *afm, void** inOutData, UInt32 *inOutDataSize)
    429 {
    430     if (afm->mFinishedReadingData)
    431     {
    432         ++afm->mNumTimesAskedSinceFinished;
    433         *inOutDataSize = 0;
    434         *inOutData = 0;
    435         return noErr;
    436     }
    437 
    438     if (afm->mReadFromFirstBuffer == afm->mWriteToFirstBuffer) {
    439         #if DEBUG
    440         printf ("* * * * * * * Can't keep up with reading file\n");
    441         #endif
    442 
    443         afm->mParent->DoNotification (afm->mParent, kAudioFilePlayErr_FilePlayUnderrun);
    444         *inOutDataSize = 0;
    445         *inOutData = 0;
    446     } else {
    447         *inOutDataSize = afm->mChunkSize;
    448         *inOutData = afm->mReadFromFirstBuffer ? afm->mFileBuffer : (afm->mFileBuffer + afm->mChunkSize);
    449     }
    450 
    451     afm->mLockUnsuccessful = !sReaderThread->TryNextRead (sReaderThread, afm);
    452 
    453     afm->mReadFromFirstBuffer = !afm->mReadFromFirstBuffer;
    454 
    455     return noErr;
    456 }
    457 
    458 static void    AudioFileManager_AfterRender (AudioFileManager *afm)
    459 {
    460     if (afm->mNumTimesAskedSinceFinished > 0)
    461     {
    462         int didLock = 0;
    463         SDLOSXCAGuard *guard = sReaderThread->GetGuard(sReaderThread);
    464         if (guard->Try(guard, &didLock)) {
    465             afm->mParent->DoNotification (afm->mParent, kAudioFilePlay_FileIsFinished);
    466             if (didLock)
    467                 guard->Unlock(guard);
    468         }
    469     }
    470 
    471     if (afm->mLockUnsuccessful)
    472         afm->mLockUnsuccessful = !sReaderThread->TryNextRead (sReaderThread, afm);
    473 }
    474 
    475 static void    AudioFileManager_SetPosition (AudioFileManager *afm, SInt64 pos)
    476 {
    477     if (pos < 0 || pos >= afm->mFileLength) {
    478         SDL_SetError ("AudioFileManager::SetPosition - position invalid: %d filelen=%d\n",
    479             (unsigned int)pos, (unsigned int)afm->mFileLength);
    480         pos = 0;
    481     }
    482 
    483     afm->mReadFilePosition = pos;
    484 }
    485 
    486 static void    AudioFileManager_SetEndOfFile (AudioFileManager *afm, SInt64 pos)
    487 {
    488     if (pos <= 0 || pos > afm->mFileLength) {
    489         SDL_SetError ("AudioFileManager::SetEndOfFile - position beyond actual eof\n");
    490         pos = afm->mFileLength;
    491     }
    492 
    493     afm->mFileLength = pos;
    494 }
    495 
    496 static const char *AudioFileManager_GetFileBuffer(AudioFileManager *afm)
    497 {
    498     return afm->mFileBuffer;
    499 }
    500 
    501 const AudioFilePlayer *AudioFileManager_GetParent(AudioFileManager *afm)
    502 {
    503     return afm->mParent;
    504 }
    505 
    506 static int AudioFileManager_GetByteCounter(AudioFileManager *afm)
    507 {
    508     return afm->mByteCounter;
    509 }
    510 
    511 static OSStatus    AudioFileManager_FileInputProc (void                  *inRefCon,
    512                                          AudioUnitRenderActionFlags      *ioActionFlags,
    513                                          const AudioTimeStamp            *inTimeStamp,
    514                                          UInt32                          inBusNumber,
    515                                          UInt32                          inNumberFrames,
    516                                          AudioBufferList                 *ioData)
    517 {
    518     AudioFileManager* afm = (AudioFileManager*)inRefCon;
    519     return afm->Render(afm, ioData);
    520 }
    521 
    522 static OSStatus    AudioFileManager_Render (AudioFileManager *afm, AudioBufferList *ioData)
    523 {
    524     OSStatus result = noErr;
    525     AudioBuffer *abuf;
    526     UInt32 i;
    527 
    528     for (i = 0; i < ioData->mNumberBuffers; i++) {
    529         abuf = &ioData->mBuffers[i];
    530         if (afm->mBufferOffset >= afm->mBufferSize) {
    531             result = afm->GetFileData(afm, &afm->mTmpBuffer, &afm->mBufferSize);
    532             if (result) {
    533                 SDL_SetError ("AudioConverterFillBuffer:%ld\n", result);
    534                 afm->mParent->DoNotification(afm->mParent, result);
    535                 return result;
    536             }
    537 
    538             afm->mBufferOffset = 0;
    539         }
    540 
    541         if (abuf->mDataByteSize > afm->mBufferSize - afm->mBufferOffset)
    542             abuf->mDataByteSize = afm->mBufferSize - afm->mBufferOffset;
    543         abuf->mData = (char *)afm->mTmpBuffer + afm->mBufferOffset;
    544         afm->mBufferOffset += abuf->mDataByteSize;
    545 
    546         afm->mByteCounter += abuf->mDataByteSize;
    547         afm->AfterRender(afm);
    548     }
    549     return result;
    550 }
    551 
    552 
    553 void delete_AudioFileManager (AudioFileManager *afm)
    554 {
    555     if (afm != NULL) {
    556         if (afm->mFileBuffer) {
    557             free(afm->mFileBuffer);
    558         }
    559 
    560         SDL_free(afm);
    561     }
    562 }
    563 
    564 
    565 AudioFileManager *new_AudioFileManager(AudioFilePlayer *inParent,
    566                                        SInt16          inForkRefNum,
    567                                        SInt64          inFileLength,
    568                                        UInt32          inChunkSize)
    569 {
    570     AudioFileManager *afm;
    571 
    572     if (sReaderThread == NULL)
    573     {
    574         sReaderThread = new_FileReaderThread();
    575         if (sReaderThread == NULL)
    576             return NULL;
    577     }
    578 
    579     afm = (AudioFileManager *) SDL_malloc(sizeof (AudioFileManager));
    580     if (afm == NULL)
    581         return NULL;
    582     SDL_memset(afm, '\0', sizeof (*afm));
    583 
    584     #define SET_AUDIOFILEMANAGER_METHOD(m) afm->m = AudioFileManager_##m
    585     SET_AUDIOFILEMANAGER_METHOD(Disconnect);
    586     SET_AUDIOFILEMANAGER_METHOD(DoConnect);
    587     SET_AUDIOFILEMANAGER_METHOD(Read);
    588     SET_AUDIOFILEMANAGER_METHOD(GetFileBuffer);
    589     SET_AUDIOFILEMANAGER_METHOD(GetParent);
    590     SET_AUDIOFILEMANAGER_METHOD(SetPosition);
    591     SET_AUDIOFILEMANAGER_METHOD(GetByteCounter);
    592     SET_AUDIOFILEMANAGER_METHOD(SetEndOfFile);
    593     SET_AUDIOFILEMANAGER_METHOD(Render);
    594     SET_AUDIOFILEMANAGER_METHOD(GetFileData);
    595     SET_AUDIOFILEMANAGER_METHOD(AfterRender);
    596     SET_AUDIOFILEMANAGER_METHOD(FileInputProc);
    597     #undef SET_AUDIOFILEMANAGER_METHOD
    598 
    599     afm->mParent = inParent;
    600     afm->mForkRefNum = inForkRefNum;
    601     afm->mBufferSize = inChunkSize;
    602     afm->mBufferOffset = inChunkSize;
    603     afm->mChunkSize = inChunkSize;
    604     afm->mFileLength = inFileLength;
    605     afm->mFileBuffer = (char*) SDL_malloc(afm->mChunkSize * 2);
    606     FSGetForkPosition(afm->mForkRefNum, &afm->mAudioDataOffset);
    607     assert (afm->mFileBuffer != NULL);
    608     return afm;
    609 }
    610 
    611