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@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 611