Mercurial > sdl-ios-xcode
comparison src/cdrom/macosx/AudioFilePlayer.cpp @ 768:de1b2c3063b9
Max has been reworking this code so it works on MacOS X 10.1
author | Sam Lantinga <slouken@libsdl.org> |
---|---|
date | Sun, 04 Jan 2004 16:20:28 +0000 |
parents | 9c6717a1c66f |
children | 5c5656163ebd |
comparison
equal
deleted
inserted
replaced
767:d9e79e31a7b7 | 768:de1b2c3063b9 |
---|---|
26 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 26 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
27 // AudioFilePlayer.cpp | 27 // AudioFilePlayer.cpp |
28 // | 28 // |
29 #include "AudioFilePlayer.h" | 29 #include "AudioFilePlayer.h" |
30 | 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) | 31 void ThrowResult (OSStatus result, const char* str) |
53 { | 32 { |
54 SDL_SetError ("Error: %s %d (%s)", | 33 SDL_SetError ("Error: %s %d", str, result); |
55 str, result, AudioFilePlayerErrorStr(result)); | |
56 throw result; | 34 throw result; |
57 } | 35 } |
58 | 36 |
59 #if DEBUG | 37 #if DEBUG |
60 void PrintStreamDesc (AudioStreamBasicDescription *inDesc) | 38 void PrintStreamDesc (AudioStreamBasicDescription *inDesc) |
131 SInt64 fileDataSize = 0; | 109 SInt64 fileDataSize = 0; |
132 | 110 |
133 OpenFile (inFileRef, fileDataSize); | 111 OpenFile (inFileRef, fileDataSize); |
134 | 112 |
135 // we want about a seconds worth of data for the buffer | 113 // we want about a seconds worth of data for the buffer |
136 int secsBytes = UInt32 (mFileDescription.mSampleRate * mFileDescription.mBytesPerFrame); | 114 int bytesPerSecond = UInt32 (mFileDescription.mSampleRate * mFileDescription.mBytesPerFrame); |
137 | 115 |
138 #if DEBUG | 116 #if DEBUG |
139 printf("File format:\n"); | 117 printf("File format:\n"); |
140 PrintStreamDesc (&mFileDescription); | 118 PrintStreamDesc (&mFileDescription); |
141 #endif | 119 #endif |
142 | 120 |
143 //round to a 32K boundary | 121 mAudioFileManager = new AudioFileManager (*this, |
144 //if ((secsBytes & 0xFFFF8000) > (128 * 1024)) | 122 mForkRefNum, |
145 //secsBytes &= 0xFFFF8000; | |
146 //else | |
147 //secsBytes = (secsBytes + 0x7FFF) & 0xFFFF8000; | |
148 | |
149 mAudioFileManager = new AudioFileReaderThread (*this, | |
150 mAudioFileID, | |
151 fileDataSize, | 123 fileDataSize, |
152 secsBytes); | 124 bytesPerSecond); |
153 } | 125 } |
154 | 126 |
155 // you can put a rate scalar here to play the file faster or slower | 127 // you can put a rate scalar here to play the file faster or slower |
156 // by multiplying the same rate by the desired factor | 128 // by multiplying the same rate by the desired factor |
157 // eg fileSampleRate * 2 -> twice as fast | 129 // eg fileSampleRate * 2 -> twice as fast |
158 // before you create the AudioConverter | 130 // before you create the AudioConverter |
159 void AudioFilePlayer::SetDestination (AudioUnit &inDestUnit, | 131 void AudioFilePlayer::SetDestination (AudioUnit &inDestUnit, |
160 int inBusNumber) | 132 int inBusNumber) |
161 { | 133 { |
162 if (mConnected) throw static_cast<OSStatus>(-1); //can't set dest if already engaged | 134 if (mConnected) throw static_cast<OSStatus>(-1); //can't set dest if already engaged |
163 | 135 |
164 mPlayUnit = inDestUnit; | 136 mPlayUnit = inDestUnit; |
165 mBusNumber = inBusNumber; | 137 mBusNumber = inBusNumber; |
192 THROW_RESULT("GetComponentInfo") | 164 THROW_RESULT("GetComponentInfo") |
193 | 165 |
194 // we're going to use this to know which convert routine to call | 166 // we're going to use this to know which convert routine to call |
195 // a v1 audio unit will have a type of 'aunt' | 167 // a v1 audio unit will have a type of 'aunt' |
196 // a v2 audio unit will have one of several different types. | 168 // a v2 audio unit will have one of several different types. |
197 mIsAUNTUnit = (desc.componentType == kAudioUnitComponentType); | 169 if (desc.componentType != kAudioUnitComponentType) { |
198 | |
199 if (!mIsAUNTUnit) { | |
200 result = badComponentInstance; | 170 result = badComponentInstance; |
201 THROW_RESULT("BAD COMPONENT") | 171 THROW_RESULT("BAD COMPONENT") |
202 } | 172 } |
203 | 173 |
204 | 174 |
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); | 175 result = AudioConverterNew (&mFileDescription, &destDesc, &mConverter); |
211 THROW_RESULT("AudioConverterNew") | 176 THROW_RESULT("AudioConverterNew") |
212 | 177 |
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 | 178 #if 0 |
235 // this uses the better quality SRC | 179 // this uses the better quality SRC |
236 UInt32 srcID = kAudioUnitSRCAlgorithm_Polyphase; | 180 UInt32 srcID = kAudioUnitSRCAlgorithm_Polyphase; |
237 result = AudioConverterSetProperty(mConverter, | 181 result = AudioConverterSetProperty(mConverter, |
238 kAudioConverterSampleRateConverterAlgorithm, | 182 kAudioConverterSampleRateConverterAlgorithm, |
270 if (mAudioFileManager) { | 214 if (mAudioFileManager) { |
271 delete mAudioFileManager; | 215 delete mAudioFileManager; |
272 mAudioFileManager = 0; | 216 mAudioFileManager = 0; |
273 } | 217 } |
274 | 218 |
275 if (mAudioFileID) { | 219 if (mForkRefNum) { |
276 ::AudioFileClose (mAudioFileID); | 220 FSClose (mForkRefNum); |
277 mAudioFileID = 0; | 221 mForkRefNum = 0; |
278 } | 222 } |
279 | 223 |
280 if (mConverter) { | 224 if (mConverter) { |
281 AudioConverterDispose (mConverter); | 225 AudioConverterDispose (mConverter); |
282 mConverter = 0; | 226 mConverter = 0; |
291 if (!mConnected) | 235 if (!mConnected) |
292 { | 236 { |
293 mAudioFileManager->Connect(mConverter); | 237 mAudioFileManager->Connect(mConverter); |
294 | 238 |
295 // set the render callback for the file data to be supplied to the sound converter AU | 239 // set the render callback for the file data to be supplied to the sound converter AU |
296 if (mIsAUNTUnit) { | 240 mInputCallback.inputProc = AudioFileManager::FileInputProc; |
297 mInputCallback.inputProc = AudioFileManager::FileInputProc; | 241 mInputCallback.inputProcRefCon = mAudioFileManager; |
298 mInputCallback.inputProcRefCon = mAudioFileManager; | 242 |
299 | 243 OSStatus result = AudioUnitSetProperty (mPlayUnit, |
300 OSStatus result = AudioUnitSetProperty (mPlayUnit, | 244 kAudioUnitProperty_SetInputCallback, |
301 kAudioUnitProperty_SetInputCallback, | 245 kAudioUnitScope_Input, |
302 kAudioUnitScope_Input, | 246 mBusNumber, |
303 mBusNumber, | 247 &mInputCallback, |
304 &mInputCallback, | 248 sizeof(mInputCallback)); |
305 sizeof(mInputCallback)); | 249 THROW_RESULT("AudioUnitSetProperty") |
306 THROW_RESULT("AudioUnitSetProperty") | |
307 } | |
308 mConnected = true; | 250 mConnected = true; |
309 } | 251 } |
310 } | 252 } |
311 | 253 |
312 // warning noted, now please go away ;-) | 254 // warning noted, now please go away ;-) |
315 { | 257 { |
316 AudioFilePlayer* THIS = const_cast<AudioFilePlayer*>(this); | 258 AudioFilePlayer* THIS = const_cast<AudioFilePlayer*>(this); |
317 | 259 |
318 if (mNotifier) { | 260 if (mNotifier) { |
319 (*mNotifier) (mRefCon, inStatus); | 261 (*mNotifier) (mRefCon, inStatus); |
320 } | 262 } else { |
321 | |
322 else { | |
323 SDL_SetError ("Notification posted with no notifier in place"); | 263 SDL_SetError ("Notification posted with no notifier in place"); |
324 | 264 |
325 if (inStatus == kAudioFilePlay_FileIsFinished) | 265 if (inStatus == kAudioFilePlay_FileIsFinished) |
326 THIS->Disconnect(); | 266 THIS->Disconnect(); |
327 else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun) | 267 else if (inStatus != kAudioFilePlayErr_FilePlayUnderrun) |
336 #endif | 276 #endif |
337 if (mConnected) | 277 if (mConnected) |
338 { | 278 { |
339 mConnected = false; | 279 mConnected = false; |
340 | 280 |
341 if (mIsAUNTUnit) { | 281 mInputCallback.inputProc = 0; |
342 mInputCallback.inputProc = 0; | 282 mInputCallback.inputProcRefCon = 0; |
343 mInputCallback.inputProcRefCon = 0; | 283 OSStatus result = AudioUnitSetProperty (mPlayUnit, |
344 OSStatus result = AudioUnitSetProperty (mPlayUnit, | 284 kAudioUnitProperty_SetInputCallback, |
345 kAudioUnitProperty_SetInputCallback, | 285 kAudioUnitScope_Input, |
346 kAudioUnitScope_Input, | 286 mBusNumber, |
347 mBusNumber, | 287 &mInputCallback, |
348 &mInputCallback, | 288 sizeof(mInputCallback)); |
349 sizeof(mInputCallback)); | 289 if (result) |
350 if (result) | 290 SDL_SetError ("AudioUnitSetProperty:RemoveInputCallback:%ld", result); |
351 SDL_SetError ("AudioUnitSetProperty:RemoveInputCallback:%ld", result); | 291 |
352 | |
353 } | |
354 | |
355 mAudioFileManager->Disconnect(); | 292 mAudioFileManager->Disconnect(); |
356 } | 293 } |
357 } | 294 } |
358 | 295 |
296 struct SSNDData { | |
297 UInt32 offset; | |
298 UInt32 blockSize; | |
299 }; | |
300 | |
359 void AudioFilePlayer::OpenFile (const FSRef *inRef, SInt64& outFileDataSize) | 301 void AudioFilePlayer::OpenFile (const FSRef *inRef, SInt64& outFileDataSize) |
360 { | 302 { |
361 OSStatus result = AudioFileOpen (inRef, fsRdPerm, 0, &mAudioFileID); | 303 ContainerChunk chunkHeader; |
362 THROW_RESULT("AudioFileOpen") | 304 ChunkHeader chunk; |
363 | 305 SSNDData ssndData; |
364 UInt32 dataSize = sizeof(AudioStreamBasicDescription); | 306 |
365 result = AudioFileGetProperty (mAudioFileID, | 307 OSErr result; |
366 kAudioFilePropertyDataFormat, | 308 HFSUniStr255 dfName; |
367 &dataSize, | 309 ByteCount actual; |
368 &mFileDescription); | 310 SInt64 offset; |
369 THROW_RESULT("AudioFileGetProperty") | 311 |
370 | 312 // Open the data fork of the input file |
371 dataSize = sizeof (SInt64); | 313 result = FSGetDataForkName(&dfName); |
372 result = AudioFileGetProperty (mAudioFileID, | 314 THROW_RESULT("AudioFilePlayer::OpenFile(): FSGetDataForkName") |
373 kAudioFilePropertyAudioDataByteCount, | 315 |
374 &dataSize, | 316 result = FSOpenFork(inRef, dfName.length, dfName.unicode, fsRdPerm, &mForkRefNum); |
375 &outFileDataSize); | 317 THROW_RESULT("AudioFilePlayer::OpenFile(): FSOpenFork") |
376 THROW_RESULT("AudioFileGetProperty") | 318 |
377 } | 319 // Read the file header, and check if it's indeed an AIFC file |
320 result = FSReadFork(mForkRefNum, fsAtMark, 0, sizeof(chunkHeader), &chunkHeader, &actual); | |
321 THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork") | |
322 | |
323 if (chunkHeader.ckID != 'FORM') { | |
324 result = -1; | |
325 THROW_RESULT("AudioFilePlayer::OpenFile(): chunk id is not 'FORM'"); | |
326 } | |
327 | |
328 if (chunkHeader.formType != 'AIFC') { | |
329 result = -1; | |
330 THROW_RESULT("AudioFilePlayer::OpenFile(): file format is not 'AIFC'"); | |
331 } | |
332 | |
333 // Search for the SSND chunk. We ignore all compression etc. information | |
334 // in other chunks. Of course that is kind of evil, but for now we are lazy | |
335 // and rely on the cdfs to always give us the same fixed format. | |
336 // TODO: Parse the COMM chunk we currently skip to fill in mFileDescription. | |
337 offset = 0; | |
338 do { | |
339 result = FSReadFork(mForkRefNum, fsFromMark, offset, sizeof(chunk), &chunk, &actual); | |
340 THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork") | |
341 | |
342 // Skip the chunk data | |
343 offset = chunk.ckSize; | |
344 } while (chunk.ckID != 'SSND'); | |
345 | |
346 // Read the header of the SSND chunk. After this, we are positioned right | |
347 // at the start of the audio data. | |
348 result = FSReadFork(mForkRefNum, fsAtMark, 0, sizeof(ssndData), &ssndData, &actual); | |
349 THROW_RESULT("AudioFilePlayer::OpenFile(): FSReadFork") | |
350 | |
351 result = FSSetForkPosition(mForkRefNum, fsFromMark, ssndData.offset); | |
352 THROW_RESULT("AudioFilePlayer::OpenFile(): FSSetForkPosition") | |
353 | |
354 // Data size | |
355 outFileDataSize = chunk.ckSize - ssndData.offset; | |
356 | |
357 // File format | |
358 mFileDescription.mSampleRate = 44100; | |
359 mFileDescription.mFormatID = kAudioFormatLinearPCM; | |
360 mFileDescription.mFormatFlags = kLinearPCMFormatFlagIsPacked | kLinearPCMFormatFlagIsSignedInteger; | |
361 mFileDescription.mBytesPerPacket = 4; | |
362 mFileDescription.mFramesPerPacket = 1; | |
363 mFileDescription.mBytesPerFrame = 4; | |
364 mFileDescription.mChannelsPerFrame = 2; | |
365 mFileDescription.mBitsPerChannel = 16; | |
366 } |