comparison src/cdrom/macosx/AudioFilePlayer.c @ 1143:71a2648acc75

Replaced Mac OS X's C++ cdrom code with almost-direct translation to C. Sam requested this effort on the mailing list, apparently because of binary compatibility issues between 10.4 and earlier systems (or gcc4 and earlier compilers?). Works fine with SDL12/test/testcdrom.c, with this command line: ./testcdrom -status -list -play -sleep 5000 -pause -sleep 3000 -resume \ -sleep 5000 -stop -sleep 3000 -play -sleep 3000 -stop \ -sleep 3000 -eject Unix Makefiles work, XCode project still need updating for new filenames.
author Ryan C. Gordon <icculus@icculus.org>
date Thu, 22 Sep 2005 08:48:16 +0000
parents
children 3692456e7b0f
comparison
equal deleted inserted replaced
1142:c7376efecdb5 1143:71a2648acc75
1 /*
2 SDL - Simple DirectMedia Layer
3 Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002 Sam Lantinga
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
14
15 You should have received a copy of the GNU Library General Public
16 License along with this library; if not, write to the Free
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18
19 Sam Lantinga
20 slouken@libsdl.org
21
22 This file based on Apple sample code. We haven't changed the file name,
23 so if you want to see the original search for it on apple.com/developer
24 */
25
26 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
27 // AudioFilePlayer.cpp
28 //
29 #include "AudioFilePlayer.h"
30
31 /*
32 void ThrowResult (OSStatus result, const char* str)
33 {
34 SDL_SetError ("Error: %s %d", str, result);
35 throw result;
36 }
37 */
38
39 #if DEBUG
40 static void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
41 {
42 if (!inDesc) {
43 printf ("Can't print a NULL desc!\n");
44 return;
45 }
46
47 printf ("- - - - - - - - - - - - - - - - - - - -\n");
48 printf (" Sample Rate:%f\n", inDesc->mSampleRate);
49 printf (" Format ID:%s\n", (char*)&inDesc->mFormatID);
50 printf (" Format Flags:%lX\n", inDesc->mFormatFlags);
51 printf (" Bytes per Packet:%ld\n", inDesc->mBytesPerPacket);
52 printf (" Frames per Packet:%ld\n", inDesc->mFramesPerPacket);
53 printf (" Bytes per Frame:%ld\n", inDesc->mBytesPerFrame);
54 printf (" Channels per Frame:%ld\n", inDesc->mChannelsPerFrame);
55 printf (" Bits per Channel:%ld\n", inDesc->mBitsPerChannel);
56 printf ("- - - - - - - - - - - - - - - - - - - -\n");
57 }
58 #endif
59
60
61 static int AudioFilePlayer_SetDestination (AudioFilePlayer *afp, AudioUnit *inDestUnit)
62 {
63 //if (afp->mConnected) throw static_cast<OSStatus>(-1); //can't set dest if already engaged
64 if (afp->mConnected)
65 return 0 ;
66
67 memcpy(&afp->mPlayUnit, inDestUnit, sizeof (afp->mPlayUnit));
68
69 OSStatus result = noErr;
70
71
72 //we can "down" cast a component instance to a component
73 ComponentDescription desc;
74 result = GetComponentInfo ((Component)*inDestUnit, &desc, 0, 0, 0);
75 if (result) return 0; //THROW_RESULT("GetComponentInfo")
76
77 // we're going to use this to know which convert routine to call
78 // a v1 audio unit will have a type of 'aunt'
79 // a v2 audio unit will have one of several different types.
80 if (desc.componentType != kAudioUnitComponentType) {
81 result = badComponentInstance;
82 //THROW_RESULT("BAD COMPONENT")
83 if (result) return 0;
84 }
85
86 /* Set the input format of the audio unit. */
87 result = AudioUnitSetProperty (*inDestUnit,
88 kAudioUnitProperty_StreamFormat,
89 kAudioUnitScope_Input,
90 0,
91 &afp->mFileDescription,
92 sizeof (afp->mFileDescription));
93 //THROW_RESULT("AudioUnitSetProperty")
94 if (result) return 0;
95 return 1;
96 }
97
98 static void AudioFilePlayer_SetNotifier(AudioFilePlayer *afp, AudioFilePlayNotifier inNotifier, void *inRefCon)
99 {
100 afp->mNotifier = inNotifier;
101 afp->mRefCon = inRefCon;
102 }
103
104 static int AudioFilePlayer_IsConnected(AudioFilePlayer *afp)
105 {
106 return afp->mConnected;
107 }
108
109 static AudioUnit AudioFilePlayer_GetDestUnit(AudioFilePlayer *afp)
110 {
111 return afp->mPlayUnit;
112 }
113
114 static void AudioFilePlayer_Print(AudioFilePlayer *afp)
115 {
116 #if DEBUG
117 printf ("Is Connected:%s\n", (IsConnected() ? "true" : "false"));
118 printf ("- - - - - - - - - - - - - - \n");
119 #endif
120 }
121
122 static void AudioFilePlayer_SetStartFrame (AudioFilePlayer *afp, int frame)
123 {
124 SInt64 position = frame * 2352;
125
126 afp->mStartFrame = frame;
127 afp->mAudioFileManager->SetPosition (afp->mAudioFileManager, position);
128 }
129
130
131 static int AudioFilePlayer_GetCurrentFrame (AudioFilePlayer *afp)
132 {
133 return afp->mStartFrame + (afp->mAudioFileManager->GetByteCounter(afp->mAudioFileManager) / 2352);
134 }
135
136 static void AudioFilePlayer_SetStopFrame (AudioFilePlayer *afp, int frame)
137 {
138 SInt64 position = frame * 2352;
139
140 afp->mAudioFileManager->SetEndOfFile (afp->mAudioFileManager, position);
141 }
142
143 void delete_AudioFilePlayer(AudioFilePlayer *afp)
144 {
145 if (afp != NULL)
146 {
147 afp->Disconnect(afp);
148
149 if (afp->mAudioFileManager) {
150 delete_AudioFileManager(afp->mAudioFileManager);
151 afp->mAudioFileManager = 0;
152 }
153
154 if (afp->mForkRefNum) {
155 FSClose (afp->mForkRefNum);
156 afp->mForkRefNum = 0;
157 }
158 free(afp);
159 }
160 }
161
162 static int AudioFilePlayer_Connect(AudioFilePlayer *afp)
163 {
164 #if DEBUG
165 printf ("Connect:%x, engaged=%d\n", (int)afp->mPlayUnit, (afp->mConnected ? 1 : 0));
166 #endif
167 if (!afp->mConnected)
168 {
169 if (!afp->mAudioFileManager->DoConnect(afp->mAudioFileManager))
170 return 0;
171
172 // set the render callback for the file data to be supplied to the sound converter AU
173 afp->mInputCallback.inputProc = afp->mAudioFileManager->FileInputProc;
174 afp->mInputCallback.inputProcRefCon = afp->mAudioFileManager;
175
176 OSStatus result = AudioUnitSetProperty (afp->mPlayUnit,
177 kAudioUnitProperty_SetInputCallback,
178 kAudioUnitScope_Input,
179 0,
180 &afp->mInputCallback,
181 sizeof(afp->mInputCallback));
182 if (result) return 0; //THROW_RESULT("AudioUnitSetProperty")
183 afp->mConnected = 1;
184 }
185
186 return 1;
187 }
188
189 // warning noted, now please go away ;-)
190 // #warning This should redirect the calling of notification code to some other thread
191 static void AudioFilePlayer_DoNotification (AudioFilePlayer *afp, OSStatus inStatus)
192 {
193 if (afp->mNotifier) {
194 (*afp->mNotifier) (afp->mRefCon, inStatus);
195 } else {
196 SDL_SetError ("Notification posted with no notifier in place");
197
198 if (inStatus == kAudioFilePlay_FileIsFinished)
199 afp->Disconnect(afp);
200 else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun)
201 afp->Disconnect(afp);
202 }
203 }
204
205 static void AudioFilePlayer_Disconnect (AudioFilePlayer *afp)
206 {
207 #if DEBUG
208 printf ("Disconnect:%x,%ld, engaged=%d\n", (int)afp->mPlayUnit, 0, (afp->mConnected ? 1 : 0));
209 #endif
210 if (afp->mConnected)
211 {
212 afp->mConnected = 0;
213
214 afp->mInputCallback.inputProc = 0;
215 afp->mInputCallback.inputProcRefCon = 0;
216 OSStatus result = AudioUnitSetProperty (afp->mPlayUnit,
217 kAudioUnitProperty_SetInputCallback,
218 kAudioUnitScope_Input,
219 0,
220 &afp->mInputCallback,
221 sizeof(afp->mInputCallback));
222 if (result)
223 SDL_SetError ("AudioUnitSetProperty:RemoveInputCallback:%ld", result);
224
225 afp->mAudioFileManager->Disconnect(afp->mAudioFileManager);
226 }
227 }
228
229 typedef struct {
230 UInt32 offset;
231 UInt32 blockSize;
232 } SSNDData;
233
234 static int AudioFilePlayer_OpenFile (AudioFilePlayer *afp, const FSRef *inRef, SInt64 *outFileDataSize)
235 {
236 ContainerChunk chunkHeader;
237 ChunkHeader chunk;
238 SSNDData ssndData;
239
240 OSErr result;
241 HFSUniStr255 dfName;
242 ByteCount actual;
243 SInt64 offset;
244
245 // Open the data fork of the input file
246 result = FSGetDataForkName(&dfName);
247 if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSGetDataForkName")
248
249 result = FSOpenFork(inRef, dfName.length, dfName.unicode, fsRdPerm, &afp->mForkRefNum);
250 if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSOpenFork")
251
252 // Read the file header, and check if it's indeed an AIFC file
253 result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(chunkHeader), &chunkHeader, &actual);
254 if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")
255
256 if (chunkHeader.ckID != 'FORM') {
257 result = -1;
258 if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): chunk id is not 'FORM'");
259 }
260
261 if (chunkHeader.formType != 'AIFC') {
262 result = -1;
263 if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): file format is not 'AIFC'");
264 }
265
266 // Search for the SSND chunk. We ignore all compression etc. information
267 // in other chunks. Of course that is kind of evil, but for now we are lazy
268 // and rely on the cdfs to always give us the same fixed format.
269 // TODO: Parse the COMM chunk we currently skip to fill in mFileDescription.
270 offset = 0;
271 do {
272 result = FSReadFork(afp->mForkRefNum, fsFromMark, offset, sizeof(chunk), &chunk, &actual);
273 if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")
274
275 // Skip the chunk data
276 offset = chunk.ckSize;
277 } while (chunk.ckID != 'SSND');
278
279 // Read the header of the SSND chunk. After this, we are positioned right
280 // at the start of the audio data.
281 result = FSReadFork(afp->mForkRefNum, fsAtMark, 0, sizeof(ssndData), &ssndData, &actual);
282 if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork")
283
284 result = FSSetForkPosition(afp->mForkRefNum, fsFromMark, ssndData.offset);
285 if (result) return 0; //THROW_RESULT("AudioFilePlayer::OpenFile(): FSSetForkPosition")
286
287 // Data size
288 *outFileDataSize = chunk.ckSize - ssndData.offset - 8;
289
290 // File format
291 afp->mFileDescription.mSampleRate = 44100;
292 afp->mFileDescription.mFormatID = kAudioFormatLinearPCM;
293 afp->mFileDescription.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger;
294 afp->mFileDescription.mBytesPerPacket = 4;
295 afp->mFileDescription.mFramesPerPacket = 1;
296 afp->mFileDescription.mBytesPerFrame = 4;
297 afp->mFileDescription.mChannelsPerFrame = 2;
298 afp->mFileDescription.mBitsPerChannel = 16;
299
300 return 1;
301 }
302
303 AudioFilePlayer *new_AudioFilePlayer (const FSRef *inFileRef)
304 {
305 SInt64 fileDataSize = 0;
306
307 AudioFilePlayer *afp = (AudioFilePlayer *) malloc(sizeof (AudioFilePlayer));
308 if (afp == NULL)
309 return NULL;
310 memset(afp, '\0', sizeof (*afp));
311
312 #define SET_AUDIOFILEPLAYER_METHOD(m) afp->m = AudioFilePlayer_##m
313 SET_AUDIOFILEPLAYER_METHOD(SetDestination);
314 SET_AUDIOFILEPLAYER_METHOD(SetNotifier);
315 SET_AUDIOFILEPLAYER_METHOD(SetStartFrame);
316 SET_AUDIOFILEPLAYER_METHOD(GetCurrentFrame);
317 SET_AUDIOFILEPLAYER_METHOD(SetStopFrame);
318 SET_AUDIOFILEPLAYER_METHOD(Connect);
319 SET_AUDIOFILEPLAYER_METHOD(Disconnect);
320 SET_AUDIOFILEPLAYER_METHOD(DoNotification);
321 SET_AUDIOFILEPLAYER_METHOD(IsConnected);
322 SET_AUDIOFILEPLAYER_METHOD(GetDestUnit);
323 SET_AUDIOFILEPLAYER_METHOD(Print);
324 SET_AUDIOFILEPLAYER_METHOD(OpenFile);
325 #undef SET_AUDIOFILEPLAYER_METHOD
326
327 if (!afp->OpenFile (afp, inFileRef, &fileDataSize))
328 {
329 free(afp);
330 return NULL;
331 }
332
333 // we want about 4 seconds worth of data for the buffer
334 int bytesPerSecond = (UInt32) (4 * afp->mFileDescription.mSampleRate * afp->mFileDescription.mBytesPerFrame);
335
336 #if DEBUG
337 printf("File format:\n");
338 PrintStreamDesc (&afp->mFileDescription);
339 #endif
340
341 afp->mAudioFileManager = new_AudioFileManager(afp, afp->mForkRefNum,
342 fileDataSize,
343 bytesPerSecond);
344 if (afp->mAudioFileManager == NULL)
345 {
346 delete_AudioFilePlayer(afp);
347 return NULL;
348 }
349
350 return afp;
351 }
352