This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
AudioFileReaderThread.c
659 lines (546 loc) · 19.3 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
108
109
110
111
112
113
if (prev == NULL)
frt->mFileData = newfd;
else
prev->next = newfd;
frt->mGuard->Notify(frt->mGuard);
succeeded = 1;
if (didLock)
frt->mGuard->Unlock(frt->mGuard);
}
114
115
116
117
return succeeded;
}
118
119
static void
FileReaderThread_AddReader(FileReaderThread * frt)
120
{
121
if (frt->mNumReaders == 0) {
122
frt->mThreadShouldDie = 0;
123
frt->StartFixedPriorityThread(frt);
124
125
126
127
}
frt->mNumReaders++;
}
128
129
130
static void
FileReaderThread_RemoveReader(FileReaderThread * frt,
AudioFileManager * inItem)
131
{
132
if (frt->mNumReaders > 0) {
133
int bNeedsRelease = frt->mGuard->Lock(frt->mGuard);
134
135
/*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
pthread_attr_t theThreadAttrs;
pthread_t pThread;
168
169
OSStatus result = pthread_attr_init(&theThreadAttrs);
170
171
172
173
174
175
176
177
178
179
180
181
182
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.") */
183
pthread_attr_destroy(&theThreadAttrs);
184
185
186
187
/* 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 */
188
189
190
191
thread_extended_policy_data_t theFixedPolicy;
thread_precedence_policy_data_t thePrecedencePolicy;
SInt32 relativePriority;
192
/* make thread fixed */
193
194
195
196
197
198
199
200
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.") */
201
202
/* set priority */
/* precedency policy's "importance" value is relative to spawning thread's priority */
203
204
205
relativePriority =
frt->mThreadPriority - frt->GetThreadBasePriority(pthread_self());
206
thePrecedencePolicy.importance = relativePriority;
207
208
209
210
211
212
213
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.") */
214
215
216
217
return 1;
}
218
219
static UInt32
FileReaderThread_GetThreadBasePriority(pthread_t inThread)
220
{
221
222
223
224
thread_basic_info_data_t threadInfo;
policy_info_data_t thePolicyInfo;
unsigned int count;
225
/* get basic info */
226
count = THREAD_BASIC_INFO_COUNT;
227
228
229
thread_info(pthread_mach_thread_np(inThread), THREAD_BASIC_INFO,
(integer_t *) & threadInfo, &count);
230
switch (threadInfo.policy) {
231
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
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;
262
}
263
264
265
266
return 0;
}
267
268
static void *
FileReaderThread_DiskReaderEntry(void *inRefCon)
269
{
270
FileReaderThread *frt = (FileReaderThread *) inRefCon;
271
frt->ReadNextChunk(frt);
272
273
274
275
#if DEBUG
printf("finished with reading file\n");
#endif
276
277
278
return 0;
}
279
280
static void
FileReaderThread_ReadNextChunk(FileReaderThread * frt)
281
282
{
OSStatus result;
283
284
UInt32 dataChunkSize;
AudioFileManager *theItem = 0;
285
286
287
for (;;) {
{ /* this is a scoped based lock */
288
int bNeedsRelease = frt->mGuard->Lock(frt->mGuard);
289
290
291
if (frt->mThreadShouldDie) {
frt->mGuard->Notify(frt->mGuard);
292
293
if (bNeedsRelease)
frt->mGuard->Unlock(frt->mGuard);
294
295
return;
}
296
297
298
/*if (frt->mFileData.empty()) */
if (frt->mFileData == NULL) {
299
300
frt->mGuard->Wait(frt->mGuard);
}
301
302
/* kill thread */
303
if (frt->mThreadShouldDie) {
304
305
frt->mGuard->Notify(frt->mGuard);
306
307
if (bNeedsRelease)
frt->mGuard->Unlock(frt->mGuard);
308
309
310
return;
}
311
312
/*theItem = frt->mFileData.front(); */
/*frt->mFileData.pop_front(); */
313
theItem = NULL;
314
if (frt->mFileData != NULL) {
315
316
FileData *next = frt->mFileData->next;
theItem = frt->mFileData->obj;
317
SDL_free(frt->mFileData);
318
319
320
frt->mFileData = next;
}
321
322
if (bNeedsRelease)
frt->mGuard->Unlock(frt->mGuard);
323
}
324
325
326
if ((theItem->mFileLength - theItem->mReadFilePosition) <
theItem->mChunkSize)
327
328
329
dataChunkSize = theItem->mFileLength - theItem->mReadFilePosition;
else
dataChunkSize = theItem->mChunkSize;
330
331
/* this is the exit condition for the thread */
332
333
334
335
if (dataChunkSize <= 0) {
theItem->mFinishedReadingData = 1;
continue;
}
336
337
338
339
340
341
342
/* construct pointer */
char *writePtr = (char *) (theItem->GetFileBuffer(theItem) +
(theItem->
mWriteToFirstBuffer ? 0 : theItem->
mChunkSize));
/* read data */
343
344
result = theItem->Read(theItem, writePtr, &dataChunkSize);
if (result != noErr && result != eofErr) {
345
346
AudioFilePlayer *afp =
(AudioFilePlayer *) theItem->GetParent(theItem);
347
348
349
afp->DoNotification(afp, result);
continue;
}
350
351
if (dataChunkSize != theItem->mChunkSize) {
352
353
writePtr += dataChunkSize;
354
355
/* can't exit yet.. we still have to pass the partial buffer back */
SDL_memset(writePtr, 0, (theItem->mChunkSize - dataChunkSize));
356
}
357
358
theItem->mWriteToFirstBuffer = !theItem->mWriteToFirstBuffer; /* switch buffers */
359
360
361
362
if (result == eofErr)
theItem->mReadFilePosition = theItem->mFileLength;
else
363
theItem->mReadFilePosition += dataChunkSize; /* increment count */
364
365
366
}
}
367
368
void
delete_FileReaderThread(FileReaderThread * frt)
369
{
370
if (frt != NULL) {
371
delete_SDLOSXCAGuard(frt->mGuard);
372
SDL_free(frt);
373
374
375
}
}
376
377
FileReaderThread *
new_FileReaderThread()
378
{
379
380
FileReaderThread *frt =
(FileReaderThread *) SDL_malloc(sizeof(FileReaderThread));
381
382
if (frt == NULL)
return NULL;
383
SDL_memset(frt, '\0', sizeof(*frt));
384
385
frt->mGuard = new_SDLOSXCAGuard();
386
if (frt->mGuard == NULL) {
387
SDL_free(frt);
388
389
return NULL;
}
390
#define SET_FILEREADERTHREAD_METHOD(m) frt->m = FileReaderThread_##m
391
392
393
394
395
396
397
398
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);
399
#undef SET_FILEREADERTHREAD_METHOD
400
401
402
403
404
405
406
407
408
frt->mThreadPriority = 62;
return frt;
}
static FileReaderThread *sReaderThread;
409
410
static int
AudioFileManager_DoConnect(AudioFileManager * afm)
411
{
412
if (!afm->mIsEngaged) {
413
414
OSStatus result;
415
/*afm->mReadFilePosition = 0; */
416
417
418
419
afm->mFinishedReadingData = 0;
afm->mNumTimesAskedSinceFinished = 0;
afm->mLockUnsuccessful = 0;
420
421
UInt32 dataChunkSize;
422
423
424
425
426
if ((afm->mFileLength - afm->mReadFilePosition) < afm->mChunkSize)
dataChunkSize = afm->mFileLength - afm->mReadFilePosition;
else
dataChunkSize = afm->mChunkSize;
427
428
result = afm->Read(afm, afm->mFileBuffer, &dataChunkSize);
429
430
if (result)
return 0; /*THROW_RESULT("AudioFileManager::DoConnect(): Read") */
431
432
afm->mReadFilePosition += dataChunkSize;
433
434
435
436
437
afm->mWriteToFirstBuffer = 0;
afm->mReadFromFirstBuffer = 1;
sReaderThread->AddReader(sReaderThread);
438
439
440
afm->mIsEngaged = 1;
}
441
/*
442
443
else
throw static_cast<OSStatus>(-1); *//* thread has already been started */
444
445
446
447
return 1;
}
448
449
static void
AudioFileManager_Disconnect(AudioFileManager * afm)
450
{
451
452
if (afm->mIsEngaged) {
sReaderThread->RemoveReader(sReaderThread, afm);
453
454
455
456
afm->mIsEngaged = 0;
}
}
457
458
static OSStatus
AudioFileManager_Read(AudioFileManager * afm, char *buffer, UInt32 * len)
459
{
460
461
462
463
return FSReadFork(afm->mForkRefNum,
fsFromStart,
afm->mReadFilePosition + afm->mAudioDataOffset,
*len, buffer, len);
464
465
}
466
467
468
static OSStatus
AudioFileManager_GetFileData(AudioFileManager * afm, void **inOutData,
UInt32 * inOutDataSize)
469
{
470
if (afm->mFinishedReadingData) {
471
472
473
474
475
++afm->mNumTimesAskedSinceFinished;
*inOutDataSize = 0;
*inOutData = 0;
return noErr;
}
476
477
if (afm->mReadFromFirstBuffer == afm->mWriteToFirstBuffer) {
478
479
480
481
482
483
#if DEBUG
printf("* * * * * * * Can't keep up with reading file\n");
#endif
afm->mParent->DoNotification(afm->mParent,
kAudioFilePlayErr_FilePlayUnderrun);
484
485
486
487
*inOutDataSize = 0;
*inOutData = 0;
} else {
*inOutDataSize = afm->mChunkSize;
488
489
490
491
*inOutData =
afm->mReadFromFirstBuffer ? afm->mFileBuffer : (afm->
mFileBuffer +
afm->mChunkSize);
492
493
}
494
495
afm->mLockUnsuccessful = !sReaderThread->TryNextRead(sReaderThread, afm);
496
497
498
499
500
afm->mReadFromFirstBuffer = !afm->mReadFromFirstBuffer;
return noErr;
}
501
502
static void
AudioFileManager_AfterRender(AudioFileManager * afm)
503
{
504
if (afm->mNumTimesAskedSinceFinished > 0) {
505
506
507
int didLock = 0;
SDLOSXCAGuard *guard = sReaderThread->GetGuard(sReaderThread);
if (guard->Try(guard, &didLock)) {
508
509
afm->mParent->DoNotification(afm->mParent,
kAudioFilePlay_FileIsFinished);
510
511
512
513
514
515
if (didLock)
guard->Unlock(guard);
}
}
if (afm->mLockUnsuccessful)
516
517
afm->mLockUnsuccessful =
!sReaderThread->TryNextRead(sReaderThread, afm);
518
519
}
520
521
static void
AudioFileManager_SetPosition(AudioFileManager * afm, SInt64 pos)
522
523
{
if (pos < 0 || pos >= afm->mFileLength) {
524
525
526
SDL_SetError
("AudioFileManager::SetPosition - position invalid: %d filelen=%d\n",
(unsigned int) pos, (unsigned int) afm->mFileLength);
527
528
pos = 0;
}
529
530
531
afm->mReadFilePosition = pos;
}
532
533
534
static void
AudioFileManager_SetEndOfFile(AudioFileManager * afm, SInt64 pos)
535
536
{
if (pos <= 0 || pos > afm->mFileLength) {
537
538
SDL_SetError
("AudioFileManager::SetEndOfFile - position beyond actual eof\n");
539
540
pos = afm->mFileLength;
}
541
542
543
544
afm->mFileLength = pos;
}
545
546
static const char *
AudioFileManager_GetFileBuffer(AudioFileManager * afm)
547
548
549
550
{
return afm->mFileBuffer;
}
551
552
const AudioFilePlayer *
AudioFileManager_GetParent(AudioFileManager * afm)
553
554
555
556
{
return afm->mParent;
}
557
558
static int
AudioFileManager_GetByteCounter(AudioFileManager * afm)
559
560
561
562
563
{
return afm->mByteCounter;
}
564
565
566
567
568
static OSStatus
AudioFileManager_FileInputProc(void *inRefCon,
AudioUnitRenderActionFlags inActionFlags,
const AudioTimeStamp * inTimeStamp,
UInt32 inBusNumber, AudioBuffer * ioData)
569
{
570
AudioFileManager *afm = (AudioFileManager *) inRefCon;
571
572
573
return afm->Render(afm, ioData);
}
574
575
static OSStatus
AudioFileManager_Render(AudioFileManager * afm, AudioBuffer * ioData)
576
577
{
OSStatus result = noErr;
578
579
580
581
582
583
584
585
586
587
588
589
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;
}
590
if (ioData->mDataByteSize > afm->mBufferSize - afm->mBufferOffset)
591
592
ioData->mDataByteSize = afm->mBufferSize - afm->mBufferOffset;
ioData->mData = (char *) afm->mTmpBuffer + afm->mBufferOffset;
593
afm->mBufferOffset += ioData->mDataByteSize;
594
595
596
afm->mByteCounter += ioData->mDataByteSize;
afm->AfterRender(afm);
597
598
599
600
return result;
}
601
602
void
delete_AudioFileManager(AudioFileManager * afm)
603
604
605
{
if (afm != NULL) {
if (afm->mFileBuffer) {
606
free(afm->mFileBuffer);
607
608
}
609
SDL_free(afm);
610
611
612
613
}
}
614
615
616
617
AudioFileManager *
new_AudioFileManager(AudioFilePlayer * inParent,
SInt16 inForkRefNum,
SInt64 inFileLength, UInt32 inChunkSize)
618
619
620
{
AudioFileManager *afm;
621
if (sReaderThread == NULL) {
622
623
624
625
626
sReaderThread = new_FileReaderThread();
if (sReaderThread == NULL)
return NULL;
}
627
afm = (AudioFileManager *) SDL_malloc(sizeof(AudioFileManager));
628
629
if (afm == NULL)
return NULL;
630
SDL_memset(afm, '\0', sizeof(*afm));
631
632
#define SET_AUDIOFILEMANAGER_METHOD(m) afm->m = AudioFileManager_##m
633
634
635
636
637
638
639
640
641
642
643
644
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);
645
#undef SET_AUDIOFILEMANAGER_METHOD
646
647
648
649
650
651
652
afm->mParent = inParent;
afm->mForkRefNum = inForkRefNum;
afm->mBufferSize = inChunkSize;
afm->mBufferOffset = inChunkSize;
afm->mChunkSize = inChunkSize;
afm->mFileLength = inFileLength;
653
afm->mFileBuffer = (char *) SDL_malloc(afm->mChunkSize * 2);
654
FSGetForkPosition(afm->mForkRefNum, &afm->mAudioDataOffset);
655
assert(afm->mFileBuffer != NULL);
656
657
658
return afm;
}
659
/* vi: set ts=4 sw=4 expandtab: */