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
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 }