Mercurial > sdl-ios-xcode
comparison src/cdrom/macosx/AudioFilePlayer.cpp @ 613:9c6717a1c66f
Added MacOS X CD-ROM audio support (thanks Max and Darrell)
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Tue, 15 Apr 2003 16:33:56 +0000 (2003-04-15) |
parents | |
children | de1b2c3063b9 |
comparison
equal
deleted
inserted
replaced
612:0648505b1f8b | 613:9c6717a1c66f |
---|---|
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 extern const char* AudioFilePlayerErrorStr (OSStatus error) | |
32 { | |
33 const char *str; | |
34 | |
35 switch (error) { | |
36 case kAudioFileUnspecifiedError: str = "wht?"; break; | |
37 case kAudioFileUnsupportedFileTypeError: str = "typ?"; break; | |
38 case kAudioFileUnsupportedDataFormatError: str = "fmt?"; break; | |
39 case kAudioFileUnsupportedPropertyError: str = "pty?"; break; | |
40 case kAudioFileBadPropertySizeError: str = "!siz"; break; | |
41 case kAudioFileNotOptimizedError: str = "optm"; break; | |
42 case kAudioFilePermissionsError: str = "prm?"; break; | |
43 case kAudioFileFormatNameUnavailableError: str = "nme?"; break; | |
44 case kAudioFileInvalidChunkError: str = "chk?"; break; | |
45 case kAudioFileDoesNotAllow64BitDataSizeError: str = "off?"; break; | |
46 default: str = "error unspecified"; | |
47 } | |
48 | |
49 return str; | |
50 } | |
51 | |
52 void ThrowResult (OSStatus result, const char* str) | |
53 { | |
54 SDL_SetError ("Error: %s %d (%s)", | |
55 str, result, AudioFilePlayerErrorStr(result)); | |
56 throw result; | |
57 } | |
58 | |
59 #if DEBUG | |
60 void PrintStreamDesc (AudioStreamBasicDescription *inDesc) | |
61 { | |
62 if (!inDesc) { | |
63 printf ("Can't print a NULL desc!\n"); | |
64 return; | |
65 } | |
66 | |
67 printf ("- - - - - - - - - - - - - - - - - - - -\n"); | |
68 printf (" Sample Rate:%f\n", inDesc->mSampleRate); | |
69 printf (" Format ID:%s\n", (char*)&inDesc->mFormatID); | |
70 printf (" Format Flags:%lX\n", inDesc->mFormatFlags); | |
71 printf (" Bytes per Packet:%ld\n", inDesc->mBytesPerPacket); | |
72 printf (" Frames per Packet:%ld\n", inDesc->mFramesPerPacket); | |
73 printf (" Bytes per Frame:%ld\n", inDesc->mBytesPerFrame); | |
74 printf (" Channels per Frame:%ld\n", inDesc->mChannelsPerFrame); | |
75 printf (" Bits per Channel:%ld\n", inDesc->mBitsPerChannel); | |
76 printf ("- - - - - - - - - - - - - - - - - - - -\n"); | |
77 } | |
78 #endif | |
79 | |
80 OSStatus AudioFileManager::FileInputProc (void *inRefCon, | |
81 AudioUnitRenderActionFlags inActionFlags, | |
82 const AudioTimeStamp *inTimeStamp, | |
83 UInt32 inBusNumber, | |
84 AudioBuffer *ioData) | |
85 { | |
86 AudioFileManager* THIS = (AudioFileManager*)inRefCon; | |
87 return THIS->Render(*ioData); | |
88 } | |
89 | |
90 OSStatus AudioFileManager::Render (AudioBuffer &ioData) | |
91 { | |
92 OSStatus result = AudioConverterFillBuffer(mParentConverter, | |
93 AudioFileManager::ACInputProc, | |
94 this, | |
95 &ioData.mDataByteSize, | |
96 ioData.mData); | |
97 if (result) { | |
98 SDL_SetError ("AudioConverterFillBuffer:%ld\n", result); | |
99 mParent.DoNotification (result); | |
100 } else { | |
101 mByteCounter += ioData.mDataByteSize / 2; | |
102 AfterRender(); | |
103 } | |
104 return result; | |
105 } | |
106 | |
107 OSStatus AudioFileManager::ACInputProc (AudioConverterRef inAudioConverter, | |
108 UInt32* outDataSize, | |
109 void** outData, | |
110 void* inUserData) | |
111 { | |
112 AudioFileManager* THIS = (AudioFileManager*)inUserData; | |
113 return THIS->GetFileData(outData, outDataSize); | |
114 } | |
115 | |
116 AudioFileManager::~AudioFileManager () | |
117 { | |
118 if (mFileBuffer) { | |
119 free (mFileBuffer); | |
120 mFileBuffer = 0; | |
121 } | |
122 } | |
123 | |
124 AudioFilePlayer::AudioFilePlayer (const FSRef *inFileRef) | |
125 : mConnected (false), | |
126 mAudioFileManager (0), | |
127 mConverter (0), | |
128 mNotifier (0), | |
129 mStartFrame (0) | |
130 { | |
131 SInt64 fileDataSize = 0; | |
132 | |
133 OpenFile (inFileRef, fileDataSize); | |
134 | |
135 // we want about a seconds worth of data for the buffer | |
136 int secsBytes = UInt32 (mFileDescription.mSampleRate * mFileDescription.mBytesPerFrame); | |
137 | |
138 #if DEBUG | |
139 printf("File format:\n"); | |
140 PrintStreamDesc (&mFileDescription); | |
141 #endif | |
142 | |
143 //round to a 32K boundary | |
144 //if ((secsBytes & 0xFFFF8000) > (128 * 1024)) | |
145 //secsBytes &= 0xFFFF8000; | |
146 //else | |
147 //secsBytes = (secsBytes + 0x7FFF) & 0xFFFF8000; | |
148 | |
149 mAudioFileManager = new AudioFileReaderThread (*this, | |
150 mAudioFileID, | |
151 fileDataSize, | |
152 secsBytes); | |
153 } | |
154 | |
155 // you can put a rate scalar here to play the file faster or slower | |
156 // by multiplying the same rate by the desired factor | |
157 // eg fileSampleRate * 2 -> twice as fast | |
158 // before you create the AudioConverter | |
159 void AudioFilePlayer::SetDestination (AudioUnit &inDestUnit, | |
160 int inBusNumber) | |
161 { | |
162 if (mConnected) throw static_cast<OSStatus>(-1); //can't set dest if already engaged | |
163 | |
164 mPlayUnit = inDestUnit; | |
165 mBusNumber = inBusNumber; | |
166 | |
167 OSStatus result = noErr; | |
168 | |
169 if (mConverter) { | |
170 result = AudioConverterDispose (mConverter); | |
171 THROW_RESULT("AudioConverterDispose") | |
172 } | |
173 | |
174 AudioStreamBasicDescription destDesc; | |
175 UInt32 size = sizeof (destDesc); | |
176 result = AudioUnitGetProperty (inDestUnit, | |
177 kAudioUnitProperty_StreamFormat, | |
178 kAudioUnitScope_Input, | |
179 inBusNumber, | |
180 &destDesc, | |
181 &size); | |
182 THROW_RESULT("AudioUnitGetProperty") | |
183 | |
184 #if DEBUG | |
185 printf("Destination format:\n"); | |
186 PrintStreamDesc (&destDesc); | |
187 #endif | |
188 | |
189 //we can "down" cast a component instance to a component | |
190 ComponentDescription desc; | |
191 result = GetComponentInfo ((Component)inDestUnit, &desc, 0, 0, 0); | |
192 THROW_RESULT("GetComponentInfo") | |
193 | |
194 // we're going to use this to know which convert routine to call | |
195 // a v1 audio unit will have a type of 'aunt' | |
196 // a v2 audio unit will have one of several different types. | |
197 mIsAUNTUnit = (desc.componentType == kAudioUnitComponentType); | |
198 | |
199 if (!mIsAUNTUnit) { | |
200 result = badComponentInstance; | |
201 THROW_RESULT("BAD COMPONENT") | |
202 } | |
203 | |
204 | |
205 // HACK - the AIFF files on CDs are in little endian order! | |
206 if (mFileDescription.mFormatFlags == 0xE) | |
207 mFileDescription.mFormatFlags &= ~kAudioFormatFlagIsBigEndian; | |
208 | |
209 | |
210 result = AudioConverterNew (&mFileDescription, &destDesc, &mConverter); | |
211 THROW_RESULT("AudioConverterNew") | |
212 | |
213 | |
214 /* | |
215 // if we have a mono source, we're going to copy each channel into | |
216 // the destination's channel source... | |
217 if (mFileDescription.mChannelsPerFrame == 1) { | |
218 | |
219 SInt32* channelMap = new SInt32 [destDesc.mChannelsPerFrame]; | |
220 for (unsigned int i = 0; i < destDesc.mChannelsPerFrame; ++i) | |
221 channelMap[i] = 0; //set first channel to all output channels | |
222 | |
223 result = AudioConverterSetProperty(mConverter, | |
224 kAudioConverterChannelMap, | |
225 (sizeof(SInt32) * destDesc.mChannelsPerFrame), | |
226 channelMap); | |
227 THROW_RESULT("AudioConverterSetProperty") | |
228 | |
229 delete [] channelMap; | |
230 } | |
231 */ | |
232 assert (mFileDescription.mChannelsPerFrame == 2); | |
233 | |
234 #if 0 | |
235 // this uses the better quality SRC | |
236 UInt32 srcID = kAudioUnitSRCAlgorithm_Polyphase; | |
237 result = AudioConverterSetProperty(mConverter, | |
238 kAudioConverterSampleRateConverterAlgorithm, | |
239 sizeof(srcID), | |
240 &srcID); | |
241 THROW_RESULT("AudioConverterSetProperty") | |
242 #endif | |
243 } | |
244 | |
245 void AudioFilePlayer::SetStartFrame (int frame) | |
246 { | |
247 SInt64 position = frame * 2352; | |
248 | |
249 mStartFrame = frame; | |
250 mAudioFileManager->SetPosition (position); | |
251 } | |
252 | |
253 | |
254 int AudioFilePlayer::GetCurrentFrame () | |
255 { | |
256 return mStartFrame + (mAudioFileManager->GetByteCounter() / 2352); | |
257 } | |
258 | |
259 void AudioFilePlayer::SetStopFrame (int frame) | |
260 { | |
261 SInt64 position = frame * 2352; | |
262 | |
263 mAudioFileManager->SetEndOfFile (position); | |
264 } | |
265 | |
266 AudioFilePlayer::~AudioFilePlayer() | |
267 { | |
268 Disconnect(); | |
269 | |
270 if (mAudioFileManager) { | |
271 delete mAudioFileManager; | |
272 mAudioFileManager = 0; | |
273 } | |
274 | |
275 if (mAudioFileID) { | |
276 ::AudioFileClose (mAudioFileID); | |
277 mAudioFileID = 0; | |
278 } | |
279 | |
280 if (mConverter) { | |
281 AudioConverterDispose (mConverter); | |
282 mConverter = 0; | |
283 } | |
284 } | |
285 | |
286 void AudioFilePlayer::Connect() | |
287 { | |
288 #if DEBUG | |
289 printf ("Connect:%x,%ld, engaged=%d\n", (int)mPlayUnit, mBusNumber, (mConnected ? 1 : 0)); | |
290 #endif | |
291 if (!mConnected) | |
292 { | |
293 mAudioFileManager->Connect(mConverter); | |
294 | |
295 // set the render callback for the file data to be supplied to the sound converter AU | |
296 if (mIsAUNTUnit) { | |
297 mInputCallback.inputProc = AudioFileManager::FileInputProc; | |
298 mInputCallback.inputProcRefCon = mAudioFileManager; | |
299 | |
300 OSStatus result = AudioUnitSetProperty (mPlayUnit, | |
301 kAudioUnitProperty_SetInputCallback, | |
302 kAudioUnitScope_Input, | |
303 mBusNumber, | |
304 &mInputCallback, | |
305 sizeof(mInputCallback)); | |
306 THROW_RESULT("AudioUnitSetProperty") | |
307 } | |
308 mConnected = true; | |
309 } | |
310 } | |
311 | |
312 // warning noted, now please go away ;-) | |
313 // #warning This should redirect the calling of notification code to some other thread | |
314 void AudioFilePlayer::DoNotification (OSStatus inStatus) const | |
315 { | |
316 AudioFilePlayer* THIS = const_cast<AudioFilePlayer*>(this); | |
317 | |
318 if (mNotifier) { | |
319 (*mNotifier) (mRefCon, inStatus); | |
320 } | |
321 | |
322 else { | |
323 SDL_SetError ("Notification posted with no notifier in place"); | |
324 | |
325 if (inStatus == kAudioFilePlay_FileIsFinished) | |
326 THIS->Disconnect(); | |
327 else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun) | |
328 THIS->Disconnect(); | |
329 } | |
330 } | |
331 | |
332 void AudioFilePlayer::Disconnect () | |
333 { | |
334 #if DEBUG | |
335 printf ("Disconnect:%x,%ld, engaged=%d\n", (int)mPlayUnit, mBusNumber, (mConnected ? 1 : 0)); | |
336 #endif | |
337 if (mConnected) | |
338 { | |
339 mConnected = false; | |
340 | |
341 if (mIsAUNTUnit) { | |
342 mInputCallback.inputProc = 0; | |
343 mInputCallback.inputProcRefCon = 0; | |
344 OSStatus result = AudioUnitSetProperty (mPlayUnit, | |
345 kAudioUnitProperty_SetInputCallback, | |
346 kAudioUnitScope_Input, | |
347 mBusNumber, | |
348 &mInputCallback, | |
349 sizeof(mInputCallback)); | |
350 if (result) | |
351 SDL_SetError ("AudioUnitSetProperty:RemoveInputCallback:%ld", result); | |
352 | |
353 } | |
354 | |
355 mAudioFileManager->Disconnect(); | |
356 } | |
357 } | |
358 | |
359 void AudioFilePlayer::OpenFile (const FSRef *inRef, SInt64& outFileDataSize) | |
360 { | |
361 OSStatus result = AudioFileOpen (inRef, fsRdPerm, 0, &mAudioFileID); | |
362 THROW_RESULT("AudioFileOpen") | |
363 | |
364 UInt32 dataSize = sizeof(AudioStreamBasicDescription); | |
365 result = AudioFileGetProperty (mAudioFileID, | |
366 kAudioFilePropertyDataFormat, | |
367 &dataSize, | |
368 &mFileDescription); | |
369 THROW_RESULT("AudioFileGetProperty") | |
370 | |
371 dataSize = sizeof (SInt64); | |
372 result = AudioFileGetProperty (mAudioFileID, | |
373 kAudioFilePropertyAudioDataByteCount, | |
374 &dataSize, | |
375 &outFileDataSize); | |
376 THROW_RESULT("AudioFileGetProperty") | |
377 } |