2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997-2009 Sam Lantinga
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.
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.
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
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
25 #include "SDL_config.h"
27 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
30 #include "AudioFilePlayer.h"
31 #include <mach/mach.h> /* used for setting policy of thread */
32 #include "SDLOSXCAGuard.h"
37 /*typedef void *FileData;*/
38 typedef struct S_FileData
40 AudioFileManager *obj;
41 struct S_FileData *next;
45 typedef struct S_FileReaderThread {
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);
55 /*typedef std::list<AudioFileManager*> FileData;*/
57 SDLOSXCAGuard *mGuard;
58 UInt32 mThreadPriority;
64 void (*ReadNextChunk)(struct S_FileReaderThread *frt);
65 int (*StartFixedPriorityThread)(struct S_FileReaderThread *frt);
67 UInt32 (*GetThreadBasePriority)(pthread_t inThread);
69 void* (*DiskReaderEntry)(void *inRefCon);
73 static SDLOSXCAGuard* FileReaderThread_GetGuard(FileReaderThread *frt)
78 /* returns 1 if succeeded */
79 static int FileReaderThread_TryNextRead (FileReaderThread *frt, AudioFileManager* inItem)
83 if (frt->mGuard->Try(frt->mGuard, &didLock))
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;
90 FileData *newfd = (FileData *) SDL_malloc(sizeof (FileData));
94 while (i != NULL) { prev = i; i = i->next; }
96 frt->mFileData = newfd;
100 frt->mGuard->Notify(frt->mGuard);
104 frt->mGuard->Unlock(frt->mGuard);
110 static void FileReaderThread_AddReader(FileReaderThread *frt)
112 if (frt->mNumReaders == 0)
114 frt->mThreadShouldDie = 0;
115 frt->StartFixedPriorityThread (frt);
120 static void FileReaderThread_RemoveReader (FileReaderThread *frt, AudioFileManager* inItem)
122 if (frt->mNumReaders > 0)
124 int bNeedsRelease = frt->mGuard->Lock(frt->mGuard);
126 /*frt->mFileData.remove (inItem);*/
127 FileData *i = frt->mFileData;
128 FileData *prev = NULL;
131 FileData *next = i->next;
132 if (i->obj != inItem)
137 frt->mFileData = next;
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 */
151 if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
155 static int FileReaderThread_StartFixedPriorityThread (FileReaderThread *frt)
157 pthread_attr_t theThreadAttrs;
160 OSStatus result = pthread_attr_init(&theThreadAttrs);
161 if (result) return 0; /*THROW_RESULT("pthread_attr_init - Thread attributes could not be created.")*/
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.")*/
166 result = pthread_create (&pThread, &theThreadAttrs, frt->DiskReaderEntry, frt);
167 if (result) return 0; /*THROW_RESULT("pthread_create - Create and start the thread.")*/
169 pthread_attr_destroy(&theThreadAttrs);
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;
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.")*/
183 /* precedency policy's "importance" value is relative to spawning thread's priority */
184 relativePriority = frt->mThreadPriority - frt->GetThreadBasePriority(pthread_self());
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.")*/
193 static UInt32 FileReaderThread_GetThreadBasePriority (pthread_t inThread)
195 thread_basic_info_data_t threadInfo;
196 policy_info_data_t thePolicyInfo;
200 count = THREAD_BASIC_INFO_COUNT;
201 thread_info (pthread_mach_thread_np (inThread), THREAD_BASIC_INFO, (integer_t*)&threadInfo, &count);
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;
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;
216 return thePolicyInfo.fifo.base_priority;
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;
226 return thePolicyInfo.rr.base_priority;
234 static void *FileReaderThread_DiskReaderEntry (void *inRefCon)
236 FileReaderThread *frt = (FileReaderThread *)inRefCon;
237 frt->ReadNextChunk(frt);
239 printf ("finished with reading file\n");
245 static void FileReaderThread_ReadNextChunk (FileReaderThread *frt)
248 ByteCount dataChunkSize;
249 AudioFileManager* theItem = 0;
253 { /* this is a scoped based lock */
254 int bNeedsRelease = frt->mGuard->Lock(frt->mGuard);
256 if (frt->mThreadShouldDie) {
257 frt->mGuard->Notify(frt->mGuard);
258 if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
262 /*if (frt->mFileData.empty())*/
263 if (frt->mFileData == NULL)
265 frt->mGuard->Wait(frt->mGuard);
269 if (frt->mThreadShouldDie) {
271 frt->mGuard->Notify(frt->mGuard);
272 if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
276 /*theItem = frt->mFileData.front();*/
277 /*frt->mFileData.pop_front();*/
279 if (frt->mFileData != NULL)
281 FileData *next = frt->mFileData->next;
282 theItem = frt->mFileData->obj;
283 SDL_free(frt->mFileData);
284 frt->mFileData = next;
287 if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
290 if ((theItem->mFileLength - theItem->mReadFilePosition) < theItem->mChunkSize)
291 dataChunkSize = theItem->mFileLength - theItem->mReadFilePosition;
293 dataChunkSize = theItem->mChunkSize;
295 /* this is the exit condition for the thread */
296 if (dataChunkSize <= 0) {
297 theItem->mFinishedReadingData = 1;
300 /* construct pointer */
301 char* writePtr = (char *) (theItem->GetFileBuffer(theItem) +
302 (theItem->mWriteToFirstBuffer ? 0 : theItem->mChunkSize));
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);
312 if (dataChunkSize != theItem->mChunkSize)
314 writePtr += dataChunkSize;
316 /* can't exit yet.. we still have to pass the partial buffer back */
317 SDL_memset(writePtr, 0, (theItem->mChunkSize - dataChunkSize));
320 theItem->mWriteToFirstBuffer = !theItem->mWriteToFirstBuffer; /* switch buffers */
322 if (result == eofErr)
323 theItem->mReadFilePosition = theItem->mFileLength;
325 theItem->mReadFilePosition += dataChunkSize; /* increment count */
329 void delete_FileReaderThread(FileReaderThread *frt)
333 delete_SDLOSXCAGuard(frt->mGuard);
338 FileReaderThread *new_FileReaderThread ()
340 FileReaderThread *frt = (FileReaderThread *) SDL_malloc(sizeof (FileReaderThread));
343 SDL_memset(frt, '\0', sizeof (*frt));
345 frt->mGuard = new_SDLOSXCAGuard();
346 if (frt->mGuard == NULL)
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
363 frt->mThreadPriority = 62;
368 static FileReaderThread *sReaderThread;
371 static int AudioFileManager_DoConnect (AudioFileManager *afm)
373 if (!afm->mIsEngaged)
377 /*afm->mReadFilePosition = 0;*/
378 afm->mFinishedReadingData = 0;
380 afm->mNumTimesAskedSinceFinished = 0;
381 afm->mLockUnsuccessful = 0;
383 ByteCount dataChunkSize;
385 if ((afm->mFileLength - afm->mReadFilePosition) < afm->mChunkSize)
386 dataChunkSize = afm->mFileLength - afm->mReadFilePosition;
388 dataChunkSize = afm->mChunkSize;
390 result = afm->Read(afm, afm->mFileBuffer, &dataChunkSize);
391 if (result) return 0; /*THROW_RESULT("AudioFileManager::DoConnect(): Read")*/
393 afm->mReadFilePosition += dataChunkSize;
395 afm->mWriteToFirstBuffer = 0;
396 afm->mReadFromFirstBuffer = 1;
398 sReaderThread->AddReader(sReaderThread);
404 throw static_cast<OSStatus>(-1); */ /* thread has already been started */
409 static void AudioFileManager_Disconnect (AudioFileManager *afm)
413 sReaderThread->RemoveReader (sReaderThread, afm);
418 static OSStatus AudioFileManager_Read(AudioFileManager *afm, char *buffer, ByteCount *len)
420 return FSReadFork (afm->mForkRefNum,
422 afm->mReadFilePosition + afm->mAudioDataOffset,
428 static OSStatus AudioFileManager_GetFileData (AudioFileManager *afm, void** inOutData, UInt32 *inOutDataSize)
430 if (afm->mFinishedReadingData)
432 ++afm->mNumTimesAskedSinceFinished;
438 if (afm->mReadFromFirstBuffer == afm->mWriteToFirstBuffer) {
440 printf ("* * * * * * * Can't keep up with reading file\n");
443 afm->mParent->DoNotification (afm->mParent, kAudioFilePlayErr_FilePlayUnderrun);
447 *inOutDataSize = afm->mChunkSize;
448 *inOutData = afm->mReadFromFirstBuffer ? afm->mFileBuffer : (afm->mFileBuffer + afm->mChunkSize);
451 afm->mLockUnsuccessful = !sReaderThread->TryNextRead (sReaderThread, afm);
453 afm->mReadFromFirstBuffer = !afm->mReadFromFirstBuffer;
458 static void AudioFileManager_AfterRender (AudioFileManager *afm)
460 if (afm->mNumTimesAskedSinceFinished > 0)
463 SDLOSXCAGuard *guard = sReaderThread->GetGuard(sReaderThread);
464 if (guard->Try(guard, &didLock)) {
465 afm->mParent->DoNotification (afm->mParent, kAudioFilePlay_FileIsFinished);
467 guard->Unlock(guard);
471 if (afm->mLockUnsuccessful)
472 afm->mLockUnsuccessful = !sReaderThread->TryNextRead (sReaderThread, afm);
475 static void AudioFileManager_SetPosition (AudioFileManager *afm, SInt64 pos)
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);
483 afm->mReadFilePosition = pos;
486 static void AudioFileManager_SetEndOfFile (AudioFileManager *afm, SInt64 pos)
488 if (pos <= 0 || pos > afm->mFileLength) {
489 SDL_SetError ("AudioFileManager::SetEndOfFile - position beyond actual eof\n");
490 pos = afm->mFileLength;
493 afm->mFileLength = pos;
496 static const char *AudioFileManager_GetFileBuffer(AudioFileManager *afm)
498 return afm->mFileBuffer;
501 const AudioFilePlayer *AudioFileManager_GetParent(AudioFileManager *afm)
506 static int AudioFileManager_GetByteCounter(AudioFileManager *afm)
508 return afm->mByteCounter;
511 static OSStatus AudioFileManager_FileInputProc (void *inRefCon,
512 AudioUnitRenderActionFlags *ioActionFlags,
513 const AudioTimeStamp *inTimeStamp,
515 UInt32 inNumberFrames,
516 AudioBufferList *ioData)
518 AudioFileManager* afm = (AudioFileManager*)inRefCon;
519 return afm->Render(afm, ioData);
522 static OSStatus AudioFileManager_Render (AudioFileManager *afm, AudioBufferList *ioData)
524 OSStatus result = noErr;
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);
533 SDL_SetError ("AudioConverterFillBuffer:%ld\n", result);
534 afm->mParent->DoNotification(afm->mParent, result);
538 afm->mBufferOffset = 0;
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;
546 afm->mByteCounter += abuf->mDataByteSize;
547 afm->AfterRender(afm);
553 void delete_AudioFileManager (AudioFileManager *afm)
556 if (afm->mFileBuffer) {
557 free(afm->mFileBuffer);
565 AudioFileManager *new_AudioFileManager(AudioFilePlayer *inParent,
570 AudioFileManager *afm;
572 if (sReaderThread == NULL)
574 sReaderThread = new_FileReaderThread();
575 if (sReaderThread == NULL)
579 afm = (AudioFileManager *) SDL_malloc(sizeof (AudioFileManager));
582 SDL_memset(afm, '\0', sizeof (*afm));
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
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);