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
42
static void
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
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
}
/* Set the input format of the audio unit. */
92
93
94
95
96
97
98
99
100
result = AudioUnitSetProperty(*inDestUnit,
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
129
#if DEBUG
printf("Is Connected:%s\n", (IsConnected()? "true" : "false"));
printf("- - - - - - - - - - - - - - \n");
130
131
132
#endif
}
133
134
static void
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
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
156
SInt64 position = frame * 2352;
afp->mAudioFileManager->SetEndOfFile(afp->mAudioFileManager, position);
157
}
158
159
160
void
delete_AudioFilePlayer(AudioFilePlayer * afp)
161
{
162
if (afp != NULL) {
163
afp->Disconnect(afp);
164
165
166
167
168
if (afp->mAudioFileManager) {
delete_AudioFileManager(afp->mAudioFileManager);
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
if (!afp->mConnected) {
186
187
188
if (!afp->mAudioFileManager->DoConnect(afp->mAudioFileManager))
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
{
if (afp->mNotifier) {
(*afp->mNotifier) (afp->mRefCon, inStatus);
} else {
215
216
SDL_SetError("Notification posted with no notifier in place");
217
218
219
220
221
222
223
if (inStatus == kAudioFilePlay_FileIsFinished)
afp->Disconnect(afp);
else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun)
afp->Disconnect(afp);
}
}
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
OSStatus result = AudioUnitSetProperty(afp->mPlayUnit,
kAudioUnitProperty_SetInputCallback,
kAudioUnitScope_Input,
0,
&afp->mInputCallback,
sizeof(afp->mInputCallback));
if (result)
SDL_SetError("AudioUnitSetProperty:RemoveInputCallback:%ld",
result);
245
246
247
248
249
afp->mAudioFileManager->Disconnect(afp->mAudioFileManager);
}
}
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
result = FSGetDataForkName(&dfName);
271
272
273
274
275
276
277
278
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
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
347
AudioFilePlayer *
new_AudioFilePlayer(const FSRef * inFileRef)
348
{
349
SInt64 fileDataSize = 0;
350
351
352
AudioFilePlayer *afp =
(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
#undef SET_AUDIOFILEPLAYER_METHOD
371
372
if (!afp->OpenFile(afp, inFileRef, &fileDataSize)) {
373
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
383
#if DEBUG
printf("File format:\n");
384
PrintStreamDesc(&afp->mFileDescription);
385
#endif
386
387
388
389
afp->mAudioFileManager = new_AudioFileManager(afp, afp->mForkRefNum,
fileDataSize,
bytesPerSecond);
390
if (afp->mAudioFileManager == NULL) {
391
392
393
394
395
396
397
delete_AudioFilePlayer(afp);
return NULL;
}
return afp;
}
398
/* vi: set ts=4 sw=4 expandtab: */