1 /* 2 SDL - Simple DirectMedia Layer 3 Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 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 UInt32 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 UInt32 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, UInt32 *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 512 static OSStatus AudioFileManager_FileInputProc (void *inRefCon, 513 AudioUnitRenderActionFlags inActionFlags, 514 const AudioTimeStamp *inTimeStamp, 515 UInt32 inBusNumber, 516 AudioBuffer *ioData) 517 { 518 AudioFileManager* afm = (AudioFileManager*)inRefCon; 519 return afm->Render(afm, ioData); 520 } 521 522 static OSStatus AudioFileManager_Render (AudioFileManager *afm, AudioBuffer *ioData) 523 { 524 OSStatus result = noErr; 525 526 if (afm->mBufferOffset >= afm->mBufferSize) { 527 result = afm->GetFileData(afm, &afm->mTmpBuffer, &afm->mBufferSize); 528 if (result) { 529 SDL_SetError ("AudioConverterFillBuffer:%ld\n", result); 530 afm->mParent->DoNotification(afm->mParent, result); 531 return result; 532 } 533 534 afm->mBufferOffset = 0; 535 } 536 537 if (ioData->mDataByteSize > afm->mBufferSize - afm->mBufferOffset) 538 ioData->mDataByteSize = afm->mBufferSize - afm->mBufferOffset; 539 ioData->mData = (char *)afm->mTmpBuffer + afm->mBufferOffset; 540 afm->mBufferOffset += ioData->mDataByteSize; 541 542 afm->mByteCounter += ioData->mDataByteSize; 543 afm->AfterRender(afm); 544 return result; 545 } 546 547 548 void delete_AudioFileManager (AudioFileManager *afm) 549 { 550 if (afm != NULL) { 551 if (afm->mFileBuffer) { 552 free(afm->mFileBuffer); 553 } 554 555 SDL_free(afm); 556 } 557 } 558 559 560 AudioFileManager *new_AudioFileManager(AudioFilePlayer *inParent, 561 SInt16 inForkRefNum, 562 SInt64 inFileLength, 563 UInt32 inChunkSize) 564 { 565 AudioFileManager *afm; 566 567 if (sReaderThread == NULL) 568 { 569 sReaderThread = new_FileReaderThread(); 570 if (sReaderThread == NULL) 571 return NULL; 572 } 573 574 afm = (AudioFileManager *) SDL_malloc(sizeof (AudioFileManager)); 575 if (afm == NULL) 576 return NULL; 577 SDL_memset(afm, '\0', sizeof (*afm)); 578 579 #define SET_AUDIOFILEMANAGER_METHOD(m) afm->m = AudioFileManager_##m 580 SET_AUDIOFILEMANAGER_METHOD(Disconnect); 581 SET_AUDIOFILEMANAGER_METHOD(DoConnect); 582 SET_AUDIOFILEMANAGER_METHOD(Read); 583 SET_AUDIOFILEMANAGER_METHOD(GetFileBuffer); 584 SET_AUDIOFILEMANAGER_METHOD(GetParent); 585 SET_AUDIOFILEMANAGER_METHOD(SetPosition); 586 SET_AUDIOFILEMANAGER_METHOD(GetByteCounter); 587 SET_AUDIOFILEMANAGER_METHOD(SetEndOfFile); 588 SET_AUDIOFILEMANAGER_METHOD(Render); 589 SET_AUDIOFILEMANAGER_METHOD(GetFileData); 590 SET_AUDIOFILEMANAGER_METHOD(AfterRender); 591 SET_AUDIOFILEMANAGER_METHOD(FileInputProc); 592 #undef SET_AUDIOFILEMANAGER_METHOD 593 594 afm->mParent = inParent; 595 afm->mForkRefNum = inForkRefNum; 596 afm->mBufferSize = inChunkSize; 597 afm->mBufferOffset = inChunkSize; 598 afm->mChunkSize = inChunkSize; 599 afm->mFileLength = inFileLength; 600 afm->mFileBuffer = (char*) SDL_malloc(afm->mChunkSize * 2); 601 FSGetForkPosition(afm->mForkRefNum, &afm->mAudioDataOffset); 602 assert (afm->mFileBuffer != NULL); 603 return afm; 604 } 605 606