Mercurial > sdl-ios-xcode
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 |