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