SDL-1.2.14
[sdl_omap.git] / src / cdrom / macosx / AudioFileReaderThread.c
1 /*
2     SDL - Simple DirectMedia Layer
3     Copyright (C) 1997-2009 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@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