This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
AudioFileReaderThread.c
660 lines (547 loc) · 19.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
SDL - Simple DirectMedia Layer
Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Sam Lantinga
slouken@libsdl.org
This file based on Apple sample code. We haven't changed the file name,
so if you want to see the original search for it on apple.com/developer
*/
25
#include "SDL_config.h"
26
27
28
29
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
AudioFileManager.cpp
*/
30
#include "AudioFilePlayer.h"
31
#include <mach/mach.h> /* used for setting policy of thread */
32
33
34
#include "SDLOSXCAGuard.h"
#include <pthread.h>
35
/*#include <list>*/
36
37
/*typedef void *FileData;*/
38
39
40
41
42
43
44
typedef struct S_FileData
{
AudioFileManager *obj;
struct S_FileData *next;
} FileData;
45
46
typedef struct S_FileReaderThread
{
47
/*public:*/
48
49
50
51
52
53
54
55
SDLOSXCAGuard *(*GetGuard) (struct S_FileReaderThread * frt);
void (*AddReader) (struct S_FileReaderThread * frt);
void (*RemoveReader) (struct S_FileReaderThread * frt,
AudioFileManager * inItem);
int (*TryNextRead) (struct S_FileReaderThread * frt,
AudioFileManager * inItem);
int mThreadShouldDie;
56
57
/*private:*/
58
/*typedef std::list<AudioFileManager*> FileData; */
59
60
61
SDLOSXCAGuard *mGuard;
UInt32 mThreadPriority;
62
63
64
int mNumReaders;
FileData *mFileData;
65
66
67
68
69
70
71
72
void (*ReadNextChunk) (struct S_FileReaderThread * frt);
int (*StartFixedPriorityThread) (struct S_FileReaderThread * frt);
/*static */
UInt32 (*GetThreadBasePriority) (pthread_t inThread);
/*static */
void *(*DiskReaderEntry) (void *inRefCon);
73
74
75
} FileReaderThread;
76
77
static SDLOSXCAGuard *
FileReaderThread_GetGuard (FileReaderThread * frt)
78
79
80
81
{
return frt->mGuard;
}
82
/* returns 1 if succeeded */
83
84
85
static int
FileReaderThread_TryNextRead (FileReaderThread * frt,
AudioFileManager * inItem)
86
87
88
{
int didLock = 0;
int succeeded = 0;
89
90
if (frt->mGuard->Try (frt->mGuard, &didLock)) {
/*frt->mFileData.push_back (inItem); */
91
/* !!! FIXME: this could be faster with a "tail" member. --ryan. */
92
93
94
FileData *i = frt->mFileData;
FileData *prev = NULL;
95
FileData *newfd = (FileData *) SDL_malloc (sizeof (FileData));
96
97
98
newfd->obj = inItem;
newfd->next = NULL;
99
100
101
102
while (i != NULL) {
prev = i;
i = i->next;
}
103
104
105
106
107
if (prev == NULL)
frt->mFileData = newfd;
else
prev->next = newfd;
108
frt->mGuard->Notify (frt->mGuard);
109
110
111
succeeded = 1;
if (didLock)
112
frt->mGuard->Unlock (frt->mGuard);
113
}
114
115
116
117
return succeeded;
}
118
119
static void
FileReaderThread_AddReader (FileReaderThread * frt)
120
{
121
if (frt->mNumReaders == 0) {
122
123
124
125
126
127
frt->mThreadShouldDie = 0;
frt->StartFixedPriorityThread (frt);
}
frt->mNumReaders++;
}
128
129
130
static void
FileReaderThread_RemoveReader (FileReaderThread * frt,
AudioFileManager * inItem)
131
{
132
133
134
135
if (frt->mNumReaders > 0) {
int bNeedsRelease = frt->mGuard->Lock (frt->mGuard);
/*frt->mFileData.remove (inItem); */
136
137
FileData *i = frt->mFileData;
FileData *prev = NULL;
138
while (i != NULL) {
139
140
141
FileData *next = i->next;
if (i->obj != inItem)
prev = i;
142
else {
143
144
145
146
if (prev == NULL)
frt->mFileData = next;
else
prev->next = next;
147
SDL_free (i);
148
149
150
151
152
153
}
i = next;
}
if (--frt->mNumReaders == 0) {
frt->mThreadShouldDie = 1;
154
155
frt->mGuard->Notify (frt->mGuard); /* wake up thread so it will quit */
frt->mGuard->Wait (frt->mGuard); /* wait for thread to die */
156
157
}
158
159
160
if (bNeedsRelease)
frt->mGuard->Unlock (frt->mGuard);
}
161
162
}
163
164
static int
FileReaderThread_StartFixedPriorityThread (FileReaderThread * frt)
165
{
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
pthread_attr_t theThreadAttrs;
pthread_t pThread;
OSStatus result = pthread_attr_init (&theThreadAttrs);
if (result)
return 0; /*THROW_RESULT("pthread_attr_init - Thread attributes could not be created.") */
result =
pthread_attr_setdetachstate (&theThreadAttrs,
PTHREAD_CREATE_DETACHED);
if (result)
return 0; /*THROW_RESULT("pthread_attr_setdetachstate - Thread attributes could not be detached.") */
result =
pthread_create (&pThread, &theThreadAttrs, frt->DiskReaderEntry, frt);
if (result)
return 0; /*THROW_RESULT("pthread_create - Create and start the thread.") */
pthread_attr_destroy (&theThreadAttrs);
186
187
188
/* we've now created the thread and started it
we'll now set the priority of the thread to the nominated priority
and we'll also make the thread fixed */
189
190
191
192
thread_extended_policy_data_t theFixedPolicy;
thread_precedence_policy_data_t thePrecedencePolicy;
SInt32 relativePriority;
193
/* make thread fixed */
194
195
196
197
198
199
200
201
theFixedPolicy.timeshare = 0; /* set to 1 for a non-fixed thread */
result =
thread_policy_set (pthread_mach_thread_np (pThread),
THREAD_EXTENDED_POLICY,
(thread_policy_t) & theFixedPolicy,
THREAD_EXTENDED_POLICY_COUNT);
if (result)
return 0; /*THROW_RESULT("thread_policy - Couldn't set thread as fixed priority.") */
202
203
/* set priority */
/* precedency policy's "importance" value is relative to spawning thread's priority */
204
205
206
relativePriority =
frt->mThreadPriority - frt->GetThreadBasePriority (pthread_self ());
207
thePrecedencePolicy.importance = relativePriority;
208
209
210
211
212
213
214
result =
thread_policy_set (pthread_mach_thread_np (pThread),
THREAD_PRECEDENCE_POLICY,
(thread_policy_t) & thePrecedencePolicy,
THREAD_PRECEDENCE_POLICY_COUNT);
if (result)
return 0; /*THROW_RESULT("thread_policy - Couldn't set thread priority.") */
215
216
217
218
return 1;
}
219
220
static UInt32
FileReaderThread_GetThreadBasePriority (pthread_t inThread)
221
{
222
223
224
225
thread_basic_info_data_t threadInfo;
policy_info_data_t thePolicyInfo;
unsigned int count;
226
/* get basic info */
227
count = THREAD_BASIC_INFO_COUNT;
228
229
230
thread_info (pthread_mach_thread_np (inThread), THREAD_BASIC_INFO,
(integer_t *) & threadInfo, &count);
231
switch (threadInfo.policy) {
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
case POLICY_TIMESHARE:
count = POLICY_TIMESHARE_INFO_COUNT;
thread_info (pthread_mach_thread_np (inThread),
THREAD_SCHED_TIMESHARE_INFO,
(integer_t *) & (thePolicyInfo.ts), &count);
return thePolicyInfo.ts.base_priority;
break;
case POLICY_FIFO:
count = POLICY_FIFO_INFO_COUNT;
thread_info (pthread_mach_thread_np (inThread),
THREAD_SCHED_FIFO_INFO,
(integer_t *) & (thePolicyInfo.fifo), &count);
if (thePolicyInfo.fifo.depressed) {
return thePolicyInfo.fifo.depress_priority;
} else {
return thePolicyInfo.fifo.base_priority;
}
break;
case POLICY_RR:
count = POLICY_RR_INFO_COUNT;
thread_info (pthread_mach_thread_np (inThread),
THREAD_SCHED_RR_INFO,
(integer_t *) & (thePolicyInfo.rr), &count);
if (thePolicyInfo.rr.depressed) {
return thePolicyInfo.rr.depress_priority;
} else {
return thePolicyInfo.rr.base_priority;
}
break;
263
}
264
265
266
267
return 0;
}
268
269
static void *
FileReaderThread_DiskReaderEntry (void *inRefCon)
270
{
271
272
273
FileReaderThread *frt = (FileReaderThread *) inRefCon;
frt->ReadNextChunk (frt);
#if DEBUG
274
printf ("finished with reading file\n");
275
276
#endif
277
278
279
return 0;
}
280
281
static void
FileReaderThread_ReadNextChunk (FileReaderThread * frt)
282
283
{
OSStatus result;
284
285
286
287
288
289
290
UInt32 dataChunkSize;
AudioFileManager *theItem = 0;
for (;;) {
{ /* this is a scoped based lock */
int bNeedsRelease = frt->mGuard->Lock (frt->mGuard);
291
if (frt->mThreadShouldDie) {
292
293
294
frt->mGuard->Notify (frt->mGuard);
if (bNeedsRelease)
frt->mGuard->Unlock (frt->mGuard);
295
296
return;
}
297
298
299
300
/*if (frt->mFileData.empty()) */
if (frt->mFileData == NULL) {
frt->mGuard->Wait (frt->mGuard);
301
}
302
303
/* kill thread */
304
if (frt->mThreadShouldDie) {
305
306
307
308
frt->mGuard->Notify (frt->mGuard);
if (bNeedsRelease)
frt->mGuard->Unlock (frt->mGuard);
309
310
311
return;
}
312
313
/*theItem = frt->mFileData.front(); */
/*frt->mFileData.pop_front(); */
314
theItem = NULL;
315
if (frt->mFileData != NULL) {
316
317
FileData *next = frt->mFileData->next;
theItem = frt->mFileData->obj;
318
SDL_free (frt->mFileData);
319
320
321
frt->mFileData = next;
}
322
323
if (bNeedsRelease)
frt->mGuard->Unlock (frt->mGuard);
324
}
325
326
327
if ((theItem->mFileLength - theItem->mReadFilePosition) <
theItem->mChunkSize)
328
329
330
dataChunkSize = theItem->mFileLength - theItem->mReadFilePosition;
else
dataChunkSize = theItem->mChunkSize;
331
332
/* this is the exit condition for the thread */
333
334
335
336
if (dataChunkSize <= 0) {
theItem->mFinishedReadingData = 1;
continue;
}
337
338
339
340
341
342
343
344
/* construct pointer */
char *writePtr = (char *) (theItem->GetFileBuffer (theItem) +
(theItem->
mWriteToFirstBuffer ? 0 : theItem->
mChunkSize));
/* read data */
result = theItem->Read (theItem, writePtr, &dataChunkSize);
345
if (result != noErr && result != eofErr) {
346
347
348
AudioFilePlayer *afp =
(AudioFilePlayer *) theItem->GetParent (theItem);
afp->DoNotification (afp, result);
349
350
continue;
}
351
352
if (dataChunkSize != theItem->mChunkSize) {
353
354
writePtr += dataChunkSize;
355
/* can't exit yet.. we still have to pass the partial buffer back */
356
SDL_memset (writePtr, 0, (theItem->mChunkSize - dataChunkSize));
357
}
358
359
theItem->mWriteToFirstBuffer = !theItem->mWriteToFirstBuffer; /* switch buffers */
360
361
362
363
if (result == eofErr)
theItem->mReadFilePosition = theItem->mFileLength;
else
364
theItem->mReadFilePosition += dataChunkSize; /* increment count */
365
366
367
}
}
368
369
void
delete_FileReaderThread (FileReaderThread * frt)
370
{
371
372
373
if (frt != NULL) {
delete_SDLOSXCAGuard (frt->mGuard);
SDL_free (frt);
374
375
376
}
}
377
378
FileReaderThread *
new_FileReaderThread ()
379
{
380
381
FileReaderThread *frt =
(FileReaderThread *) SDL_malloc (sizeof (FileReaderThread));
382
383
if (frt == NULL)
return NULL;
384
SDL_memset (frt, '\0', sizeof (*frt));
385
386
387
388
frt->mGuard = new_SDLOSXCAGuard ();
if (frt->mGuard == NULL) {
SDL_free (frt);
389
390
return NULL;
}
391
392
393
394
395
396
397
398
399
400
#define SET_FILEREADERTHREAD_METHOD(m) frt->m = FileReaderThread_##m
SET_FILEREADERTHREAD_METHOD (GetGuard);
SET_FILEREADERTHREAD_METHOD (AddReader);
SET_FILEREADERTHREAD_METHOD (RemoveReader);
SET_FILEREADERTHREAD_METHOD (TryNextRead);
SET_FILEREADERTHREAD_METHOD (ReadNextChunk);
SET_FILEREADERTHREAD_METHOD (StartFixedPriorityThread);
SET_FILEREADERTHREAD_METHOD (GetThreadBasePriority);
SET_FILEREADERTHREAD_METHOD (DiskReaderEntry);
#undef SET_FILEREADERTHREAD_METHOD
401
402
403
404
405
406
407
408
409
frt->mThreadPriority = 62;
return frt;
}
static FileReaderThread *sReaderThread;
410
411
static int
AudioFileManager_DoConnect (AudioFileManager * afm)
412
{
413
if (!afm->mIsEngaged) {
414
415
OSStatus result;
416
/*afm->mReadFilePosition = 0; */
417
418
419
420
afm->mFinishedReadingData = 0;
afm->mNumTimesAskedSinceFinished = 0;
afm->mLockUnsuccessful = 0;
421
422
UInt32 dataChunkSize;
423
424
425
426
427
if ((afm->mFileLength - afm->mReadFilePosition) < afm->mChunkSize)
dataChunkSize = afm->mFileLength - afm->mReadFilePosition;
else
dataChunkSize = afm->mChunkSize;
428
429
430
431
result = afm->Read (afm, afm->mFileBuffer, &dataChunkSize);
if (result)
return 0; /*THROW_RESULT("AudioFileManager::DoConnect(): Read") */
432
433
afm->mReadFilePosition += dataChunkSize;
434
435
436
437
afm->mWriteToFirstBuffer = 0;
afm->mReadFromFirstBuffer = 1;
438
439
sReaderThread->AddReader (sReaderThread);
440
441
afm->mIsEngaged = 1;
}
442
/*
443
444
else
throw static_cast<OSStatus>(-1); *//* thread has already been started */
445
446
447
448
return 1;
}
449
450
static void
AudioFileManager_Disconnect (AudioFileManager * afm)
451
{
452
if (afm->mIsEngaged) {
453
454
455
456
457
sReaderThread->RemoveReader (sReaderThread, afm);
afm->mIsEngaged = 0;
}
}
458
459
static OSStatus
AudioFileManager_Read (AudioFileManager * afm, char *buffer, UInt32 * len)
460
461
462
463
{
return FSReadFork (afm->mForkRefNum,
fsFromStart,
afm->mReadFilePosition + afm->mAudioDataOffset,
464
*len, buffer, len);
465
466
}
467
468
469
static OSStatus
AudioFileManager_GetFileData (AudioFileManager * afm, void **inOutData,
UInt32 * inOutDataSize)
470
{
471
if (afm->mFinishedReadingData) {
472
473
474
475
476
++afm->mNumTimesAskedSinceFinished;
*inOutDataSize = 0;
*inOutData = 0;
return noErr;
}
477
478
if (afm->mReadFromFirstBuffer == afm->mWriteToFirstBuffer) {
479
#if DEBUG
480
printf ("* * * * * * * Can't keep up with reading file\n");
481
482
483
484
#endif
afm->mParent->DoNotification (afm->mParent,
kAudioFilePlayErr_FilePlayUnderrun);
485
486
487
488
*inOutDataSize = 0;
*inOutData = 0;
} else {
*inOutDataSize = afm->mChunkSize;
489
490
491
492
*inOutData =
afm->mReadFromFirstBuffer ? afm->mFileBuffer : (afm->
mFileBuffer +
afm->mChunkSize);
493
494
495
}
afm->mLockUnsuccessful = !sReaderThread->TryNextRead (sReaderThread, afm);
496
497
498
499
500
501
afm->mReadFromFirstBuffer = !afm->mReadFromFirstBuffer;
return noErr;
}
502
503
static void
AudioFileManager_AfterRender (AudioFileManager * afm)
504
{
505
if (afm->mNumTimesAskedSinceFinished > 0) {
506
int didLock = 0;
507
508
509
510
SDLOSXCAGuard *guard = sReaderThread->GetGuard (sReaderThread);
if (guard->Try (guard, &didLock)) {
afm->mParent->DoNotification (afm->mParent,
kAudioFilePlay_FileIsFinished);
511
if (didLock)
512
guard->Unlock (guard);
513
514
515
516
}
}
if (afm->mLockUnsuccessful)
517
518
afm->mLockUnsuccessful =
!sReaderThread->TryNextRead (sReaderThread, afm);
519
520
}
521
522
static void
AudioFileManager_SetPosition (AudioFileManager * afm, SInt64 pos)
523
524
{
if (pos < 0 || pos >= afm->mFileLength) {
525
526
527
SDL_SetError
("AudioFileManager::SetPosition - position invalid: %d filelen=%d\n",
(unsigned int) pos, (unsigned int) afm->mFileLength);
528
529
pos = 0;
}
530
531
532
afm->mReadFilePosition = pos;
}
533
534
535
static void
AudioFileManager_SetEndOfFile (AudioFileManager * afm, SInt64 pos)
536
537
{
if (pos <= 0 || pos > afm->mFileLength) {
538
539
SDL_SetError
("AudioFileManager::SetEndOfFile - position beyond actual eof\n");
540
541
pos = afm->mFileLength;
}
542
543
544
545
afm->mFileLength = pos;
}
546
547
static const char *
AudioFileManager_GetFileBuffer (AudioFileManager * afm)
548
549
550
551
{
return afm->mFileBuffer;
}
552
553
const AudioFilePlayer *
AudioFileManager_GetParent (AudioFileManager * afm)
554
555
556
557
{
return afm->mParent;
}
558
559
static int
AudioFileManager_GetByteCounter (AudioFileManager * afm)
560
561
562
563
564
{
return afm->mByteCounter;
}
565
566
567
568
569
static OSStatus
AudioFileManager_FileInputProc (void *inRefCon,
AudioUnitRenderActionFlags inActionFlags,
const AudioTimeStamp * inTimeStamp,
UInt32 inBusNumber, AudioBuffer * ioData)
570
{
571
572
AudioFileManager *afm = (AudioFileManager *) inRefCon;
return afm->Render (afm, ioData);
573
574
}
575
576
static OSStatus
AudioFileManager_Render (AudioFileManager * afm, AudioBuffer * ioData)
577
578
{
OSStatus result = noErr;
579
580
581
582
583
584
585
586
587
588
589
590
if (afm->mBufferOffset >= afm->mBufferSize) {
result = afm->GetFileData (afm, &afm->mTmpBuffer, &afm->mBufferSize);
if (result) {
SDL_SetError ("AudioConverterFillBuffer:%ld\n", result);
afm->mParent->DoNotification (afm->mParent, result);
return result;
}
afm->mBufferOffset = 0;
}
591
if (ioData->mDataByteSize > afm->mBufferSize - afm->mBufferOffset)
592
593
ioData->mDataByteSize = afm->mBufferSize - afm->mBufferOffset;
ioData->mData = (char *) afm->mTmpBuffer + afm->mBufferOffset;
594
afm->mBufferOffset += ioData->mDataByteSize;
595
596
597
afm->mByteCounter += ioData->mDataByteSize;
afm->AfterRender (afm);
598
599
600
601
return result;
}
602
603
void
delete_AudioFileManager (AudioFileManager * afm)
604
605
606
{
if (afm != NULL) {
if (afm->mFileBuffer) {
607
free (afm->mFileBuffer);
608
609
}
610
SDL_free (afm);
611
612
613
614
}
}
615
616
617
618
AudioFileManager *
new_AudioFileManager (AudioFilePlayer * inParent,
SInt16 inForkRefNum,
SInt64 inFileLength, UInt32 inChunkSize)
619
620
621
{
AudioFileManager *afm;
622
623
if (sReaderThread == NULL) {
sReaderThread = new_FileReaderThread ();
624
625
626
627
if (sReaderThread == NULL)
return NULL;
}
628
afm = (AudioFileManager *) SDL_malloc (sizeof (AudioFileManager));
629
630
if (afm == NULL)
return NULL;
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
SDL_memset (afm, '\0', sizeof (*afm));
#define SET_AUDIOFILEMANAGER_METHOD(m) afm->m = AudioFileManager_##m
SET_AUDIOFILEMANAGER_METHOD (Disconnect);
SET_AUDIOFILEMANAGER_METHOD (DoConnect);
SET_AUDIOFILEMANAGER_METHOD (Read);
SET_AUDIOFILEMANAGER_METHOD (GetFileBuffer);
SET_AUDIOFILEMANAGER_METHOD (GetParent);
SET_AUDIOFILEMANAGER_METHOD (SetPosition);
SET_AUDIOFILEMANAGER_METHOD (GetByteCounter);
SET_AUDIOFILEMANAGER_METHOD (SetEndOfFile);
SET_AUDIOFILEMANAGER_METHOD (Render);
SET_AUDIOFILEMANAGER_METHOD (GetFileData);
SET_AUDIOFILEMANAGER_METHOD (AfterRender);
SET_AUDIOFILEMANAGER_METHOD (FileInputProc);
#undef SET_AUDIOFILEMANAGER_METHOD
647
648
649
650
651
652
653
afm->mParent = inParent;
afm->mForkRefNum = inForkRefNum;
afm->mBufferSize = inChunkSize;
afm->mBufferOffset = inChunkSize;
afm->mChunkSize = inChunkSize;
afm->mFileLength = inFileLength;
654
655
afm->mFileBuffer = (char *) SDL_malloc (afm->mChunkSize * 2);
FSGetForkPosition (afm->mForkRefNum, &afm->mAudioDataOffset);
656
657
658
659
assert (afm->mFileBuffer != NULL);
return afm;
}
660
/* vi: set ts=4 sw=4 expandtab: */