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 }