This repository has been archived by the owner on Feb 11, 2021. It is now read-only.
/
AudioFilePlayer.c
399 lines (336 loc) · 12.7 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
42
static void
PrintStreamDesc (AudioStreamBasicDescription * inDesc)
43
44
45
46
47
{
if (!inDesc) {
printf ("Can't print a NULL desc!\n");
return;
}
48
49
50
printf ("- - - - - - - - - - - - - - - - - - - -\n");
printf (" Sample Rate:%f\n", inDesc->mSampleRate);
51
printf (" Format ID:%s\n", (char *) &inDesc->mFormatID);
52
53
54
55
56
57
58
59
60
61
62
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");
}
#endif
63
64
static int
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
78
79
80
81
82
83
result = GetComponentInfo ((Component) * inDestUnit, &desc, 0, 0, 0);
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
92
}
/* Set the input format of the audio unit. */
result = AudioUnitSetProperty (*inDestUnit,
93
94
95
96
97
98
99
100
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
0,
&afp->mFileDescription,
sizeof (afp->mFileDescription));
/*THROW_RESULT("AudioUnitSetProperty") */
if (result)
return 0;
101
102
103
return 1;
}
104
105
106
static void
AudioFilePlayer_SetNotifier (AudioFilePlayer * afp,
AudioFilePlayNotifier inNotifier, void *inRefCon)
107
108
109
110
111
{
afp->mNotifier = inNotifier;
afp->mRefCon = inRefCon;
}
112
113
static int
AudioFilePlayer_IsConnected (AudioFilePlayer * afp)
114
115
116
117
{
return afp->mConnected;
}
118
119
static AudioUnit
AudioFilePlayer_GetDestUnit (AudioFilePlayer * afp)
120
{
121
return afp->mPlayUnit;
122
123
}
124
125
static void
AudioFilePlayer_Print (AudioFilePlayer * afp)
126
{
127
128
#if DEBUG
printf ("Is Connected:%s\n", (IsConnected ()? "true" : "false"));
129
130
131
132
printf ("- - - - - - - - - - - - - - \n");
#endif
}
133
134
static void
AudioFilePlayer_SetStartFrame (AudioFilePlayer * afp, int frame)
135
136
137
138
139
140
141
{
SInt64 position = frame * 2352;
afp->mStartFrame = frame;
afp->mAudioFileManager->SetPosition (afp->mAudioFileManager, position);
}
142
143
144
static int
AudioFilePlayer_GetCurrentFrame (AudioFilePlayer * afp)
145
{
146
147
148
return afp->mStartFrame +
(afp->mAudioFileManager->GetByteCounter (afp->mAudioFileManager) /
2352);
149
}
150
151
152
static void
AudioFilePlayer_SetStopFrame (AudioFilePlayer * afp, int frame)
153
{
154
155
SInt64 position = frame * 2352;
156
157
afp->mAudioFileManager->SetEndOfFile (afp->mAudioFileManager, position);
}
158
159
160
void
delete_AudioFilePlayer (AudioFilePlayer * afp)
161
{
162
163
164
if (afp != NULL) {
afp->Disconnect (afp);
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
179
static int
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
186
if (!afp->mConnected) {
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
199
200
OSStatus result = AudioUnitSetProperty (afp->mPlayUnit,
kAudioUnitProperty_SetInputCallback,
kAudioUnitScope_Input,
0,
&afp->mInputCallback,
sizeof (afp->mInputCallback));
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
210
static void
AudioFilePlayer_DoNotification (AudioFilePlayer * afp, OSStatus inStatus)
211
212
213
214
215
{
if (afp->mNotifier) {
(*afp->mNotifier) (afp->mRefCon, inStatus);
} else {
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
225
static void
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
242
243
244
245
246
OSStatus result = AudioUnitSetProperty (afp->mPlayUnit,
kAudioUnitProperty_SetInputCallback,
kAudioUnitScope_Input,
0,
&afp->mInputCallback,
sizeof (afp->mInputCallback));
if (result)
SDL_SetError ("AudioUnitSetProperty:RemoveInputCallback:%ld",
result);
afp->mAudioFileManager->Disconnect (afp->mAudioFileManager);
247
248
249
}
}
250
251
typedef struct
{
252
253
254
255
UInt32 offset;
UInt32 blockSize;
} SSNDData;
256
257
258
static int
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
271
272
273
274
275
276
277
278
result = FSGetDataForkName (&dfName);
if (result)
return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSGetDataForkName") */
result =
FSOpenFork (inRef, dfName.length, dfName.unicode, fsRdPerm,
&afp->mForkRefNum);
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
282
283
284
285
result =
FSReadFork (afp->mForkRefNum, fsAtMark, 0, sizeof (chunkHeader),
&chunkHeader, &actual);
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
307
308
309
310
311
result =
FSReadFork (afp->mForkRefNum, fsFromMark, offset,
sizeof (chunk), &chunk, &actual);
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
320
321
322
323
result =
FSReadFork (afp->mForkRefNum, fsAtMark, 0, sizeof (ssndData),
&ssndData, &actual);
if (result)
return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork") */
324
325
326
327
328
result =
FSSetForkPosition (afp->mForkRefNum, fsFromMark, ssndData.offset);
if (result)
return 0; /*THROW_RESULT("AudioFilePlayer::OpenFile(): FSSetForkPosition") */
329
330
/* Data size */
331
332
*outFileDataSize = chunk.ckSize - ssndData.offset - 8;
333
/* File format */
334
335
afp->mFileDescription.mSampleRate = 44100;
afp->mFileDescription.mFormatID = kAudioFormatLinearPCM;
336
337
afp->mFileDescription.mFormatFlags =
kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger;
338
339
340
341
342
343
344
345
346
afp->mFileDescription.mBytesPerPacket = 4;
afp->mFileDescription.mFramesPerPacket = 1;
afp->mFileDescription.mBytesPerFrame = 4;
afp->mFileDescription.mChannelsPerFrame = 2;
afp->mFileDescription.mBitsPerChannel = 16;
return 1;
}
347
348
AudioFilePlayer *
new_AudioFilePlayer (const FSRef * inFileRef)
349
{
350
SInt64 fileDataSize = 0;
351
352
353
AudioFilePlayer *afp =
(AudioFilePlayer *) SDL_malloc (sizeof (AudioFilePlayer));
354
355
if (afp == NULL)
return NULL;
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
SDL_memset (afp, '\0', sizeof (*afp));
#define SET_AUDIOFILEPLAYER_METHOD(m) afp->m = AudioFilePlayer_##m
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);
#undef SET_AUDIOFILEPLAYER_METHOD
if (!afp->OpenFile (afp, inFileRef, &fileDataSize)) {
SDL_free (afp);
375
376
return NULL;
}
377
378
/* we want about 4 seconds worth of data for the buffer */
379
380
381
382
int bytesPerSecond =
(UInt32) (4 * afp->mFileDescription.mSampleRate *
afp->mFileDescription.mBytesPerFrame);
383
#if DEBUG
384
printf ("File format:\n");
385
386
PrintStreamDesc (&afp->mFileDescription);
#endif
387
388
389
390
391
392
afp->mAudioFileManager = new_AudioFileManager (afp, afp->mForkRefNum,
fileDataSize,
bytesPerSecond);
if (afp->mAudioFileManager == NULL) {
delete_AudioFilePlayer (afp);
393
394
395
396
397
398
return NULL;
}
return afp;
}
399
/* vi: set ts=4 sw=4 expandtab: */