This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
AudioFilePlayer.c
398 lines (335 loc) · 12.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
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
AudioFilePlayer.cpp
*/
30
31
32
33
34
35
36
37
38
39
40
#include "AudioFilePlayer.h"
/*
void ThrowResult (OSStatus result, const char* str)
{
SDL_SetError ("Error: %s %d", str, result);
throw result;
}
*/
#if DEBUG
41
static void
42
PrintStreamDesc(AudioStreamBasicDescription * inDesc)
43
44
{
if (!inDesc) {
45
printf("Can't print a NULL desc!\n");
46
47
return;
}
48
49
50
51
52
53
54
55
56
57
58
printf("- - - - - - - - - - - - - - - - - - - -\n");
printf(" Sample Rate:%f\n", inDesc->mSampleRate);
printf(" Format ID:%s\n", (char *) &inDesc->mFormatID);
printf(" Format Flags:%lX\n", inDesc->mFormatFlags);
printf(" Bytes per Packet:%ld\n", inDesc->mBytesPerPacket);
printf(" Frames per Packet:%ld\n", inDesc->mFramesPerPacket);
printf(" Bytes per Frame:%ld\n", inDesc->mBytesPerFrame);
printf(" Channels per Frame:%ld\n", inDesc->mChannelsPerFrame);
printf(" Bits per Channel:%ld\n", inDesc->mBitsPerChannel);
printf("- - - - - - - - - - - - - - - - - - - -\n");
59
60
61
62
}
#endif
63
static int
64
AudioFilePlayer_SetDestination(AudioFilePlayer * afp, AudioUnit * inDestUnit)
65
{
66
/*if (afp->mConnected) throw static_cast<OSStatus>(-1); *//* can't set dest if already engaged */
67
if (afp->mConnected)
68
return 0;
69
70
SDL_memcpy(&afp->mPlayUnit, inDestUnit, sizeof(afp->mPlayUnit));
71
72
73
OSStatus result = noErr;
74
75
/* we can "down" cast a component instance to a component */
76
ComponentDescription desc;
77
result = GetComponentInfo((Component) * inDestUnit, &desc, 0, 0, 0);
78
79
80
81
82
83
if (result)
return 0; /*THROW_RESULT("GetComponentInfo") */
/* we're going to use this to know which convert routine to call
a v1 audio unit will have a type of 'aunt'
a v2 audio unit will have one of several different types. */
84
85
if (desc.componentType != kAudioUnitComponentType) {
result = badComponentInstance;
86
87
88
/*THROW_RESULT("BAD COMPONENT") */
if (result)
return 0;
89
90
91
}
/* Set the input format of the audio unit. */
92
93
94
95
96
97
result = AudioUnitSetProperty(*inDestUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&afp->mFileDescription,
sizeof(afp->mFileDescription));
98
99
100
/*THROW_RESULT("AudioUnitSetProperty") */
if (result)
return 0;
101
102
103
return 1;
}
104
static void
105
106
AudioFilePlayer_SetNotifier(AudioFilePlayer * afp,
AudioFilePlayNotifier inNotifier, void *inRefCon)
107
108
109
110
111
{
afp->mNotifier = inNotifier;
afp->mRefCon = inRefCon;
}
112
static int
113
AudioFilePlayer_IsConnected(AudioFilePlayer * afp)
114
115
116
117
{
return afp->mConnected;
}
118
static AudioUnit
119
AudioFilePlayer_GetDestUnit(AudioFilePlayer * afp)
120
{
121
return afp->mPlayUnit;
122
123
}
124
static void
125
AudioFilePlayer_Print(AudioFilePlayer * afp)
126
{
127
#if DEBUG
128
129
printf("Is Connected:%s\n", (IsConnected()? "true" : "false"));
printf("- - - - - - - - - - - - - - \n");
130
131
132
#endif
}
133
static void
134
AudioFilePlayer_SetStartFrame(AudioFilePlayer * afp, int frame)
135
136
137
138
{
SInt64 position = frame * 2352;
afp->mStartFrame = frame;
139
afp->mAudioFileManager->SetPosition(afp->mAudioFileManager, position);
140
141
}
142
143
static int
144
AudioFilePlayer_GetCurrentFrame(AudioFilePlayer * afp)
145
{
146
return afp->mStartFrame +
147
(afp->mAudioFileManager->GetByteCounter(afp->mAudioFileManager) /
148
2352);
149
}
150
151
static void
152
AudioFilePlayer_SetStopFrame(AudioFilePlayer * afp, int frame)
153
{
154
155
SInt64 position = frame * 2352;
156
afp->mAudioFileManager->SetEndOfFile(afp->mAudioFileManager, position);
157
}
158
159
void
160
delete_AudioFilePlayer(AudioFilePlayer * afp)
161
{
162
if (afp != NULL) {
163
afp->Disconnect(afp);
164
165
if (afp->mAudioFileManager) {
166
delete_AudioFileManager(afp->mAudioFileManager);
167
168
afp->mAudioFileManager = 0;
}
169
170
if (afp->mForkRefNum) {
171
FSCloseFork(afp->mForkRefNum);
172
173
afp->mForkRefNum = 0;
}
174
SDL_free(afp);
175
176
177
}
}
178
static int
179
AudioFilePlayer_Connect(AudioFilePlayer * afp)
180
181
{
#if DEBUG
182
183
printf("Connect:%x, engaged=%d\n", (int) afp->mPlayUnit,
(afp->mConnected ? 1 : 0));
184
#endif
185
if (!afp->mConnected) {
186
if (!afp->mAudioFileManager->DoConnect(afp->mAudioFileManager))
187
188
return 0;
189
/* set the render callback for the file data to be supplied to the sound converter AU */
190
191
192
afp->mInputCallback.inputProc = afp->mAudioFileManager->FileInputProc;
afp->mInputCallback.inputProcRefCon = afp->mAudioFileManager;
193
194
195
196
197
198
OSStatus result = AudioUnitSetProperty(afp->mPlayUnit,
kAudioUnitProperty_SetInputCallback,
kAudioUnitScope_Input,
0,
&afp->mInputCallback,
sizeof(afp->mInputCallback));
199
200
if (result)
return 0; /*THROW_RESULT("AudioUnitSetProperty") */
201
202
203
204
205
206
afp->mConnected = 1;
}
return 1;
}
207
208
/* warning noted, now please go away ;-) */
/* #warning This should redirect the calling of notification code to some other thread */
209
static void
210
AudioFilePlayer_DoNotification(AudioFilePlayer * afp, OSStatus inStatus)
211
212
213
214
{
if (afp->mNotifier) {
(*afp->mNotifier) (afp->mRefCon, inStatus);
} else {
215
SDL_SetError("Notification posted with no notifier in place");
216
217
if (inStatus == kAudioFilePlay_FileIsFinished)
218
afp->Disconnect(afp);
219
else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun)
220
afp->Disconnect(afp);
221
222
223
}
}
224
static void
225
AudioFilePlayer_Disconnect(AudioFilePlayer * afp)
226
227
{
#if DEBUG
228
229
printf("Disconnect:%x,%ld, engaged=%d\n", (int) afp->mPlayUnit, 0,
(afp->mConnected ? 1 : 0));
230
#endif
231
if (afp->mConnected) {
232
afp->mConnected = 0;
233
234
235
afp->mInputCallback.inputProc = 0;
afp->mInputCallback.inputProcRefCon = 0;
236
237
238
239
240
241
OSStatus result = AudioUnitSetProperty(afp->mPlayUnit,
kAudioUnitProperty_SetInputCallback,
kAudioUnitScope_Input,
0,
&afp->mInputCallback,
sizeof(afp->mInputCallback));
242
if (result)
243
244
SDL_SetError("AudioUnitSetProperty:RemoveInputCallback:%ld",
result);
245
246
afp->mAudioFileManager->Disconnect(afp->mAudioFileManager);
247
248
249
}
}
250
251
typedef struct
{
252
253
254
255
UInt32 offset;
UInt32 blockSize;
} SSNDData;
256
static int
257
258
AudioFilePlayer_OpenFile(AudioFilePlayer * afp, const FSRef * inRef,
SInt64 * outFileDataSize)
259
260
261
262
263
264
265
266
267
268
{
ContainerChunk chunkHeader;
ChunkHeader chunk;
SSNDData ssndData;
OSErr result;
HFSUniStr255 dfName;
ByteCount actual;
SInt64 offset;
269
/* Open the data fork of the input file */
270
result = FSGetDataForkName(&dfName);
271
272
273
274
if (result)
return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSGetDataForkName") */
result =
275
276
FSOpenFork(inRef, dfName.length, dfName.unicode, fsRdPerm,
&afp->mForkRefNum);
277
278
if (result)
return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSOpenFork") */
279
280
/* Read the file header, and check if it's indeed an AIFC file */
281
result =
282
283
FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(chunkHeader),
&chunkHeader, &actual);
284
285
if (result)
return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork") */
286
287
288
if (chunkHeader.ckID != 'FORM') {
result = -1;
289
290
if (result)
return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): chunk id is not 'FORM'"); */
291
292
293
294
}
if (chunkHeader.formType != 'AIFC') {
result = -1;
295
296
if (result)
return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): file format is not 'AIFC'"); */
297
298
}
299
300
301
302
/* Search for the SSND chunk. We ignore all compression etc. information
in other chunks. Of course that is kind of evil, but for now we are lazy
and rely on the cdfs to always give us the same fixed format.
TODO: Parse the COMM chunk we currently skip to fill in mFileDescription.
303
*/
304
305
offset = 0;
do {
306
result =
307
308
FSReadFork(afp->mForkRefNum, fsFromMark, offset,
sizeof(chunk), &chunk, &actual);
309
310
311
if (result)
return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork") */
312
/* Skip the chunk data */
313
offset = chunk.ckSize;
314
315
}
while (chunk.ckID != 'SSND');
316
317
318
/* Read the header of the SSND chunk. After this, we are positioned right
at the start of the audio data. */
319
result =
320
321
FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(ssndData),
&ssndData, &actual);
322
323
if (result)
return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork") */
324
325
result = FSSetForkPosition(afp->mForkRefNum, fsFromMark, ssndData.offset);
326
327
if (result)
return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSSetForkPosition") */
328
329
/* Data size */
330
331
*outFileDataSize = chunk.ckSize - ssndData.offset - 8;
332
/* File format */
333
334
afp->mFileDescription.mSampleRate = 44100;
afp->mFileDescription.mFormatID = kAudioFormatLinearPCM;
335
336
afp->mFileDescription.mFormatFlags =
kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger;
337
338
339
340
341
342
343
344
345
afp->mFileDescription.mBytesPerPacket = 4;
afp->mFileDescription.mFramesPerPacket = 1;
afp->mFileDescription.mBytesPerFrame = 4;
afp->mFileDescription.mChannelsPerFrame = 2;
afp->mFileDescription.mBitsPerChannel = 16;
return 1;
}
346
AudioFilePlayer *
347
new_AudioFilePlayer(const FSRef * inFileRef)
348
{
349
SInt64 fileDataSize = 0;
350
351
AudioFilePlayer *afp =
352
(AudioFilePlayer *) SDL_malloc(sizeof(AudioFilePlayer));
353
354
if (afp == NULL)
return NULL;
355
SDL_memset(afp, '\0', sizeof(*afp));
356
357
#define SET_AUDIOFILEPLAYER_METHOD(m) afp->m = AudioFilePlayer_##m
358
359
360
361
362
363
364
365
366
367
368
369
SET_AUDIOFILEPLAYER_METHOD(SetDestination);
SET_AUDIOFILEPLAYER_METHOD(SetNotifier);
SET_AUDIOFILEPLAYER_METHOD(SetStartFrame);
SET_AUDIOFILEPLAYER_METHOD(GetCurrentFrame);
SET_AUDIOFILEPLAYER_METHOD(SetStopFrame);
SET_AUDIOFILEPLAYER_METHOD(Connect);
SET_AUDIOFILEPLAYER_METHOD(Disconnect);
SET_AUDIOFILEPLAYER_METHOD(DoNotification);
SET_AUDIOFILEPLAYER_METHOD(IsConnected);
SET_AUDIOFILEPLAYER_METHOD(GetDestUnit);
SET_AUDIOFILEPLAYER_METHOD(Print);
SET_AUDIOFILEPLAYER_METHOD(OpenFile);
370
371
#undef SET_AUDIOFILEPLAYER_METHOD
372
373
if (!afp->OpenFile(afp, inFileRef, &fileDataSize)) {
SDL_free(afp);
374
375
return NULL;
}
376
377
/* we want about 4 seconds worth of data for the buffer */
378
379
380
381
int bytesPerSecond =
(UInt32) (4 * afp->mFileDescription.mSampleRate *
afp->mFileDescription.mBytesPerFrame);
382
#if DEBUG
383
384
printf("File format:\n");
PrintStreamDesc(&afp->mFileDescription);
385
#endif
386
387
388
389
afp->mAudioFileManager = new_AudioFileManager(afp, afp->mForkRefNum,
fileDataSize,
bytesPerSecond);
390
if (afp->mAudioFileManager == NULL) {
391
delete_AudioFilePlayer(afp);
392
393
394
395
396
397
return NULL;
}
return afp;
}
398
/* vi: set ts=4 sw=4 expandtab: */