SDL-1.2.14
[sdl_omap.git] / src / cdrom / macosx / AudioFileReaderThread.c
CommitLineData
e14743d1 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;*/
38typedef struct S_FileData
39{
40 AudioFileManager *obj;
41 struct S_FileData *next;
42} FileData;
43
44
45typedef 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
73static SDLOSXCAGuard* FileReaderThread_GetGuard(FileReaderThread *frt)
74{
75 return frt->mGuard;
76}
77
78/* returns 1 if succeeded */
79static 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
110static 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
120static 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
155static 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
193static 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
234static 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
245static 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
329void delete_FileReaderThread(FileReaderThread *frt)
330{
331 if (frt != NULL)
332 {
333 delete_SDLOSXCAGuard(frt->mGuard);
334 SDL_free(frt);
335 }
336}
337
338FileReaderThread *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
368static FileReaderThread *sReaderThread;
369
370
371static 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
409static void AudioFileManager_Disconnect (AudioFileManager *afm)
410{
411 if (afm->mIsEngaged)
412 {
413 sReaderThread->RemoveReader (sReaderThread, afm);
414 afm->mIsEngaged = 0;
415 }
416}
417
418static 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
428static 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
458static 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
475static 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
486static 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
496static const char *AudioFileManager_GetFileBuffer(AudioFileManager *afm)
497{
498 return afm->mFileBuffer;
499}
500
501const AudioFilePlayer *AudioFileManager_GetParent(AudioFileManager *afm)
502{
503 return afm->mParent;
504}
505
506static int AudioFileManager_GetByteCounter(AudioFileManager *afm)
507{
508 return afm->mByteCounter;
509}
510
511static 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
522static 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
553void 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
565AudioFileManager *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