This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
AudioFileReaderThread.c
657 lines (544 loc) · 19.2 KB
1
2
/*
SDL - Simple DirectMedia Layer
3
Copyright (C) 1997-2009 Sam Lantinga
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
/* construct pointer */
char *writePtr = (char *) (theItem->GetFileBuffer(theItem) +
338
339
(theItem->mWriteToFirstBuffer ? 0 :
theItem->mChunkSize));
340
341
/* read data */
342
343
result = theItem->Read(theItem, writePtr, &dataChunkSize);
if (result != noErr && result != eofErr) {
344
345
AudioFilePlayer *afp =
(AudioFilePlayer *) theItem->GetParent(theItem);
346
347
348
afp->DoNotification(afp, result);
continue;
}
349
350
if (dataChunkSize != theItem->mChunkSize) {
351
352
writePtr += dataChunkSize;
353
354
/* can't exit yet.. we still have to pass the partial buffer back */
SDL_memset(writePtr, 0, (theItem->mChunkSize - dataChunkSize));
355
}
356
357
theItem->mWriteToFirstBuffer = !theItem->mWriteToFirstBuffer; /* switch buffers */
358
359
360
361
if (result == eofErr)
theItem->mReadFilePosition = theItem->mFileLength;
else
362
theItem->mReadFilePosition += dataChunkSize; /* increment count */
363
364
365
}
}
366
367
void
delete_FileReaderThread(FileReaderThread * frt)
368
{
369
if (frt != NULL) {
370
delete_SDLOSXCAGuard(frt->mGuard);
371
SDL_free(frt);
372
373
374
}
}
375
376
FileReaderThread *
new_FileReaderThread()
377
{
378
379
FileReaderThread *frt =
(FileReaderThread *) SDL_malloc(sizeof(FileReaderThread));
380
381
if (frt == NULL)
return NULL;
382
SDL_memset(frt, '\0', sizeof(*frt));
383
384
frt->mGuard = new_SDLOSXCAGuard();
385
if (frt->mGuard == NULL) {
386
SDL_free(frt);
387
388
return NULL;
}
389
#define SET_FILEREADERTHREAD_METHOD(m) frt->m = FileReaderThread_##m
390
391
392
393
394
395
396
397
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);
398
#undef SET_FILEREADERTHREAD_METHOD
399
400
401
402
403
404
405
406
407
frt->mThreadPriority = 62;
return frt;
}
static FileReaderThread *sReaderThread;
408
409
static int
AudioFileManager_DoConnect(AudioFileManager * afm)
410
{
411
if (!afm->mIsEngaged) {
412
413
OSStatus result;
414
/*afm->mReadFilePosition = 0; */
415
416
417
418
afm->mFinishedReadingData = 0;
afm->mNumTimesAskedSinceFinished = 0;
afm->mLockUnsuccessful = 0;
419
420
UInt32 dataChunkSize;
421
422
423
424
425
if ((afm->mFileLength - afm->mReadFilePosition) < afm->mChunkSize)
dataChunkSize = afm->mFileLength - afm->mReadFilePosition;
else
dataChunkSize = afm->mChunkSize;
426
427
result = afm->Read(afm, afm->mFileBuffer, &dataChunkSize);
428
429
if (result)
return 0; /*THROW_RESULT("AudioFileManager::DoConnect(): Read") */
430
431
afm->mReadFilePosition += dataChunkSize;
432
433
434
435
436
afm->mWriteToFirstBuffer = 0;
afm->mReadFromFirstBuffer = 1;
sReaderThread->AddReader(sReaderThread);
437
438
439
afm->mIsEngaged = 1;
}
440
/*
441
442
else
throw static_cast<OSStatus>(-1); *//* thread has already been started */
443
444
445
446
return 1;
}
447
448
static void
AudioFileManager_Disconnect(AudioFileManager * afm)
449
{
450
451
if (afm->mIsEngaged) {
sReaderThread->RemoveReader(sReaderThread, afm);
452
453
454
455
afm->mIsEngaged = 0;
}
}
456
457
static OSStatus
AudioFileManager_Read(AudioFileManager * afm, char *buffer, UInt32 * len)
458
{
459
460
461
462
return FSReadFork(afm->mForkRefNum,
fsFromStart,
afm->mReadFilePosition + afm->mAudioDataOffset,
*len, buffer, len);
463
464
}
465
466
467
static OSStatus
AudioFileManager_GetFileData(AudioFileManager * afm, void **inOutData,
UInt32 * inOutDataSize)
468
{
469
if (afm->mFinishedReadingData) {
470
471
472
473
474
++afm->mNumTimesAskedSinceFinished;
*inOutDataSize = 0;
*inOutData = 0;
return noErr;
}
475
476
if (afm->mReadFromFirstBuffer == afm->mWriteToFirstBuffer) {
477
478
479
480
481
482
#if DEBUG
printf("* * * * * * * Can't keep up with reading file\n");
#endif
afm->mParent->DoNotification(afm->mParent,
kAudioFilePlayErr_FilePlayUnderrun);
483
484
485
486
*inOutDataSize = 0;
*inOutData = 0;
} else {
*inOutDataSize = afm->mChunkSize;
487
*inOutData =
488
afm->mReadFromFirstBuffer ? afm->mFileBuffer : (afm->mFileBuffer +
489
afm->mChunkSize);
490
491
}
492
493
afm->mLockUnsuccessful = !sReaderThread->TryNextRead(sReaderThread, afm);
494
495
496
497
498
afm->mReadFromFirstBuffer = !afm->mReadFromFirstBuffer;
return noErr;
}
499
500
static void
AudioFileManager_AfterRender(AudioFileManager * afm)
501
{
502
if (afm->mNumTimesAskedSinceFinished > 0) {
503
504
505
int didLock = 0;
SDLOSXCAGuard *guard = sReaderThread->GetGuard(sReaderThread);
if (guard->Try(guard, &didLock)) {
506
507
afm->mParent->DoNotification(afm->mParent,
kAudioFilePlay_FileIsFinished);
508
509
510
511
512
513
if (didLock)
guard->Unlock(guard);
}
}
if (afm->mLockUnsuccessful)
514
515
afm->mLockUnsuccessful =
!sReaderThread->TryNextRead(sReaderThread, afm);
516
517
}
518
519
static void
AudioFileManager_SetPosition(AudioFileManager * afm, SInt64 pos)
520
521
{
if (pos < 0 || pos >= afm->mFileLength) {
522
523
524
SDL_SetError
("AudioFileManager::SetPosition - position invalid: %d filelen=%d\n",
(unsigned int) pos, (unsigned int) afm->mFileLength);
525
526
pos = 0;
}
527
528
529
afm->mReadFilePosition = pos;
}
530
531
532
static void
AudioFileManager_SetEndOfFile(AudioFileManager * afm, SInt64 pos)
533
534
{
if (pos <= 0 || pos > afm->mFileLength) {
535
536
SDL_SetError
("AudioFileManager::SetEndOfFile - position beyond actual eof\n");
537
538
pos = afm->mFileLength;
}
539
540
541
542
afm->mFileLength = pos;
}
543
544
static const char *
AudioFileManager_GetFileBuffer(AudioFileManager * afm)
545
546
547
548
{
return afm->mFileBuffer;
}
549
550
const AudioFilePlayer *
AudioFileManager_GetParent(AudioFileManager * afm)
551
552
553
554
{
return afm->mParent;
}
555
556
static int
AudioFileManager_GetByteCounter(AudioFileManager * afm)
557
558
559
560
561
{
return afm->mByteCounter;
}
562
563
564
565
566
static OSStatus
AudioFileManager_FileInputProc(void *inRefCon,
AudioUnitRenderActionFlags inActionFlags,
const AudioTimeStamp * inTimeStamp,
UInt32 inBusNumber, AudioBuffer * ioData)
567
{
568
AudioFileManager *afm = (AudioFileManager *) inRefCon;
569
570
571
return afm->Render(afm, ioData);
}
572
573
static OSStatus
AudioFileManager_Render(AudioFileManager * afm, AudioBuffer * ioData)
574
575
{
OSStatus result = noErr;
576
577
578
579
580
581
582
583
584
585
586
587
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;
}
588
if (ioData->mDataByteSize > afm->mBufferSize - afm->mBufferOffset)
589
590
ioData->mDataByteSize = afm->mBufferSize - afm->mBufferOffset;
ioData->mData = (char *) afm->mTmpBuffer + afm->mBufferOffset;
591
afm->mBufferOffset += ioData->mDataByteSize;
592
593
594
afm->mByteCounter += ioData->mDataByteSize;
afm->AfterRender(afm);
595
596
597
598
return result;
}
599
600
void
delete_AudioFileManager(AudioFileManager * afm)
601
602
603
{
if (afm != NULL) {
if (afm->mFileBuffer) {
604
free(afm->mFileBuffer);
605
606
}
607
SDL_free(afm);
608
609
610
611
}
}
612
613
614
615
AudioFileManager *
new_AudioFileManager(AudioFilePlayer * inParent,
SInt16 inForkRefNum,
SInt64 inFileLength, UInt32 inChunkSize)
616
617
618
{
AudioFileManager *afm;
619
if (sReaderThread == NULL) {
620
621
622
623
624
sReaderThread = new_FileReaderThread();
if (sReaderThread == NULL)
return NULL;
}
625
afm = (AudioFileManager *) SDL_malloc(sizeof(AudioFileManager));
626
627
if (afm == NULL)
return NULL;
628
SDL_memset(afm, '\0', sizeof(*afm));
629
630
#define SET_AUDIOFILEMANAGER_METHOD(m) afm->m = AudioFileManager_##m
631
632
633
634
635
636
637
638
639
640
641
642
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);
643
#undef SET_AUDIOFILEMANAGER_METHOD
644
645
646
647
648
649
650
afm->mParent = inParent;
afm->mForkRefNum = inForkRefNum;
afm->mBufferSize = inChunkSize;
afm->mBufferOffset = inChunkSize;
afm->mChunkSize = inChunkSize;
afm->mFileLength = inFileLength;
651
afm->mFileBuffer = (char *) SDL_malloc(afm->mChunkSize * 2);
652
FSGetForkPosition(afm->mForkRefNum, &afm->mAudioDataOffset);
653
assert(afm->mFileBuffer != NULL);
654
655
656
return afm;
}
657
/* vi: set ts=4 sw=4 expandtab: */