comparison src/cdrom/macosx/AudioFileReaderThread.c @ 1143:71a2648acc75

Replaced Mac OS X's C++ cdrom code with almost-direct translation to C. Sam requested this effort on the mailing list, apparently because of binary compatibility issues between 10.4 and earlier systems (or gcc4 and earlier compilers?). Works fine with SDL12/test/testcdrom.c, with this command line: ./testcdrom -status -list -play -sleep 5000 -pause -sleep 3000 -resume \ -sleep 5000 -stop -sleep 3000 -play -sleep 3000 -stop \ -sleep 3000 -eject Unix Makefiles work, XCode project still need updating for new filenames.
author Ryan C. Gordon <icculus@icculus.org>
date Thu, 22 Sep 2005 08:48:16 +0000
parents
children 3692456e7b0f
comparison
equal deleted inserted replaced
1142:c7376efecdb5 1143:71a2648acc75
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@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
26 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
27 // AudioFileManager.cpp
28 //
29 #include "AudioFilePlayer.h"
30 #include <mach/mach.h> //used for setting policy of thread
31 #include "SDLOSXCAGuard.h"
32 #include <pthread.h>
33
34 //#include <list>
35
36 //typedef void *FileData;
37 typedef struct S_FileData
38 {
39 AudioFileManager *obj;
40 struct S_FileData *next;
41 } FileData;
42
43
44 typedef struct S_FileReaderThread {
45 //public:
46 SDLOSXCAGuard* (*GetGuard)(struct S_FileReaderThread *frt);
47 void (*AddReader)(struct S_FileReaderThread *frt);
48 void (*RemoveReader)(struct S_FileReaderThread *frt, AudioFileManager* inItem);
49 int (*TryNextRead)(struct S_FileReaderThread *frt, AudioFileManager* inItem);
50
51 int mThreadShouldDie;
52
53 //private:
54 //typedef std::list<AudioFileManager*> FileData;
55
56 SDLOSXCAGuard *mGuard;
57 UInt32 mThreadPriority;
58
59 int mNumReaders;
60 FileData *mFileData;
61
62
63 void (*ReadNextChunk)(struct S_FileReaderThread *frt);
64 int (*StartFixedPriorityThread)(struct S_FileReaderThread *frt);
65 //static
66 UInt32 (*GetThreadBasePriority)(pthread_t inThread);
67 //static
68 void* (*DiskReaderEntry)(void *inRefCon);
69 } FileReaderThread;
70
71
72 static SDLOSXCAGuard* FileReaderThread_GetGuard(FileReaderThread *frt)
73 {
74 return frt->mGuard;
75 }
76
77 // returns 1 if succeeded
78 static int FileReaderThread_TryNextRead (FileReaderThread *frt, AudioFileManager* inItem)
79 {
80 int didLock = 0;
81 int succeeded = 0;
82 if (frt->mGuard->Try(frt->mGuard, &didLock))
83 {
84 //frt->mFileData.push_back (inItem);
85 // !!! FIXME: this could be faster with a "tail" member. --ryan.
86 FileData *i = frt->mFileData;
87 FileData *prev = NULL;
88
89 FileData *newfd = (FileData *) malloc(sizeof (FileData));
90 newfd->obj = inItem;
91 newfd->next = NULL;
92
93 while (i != NULL) { prev = i; i = i->next; }
94 if (prev == NULL)
95 frt->mFileData = newfd;
96 else
97 prev->next = newfd;
98
99 frt->mGuard->Notify(frt->mGuard);
100 succeeded = 1;
101
102 if (didLock)
103 frt->mGuard->Unlock(frt->mGuard);
104 }
105
106 return succeeded;
107 }
108
109 static void FileReaderThread_AddReader(FileReaderThread *frt)
110 {
111 if (frt->mNumReaders == 0)
112 {
113 frt->mThreadShouldDie = 0;
114 frt->StartFixedPriorityThread (frt);
115 }
116 frt->mNumReaders++;
117 }
118
119 static void FileReaderThread_RemoveReader (FileReaderThread *frt, AudioFileManager* inItem)
120 {
121 if (frt->mNumReaders > 0)
122 {
123 int bNeedsRelease = frt->mGuard->Lock(frt->mGuard);
124
125 //frt->mFileData.remove (inItem);
126 FileData *i = frt->mFileData;
127 FileData *prev = NULL;
128 while (i != NULL)
129 {
130 FileData *next = i->next;
131 if (i->obj != inItem)
132 prev = i;
133 else
134 {
135 if (prev == NULL)
136 frt->mFileData = next;
137 else
138 prev->next = next;
139 free(i);
140 }
141 i = next;
142 }
143
144 if (--frt->mNumReaders == 0) {
145 frt->mThreadShouldDie = 1;
146 frt->mGuard->Notify(frt->mGuard); // wake up thread so it will quit
147 frt->mGuard->Wait(frt->mGuard); // wait for thread to die
148 }
149
150 if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
151 }
152 }
153
154 static int FileReaderThread_StartFixedPriorityThread (FileReaderThread *frt)
155 {
156 pthread_attr_t theThreadAttrs;
157 pthread_t pThread;
158
159 OSStatus result = pthread_attr_init(&theThreadAttrs);
160 if (result) return 0; //THROW_RESULT("pthread_attr_init - Thread attributes could not be created.")
161
162 result = pthread_attr_setdetachstate(&theThreadAttrs, PTHREAD_CREATE_DETACHED);
163 if (result) return 0; //THROW_RESULT("pthread_attr_setdetachstate - Thread attributes could not be detached.")
164
165 result = pthread_create (&pThread, &theThreadAttrs, frt->DiskReaderEntry, frt);
166 if (result) return 0; //THROW_RESULT("pthread_create - Create and start the thread.")
167
168 pthread_attr_destroy(&theThreadAttrs);
169
170 // we've now created the thread and started it
171 // we'll now set the priority of the thread to the nominated priority
172 // and we'll also make the thread fixed
173 thread_extended_policy_data_t theFixedPolicy;
174 thread_precedence_policy_data_t thePrecedencePolicy;
175 SInt32 relativePriority;
176
177 // make thread fixed
178 theFixedPolicy.timeshare = 0; // set to 1 for a non-fixed thread
179 result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_EXTENDED_POLICY, (thread_policy_t)&theFixedPolicy, THREAD_EXTENDED_POLICY_COUNT);
180 if (result) return 0; //THROW_RESULT("thread_policy - Couldn't set thread as fixed priority.")
181 // set priority
182 // precedency policy's "importance" value is relative to spawning thread's priority
183 relativePriority = frt->mThreadPriority - frt->GetThreadBasePriority(pthread_self());
184
185 thePrecedencePolicy.importance = relativePriority;
186 result = thread_policy_set (pthread_mach_thread_np(pThread), THREAD_PRECEDENCE_POLICY, (thread_policy_t)&thePrecedencePolicy, THREAD_PRECEDENCE_POLICY_COUNT);
187 if (result) return 0; //THROW_RESULT("thread_policy - Couldn't set thread priority.")
188
189 return 1;
190 }
191
192 static UInt32 FileReaderThread_GetThreadBasePriority (pthread_t inThread)
193 {
194 thread_basic_info_data_t threadInfo;
195 policy_info_data_t thePolicyInfo;
196 unsigned int count;
197
198 // get basic info
199 count = THREAD_BASIC_INFO_COUNT;
200 thread_info (pthread_mach_thread_np (inThread), THREAD_BASIC_INFO, (integer_t*)&threadInfo, &count);
201
202 switch (threadInfo.policy) {
203 case POLICY_TIMESHARE:
204 count = POLICY_TIMESHARE_INFO_COUNT;
205 thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_TIMESHARE_INFO, (integer_t*)&(thePolicyInfo.ts), &count);
206 return thePolicyInfo.ts.base_priority;
207 break;
208
209 case POLICY_FIFO:
210 count = POLICY_FIFO_INFO_COUNT;
211 thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_FIFO_INFO, (integer_t*)&(thePolicyInfo.fifo), &count);
212 if (thePolicyInfo.fifo.depressed) {
213 return thePolicyInfo.fifo.depress_priority;
214 } else {
215 return thePolicyInfo.fifo.base_priority;
216 }
217 break;
218
219 case POLICY_RR:
220 count = POLICY_RR_INFO_COUNT;
221 thread_info(pthread_mach_thread_np (inThread), THREAD_SCHED_RR_INFO, (integer_t*)&(thePolicyInfo.rr), &count);
222 if (thePolicyInfo.rr.depressed) {
223 return thePolicyInfo.rr.depress_priority;
224 } else {
225 return thePolicyInfo.rr.base_priority;
226 }
227 break;
228 }
229
230 return 0;
231 }
232
233 static void *FileReaderThread_DiskReaderEntry (void *inRefCon)
234 {
235 FileReaderThread *frt = (FileReaderThread *)inRefCon;
236 frt->ReadNextChunk(frt);
237 #if DEBUG
238 printf ("finished with reading file\n");
239 #endif
240
241 return 0;
242 }
243
244 static void FileReaderThread_ReadNextChunk (FileReaderThread *frt)
245 {
246 OSStatus result;
247 UInt32 dataChunkSize;
248 AudioFileManager* theItem = 0;
249
250 for (;;)
251 {
252 { // this is a scoped based lock
253 int bNeedsRelease = frt->mGuard->Lock(frt->mGuard);
254
255 if (frt->mThreadShouldDie) {
256 frt->mGuard->Notify(frt->mGuard);
257 if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
258 return;
259 }
260
261 //if (frt->mFileData.empty())
262 if (frt->mFileData == NULL)
263 {
264 frt->mGuard->Wait(frt->mGuard);
265 }
266
267 // kill thread
268 if (frt->mThreadShouldDie) {
269
270 frt->mGuard->Notify(frt->mGuard);
271 if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
272 return;
273 }
274
275 //theItem = frt->mFileData.front();
276 //frt->mFileData.pop_front();
277 theItem = NULL;
278 if (frt->mFileData != NULL)
279 {
280 FileData *next = frt->mFileData->next;
281 theItem = frt->mFileData->obj;
282 free(frt->mFileData);
283 frt->mFileData = next;
284 }
285
286 if (bNeedsRelease) frt->mGuard->Unlock(frt->mGuard);
287 }
288
289 if ((theItem->mFileLength - theItem->mReadFilePosition) < theItem->mChunkSize)
290 dataChunkSize = theItem->mFileLength - theItem->mReadFilePosition;
291 else
292 dataChunkSize = theItem->mChunkSize;
293
294 // this is the exit condition for the thread
295 if (dataChunkSize <= 0) {
296 theItem->mFinishedReadingData = 1;
297 continue;
298 }
299 // construct pointer
300 char* writePtr = (char *) (theItem->GetFileBuffer(theItem) +
301 (theItem->mWriteToFirstBuffer ? 0 : theItem->mChunkSize));
302
303 // read data
304 result = theItem->Read(theItem, writePtr, &dataChunkSize);
305 if (result != noErr && result != eofErr) {
306 AudioFilePlayer *afp = (AudioFilePlayer *) theItem->GetParent(theItem);
307 afp->DoNotification(afp, result);
308 continue;
309 }
310
311 if (dataChunkSize != theItem->mChunkSize)
312 {
313 writePtr += dataChunkSize;
314
315 // can't exit yet.. we still have to pass the partial buffer back
316 memset (writePtr, 0, (theItem->mChunkSize - dataChunkSize));
317 }
318
319 theItem->mWriteToFirstBuffer = !theItem->mWriteToFirstBuffer; // switch buffers
320
321 if (result == eofErr)
322 theItem->mReadFilePosition = theItem->mFileLength;
323 else
324 theItem->mReadFilePosition += dataChunkSize; // increment count
325 }
326 }
327
328 void delete_FileReaderThread(FileReaderThread *frt)
329 {
330 if (frt != NULL)
331 {
332 delete_SDLOSXCAGuard(frt->mGuard);
333 free(frt);
334 }
335 }
336
337 FileReaderThread *new_FileReaderThread ()
338 {
339 FileReaderThread *frt = (FileReaderThread *) malloc(sizeof (FileReaderThread));
340 if (frt == NULL)
341 return NULL;
342 memset(frt, '\0', sizeof (*frt));
343
344 frt->mGuard = new_SDLOSXCAGuard();
345 if (frt->mGuard == NULL)
346 {
347 free(frt);
348 return NULL;
349 }
350
351 #define SET_FILEREADERTHREAD_METHOD(m) frt->m = FileReaderThread_##m
352 SET_FILEREADERTHREAD_METHOD(GetGuard);
353 SET_FILEREADERTHREAD_METHOD(AddReader);
354 SET_FILEREADERTHREAD_METHOD(RemoveReader);
355 SET_FILEREADERTHREAD_METHOD(TryNextRead);
356 SET_FILEREADERTHREAD_METHOD(ReadNextChunk);
357 SET_FILEREADERTHREAD_METHOD(StartFixedPriorityThread);
358 SET_FILEREADERTHREAD_METHOD(GetThreadBasePriority);
359 SET_FILEREADERTHREAD_METHOD(DiskReaderEntry);
360 #undef SET_FILEREADERTHREAD_METHOD
361
362 frt->mThreadPriority = 62;
363 return frt;
364 }
365
366
367 static FileReaderThread *sReaderThread;
368
369
370 static int AudioFileManager_DoConnect (AudioFileManager *afm)
371 {
372 if (!afm->mIsEngaged)
373 {
374 //afm->mReadFilePosition = 0;
375 afm->mFinishedReadingData = 0;
376
377 afm->mNumTimesAskedSinceFinished = 0;
378 afm->mLockUnsuccessful = 0;
379
380 OSStatus result;
381 UInt32 dataChunkSize;
382
383 if ((afm->mFileLength - afm->mReadFilePosition) < afm->mChunkSize)
384 dataChunkSize = afm->mFileLength - afm->mReadFilePosition;
385 else
386 dataChunkSize = afm->mChunkSize;
387
388 result = afm->Read(afm, afm->mFileBuffer, &dataChunkSize);
389 if (result) return 0; //THROW_RESULT("AudioFileManager::DoConnect(): Read")
390
391 afm->mReadFilePosition += dataChunkSize;
392
393 afm->mWriteToFirstBuffer = 0;
394 afm->mReadFromFirstBuffer = 1;
395
396 sReaderThread->AddReader(sReaderThread);
397
398 afm->mIsEngaged = 1;
399 }
400 //else
401 // throw static_cast<OSStatus>(-1); //thread has already been started
402
403 return 1;
404 }
405
406 static void AudioFileManager_Disconnect (AudioFileManager *afm)
407 {
408 if (afm->mIsEngaged)
409 {
410 sReaderThread->RemoveReader (sReaderThread, afm);
411 afm->mIsEngaged = 0;
412 }
413 }
414
415 static OSStatus AudioFileManager_Read(AudioFileManager *afm, char *buffer, UInt32 *len)
416 {
417 return FSReadFork (afm->mForkRefNum,
418 fsFromStart,
419 afm->mReadFilePosition + afm->mAudioDataOffset,
420 *len,
421 buffer,
422 len);
423 }
424
425 static OSStatus AudioFileManager_GetFileData (AudioFileManager *afm, void** inOutData, UInt32 *inOutDataSize)
426 {
427 if (afm->mFinishedReadingData)
428 {
429 ++afm->mNumTimesAskedSinceFinished;
430 *inOutDataSize = 0;
431 *inOutData = 0;
432 return noErr;
433 }
434
435 if (afm->mReadFromFirstBuffer == afm->mWriteToFirstBuffer) {
436 #if DEBUG
437 printf ("* * * * * * * Can't keep up with reading file\n");
438 #endif
439
440 afm->mParent->DoNotification (afm->mParent, kAudioFilePlayErr_FilePlayUnderrun);
441 *inOutDataSize = 0;
442 *inOutData = 0;
443 } else {
444 *inOutDataSize = afm->mChunkSize;
445 *inOutData = afm->mReadFromFirstBuffer ? afm->mFileBuffer : (afm->mFileBuffer + afm->mChunkSize);
446 }
447
448 afm->mLockUnsuccessful = !sReaderThread->TryNextRead (sReaderThread, afm);
449
450 afm->mReadFromFirstBuffer = !afm->mReadFromFirstBuffer;
451
452 return noErr;
453 }
454
455 static void AudioFileManager_AfterRender (AudioFileManager *afm)
456 {
457 if (afm->mNumTimesAskedSinceFinished > 0)
458 {
459 int didLock = 0;
460 SDLOSXCAGuard *guard = sReaderThread->GetGuard(sReaderThread);
461 if (guard->Try(guard, &didLock)) {
462 afm->mParent->DoNotification (afm->mParent, kAudioFilePlay_FileIsFinished);
463 if (didLock)
464 guard->Unlock(guard);
465 }
466 }
467
468 if (afm->mLockUnsuccessful)
469 afm->mLockUnsuccessful = !sReaderThread->TryNextRead (sReaderThread, afm);
470 }
471
472 static void AudioFileManager_SetPosition (AudioFileManager *afm, SInt64 pos)
473 {
474 if (pos < 0 || pos >= afm->mFileLength) {
475 SDL_SetError ("AudioFileManager::SetPosition - position invalid: %d filelen=%d\n",
476 (unsigned int)pos, (unsigned int)afm->mFileLength);
477 pos = 0;
478 }
479
480 afm->mReadFilePosition = pos;
481 }
482
483 static void AudioFileManager_SetEndOfFile (AudioFileManager *afm, SInt64 pos)
484 {
485 if (pos <= 0 || pos > afm->mFileLength) {
486 SDL_SetError ("AudioFileManager::SetEndOfFile - position beyond actual eof\n");
487 pos = afm->mFileLength;
488 }
489
490 afm->mFileLength = pos;
491 }
492
493 static const char *AudioFileManager_GetFileBuffer(AudioFileManager *afm)
494 {
495 return afm->mFileBuffer;
496 }
497
498 const AudioFilePlayer *AudioFileManager_GetParent(AudioFileManager *afm)
499 {
500 return afm->mParent;
501 }
502
503 static int AudioFileManager_GetByteCounter(AudioFileManager *afm)
504 {
505 return afm->mByteCounter;
506 }
507
508
509 static OSStatus AudioFileManager_FileInputProc (void *inRefCon,
510 AudioUnitRenderActionFlags inActionFlags,
511 const AudioTimeStamp *inTimeStamp,
512 UInt32 inBusNumber,
513 AudioBuffer *ioData)
514 {
515 AudioFileManager* afm = (AudioFileManager*)inRefCon;
516 return afm->Render(afm, ioData);
517 }
518
519 static OSStatus AudioFileManager_Render (AudioFileManager *afm, AudioBuffer *ioData)
520 {
521 OSStatus result = noErr;
522
523 if (afm->mBufferOffset >= afm->mBufferSize) {
524 result = afm->GetFileData(afm, &afm->mTmpBuffer, &afm->mBufferSize);
525 if (result) {
526 SDL_SetError ("AudioConverterFillBuffer:%ld\n", result);
527 afm->mParent->DoNotification(afm->mParent, result);
528 return result;
529 }
530
531 afm->mBufferOffset = 0;
532 }
533
534 if (ioData->mDataByteSize > afm->mBufferSize - afm->mBufferOffset)
535 ioData->mDataByteSize = afm->mBufferSize - afm->mBufferOffset;
536 ioData->mData = (char *)afm->mTmpBuffer + afm->mBufferOffset;
537 afm->mBufferOffset += ioData->mDataByteSize;
538
539 afm->mByteCounter += ioData->mDataByteSize;
540 afm->AfterRender(afm);
541 return result;
542 }
543
544
545 void delete_AudioFileManager (AudioFileManager *afm)
546 {
547 if (afm != NULL) {
548 if (afm->mFileBuffer) {
549 free (afm->mFileBuffer);
550 }
551
552 free(afm);
553 }
554 }
555
556
557 AudioFileManager *new_AudioFileManager(AudioFilePlayer *inParent,
558 SInt16 inForkRefNum,
559 SInt64 inFileLength,
560 UInt32 inChunkSize)
561 {
562 AudioFileManager *afm;
563
564 if (sReaderThread == NULL)
565 {
566 sReaderThread = new_FileReaderThread();
567 if (sReaderThread == NULL)
568 return NULL;
569 }
570
571 afm = (AudioFileManager *) malloc(sizeof (AudioFileManager));
572 if (afm == NULL)
573 return NULL;
574 memset(afm, '\0', sizeof (*afm));
575
576 #define SET_AUDIOFILEMANAGER_METHOD(m) afm->m = AudioFileManager_##m
577 SET_AUDIOFILEMANAGER_METHOD(Disconnect);
578 SET_AUDIOFILEMANAGER_METHOD(DoConnect);
579 SET_AUDIOFILEMANAGER_METHOD(Read);
580 SET_AUDIOFILEMANAGER_METHOD(GetFileBuffer);
581 SET_AUDIOFILEMANAGER_METHOD(GetParent);
582 SET_AUDIOFILEMANAGER_METHOD(SetPosition);
583 SET_AUDIOFILEMANAGER_METHOD(GetByteCounter);
584 SET_AUDIOFILEMANAGER_METHOD(SetEndOfFile);
585 SET_AUDIOFILEMANAGER_METHOD(Render);
586 SET_AUDIOFILEMANAGER_METHOD(GetFileData);
587 SET_AUDIOFILEMANAGER_METHOD(AfterRender);
588 SET_AUDIOFILEMANAGER_METHOD(FileInputProc);
589 #undef SET_AUDIOFILEMANAGER_METHOD
590
591 afm->mParent = inParent;
592 afm->mForkRefNum = inForkRefNum;
593 afm->mBufferSize = inChunkSize;
594 afm->mBufferOffset = inChunkSize;
595 afm->mChunkSize = inChunkSize;
596 afm->mFileLength = inFileLength;
597 afm->mFileBuffer = (char*) malloc (afm->mChunkSize * 2);
598 FSGetForkPosition(afm->mForkRefNum, &afm->mAudioDataOffset);
599 assert (afm->mFileBuffer != NULL);
600 return afm;
601 }
602