comparison libao2/ao_macosx.c @ 15646:f46eae5e271b

Mac OS X Audio with AudioUnits and AudioToolbox format converters, Patch by Chris Roccati<roccati@pobox.com
author nplourde
date Sun, 05 Jun 2005 14:47:26 +0000
parents 1f3e8d675f0a
children b53c4b26dc96
comparison
equal deleted inserted replaced
15645:9349c3c5c46a 15646:f46eae5e271b
35 * 35 *
36 * AC-3 and MPEG audio passthrough is possible, but I don't have 36 * AC-3 and MPEG audio passthrough is possible, but I don't have
37 * access to a sound card that supports it. 37 * access to a sound card that supports it.
38 */ 38 */
39 39
40 #include <CoreAudio/AudioHardware.h> 40 #include <CoreServices/CoreServices.h>
41 #include <AudioUnit/AudioUnit.h>
42 #include <AudioToolbox/AudioToolbox.h>
41 #include <stdio.h> 43 #include <stdio.h>
42 #include <string.h> 44 #include <string.h>
43 #include <stdlib.h> 45 #include <stdlib.h>
44 #include <inttypes.h> 46 #include <inttypes.h>
45 #include <pthread.h> 47 #include <pthread.h>
53 55
54 static ao_info_t info = 56 static ao_info_t info =
55 { 57 {
56 "Darwin/Mac OS X native audio output", 58 "Darwin/Mac OS X native audio output",
57 "macosx", 59 "macosx",
58 "Timothy J. Wood & Dan Christiansen", 60 "Timothy J. Wood & Dan Christiansen & Chris Roccati",
59 "" 61 ""
60 }; 62 };
61 63
62 LIBAO_EXTERN(macosx) 64 LIBAO_EXTERN(macosx)
63 65
68 * CoreAudio supposedly has an internal latency in the order of 2ms */ 70 * CoreAudio supposedly has an internal latency in the order of 2ms */
69 #define NUM_BUFS 32 71 #define NUM_BUFS 32
70 72
71 typedef struct ao_macosx_s 73 typedef struct ao_macosx_s
72 { 74 {
73 /* CoreAudio */ 75 /* AudioUnit */
74 AudioDeviceID outputDeviceID; 76 AudioUnit theOutputUnit;
75 AudioStreamBasicDescription outputStreamBasicDescription; 77 AudioConverterRef theConverter;
78 int packetSize;
76 79
77 /* Ring-buffer */ 80 /* Ring-buffer */
78 /* does not need explicit synchronization, but needs to allocate 81 /* does not need explicit synchronization, but needs to allocate
79 * (num_chunks + 1) * chunk_size memory to store num_chunks * chunk_size 82 * (num_chunks + 1) * chunk_size memory to store num_chunks * chunk_size
80 * data */ 83 * data */
81 unsigned char *buffer; 84 unsigned char *buffer;
85 unsigned char *chunk;
82 unsigned int buffer_len; ///< must always be (num_chunks + 1) * chunk_size 86 unsigned int buffer_len; ///< must always be (num_chunks + 1) * chunk_size
83 unsigned int num_chunks; 87 unsigned int num_chunks;
84 unsigned int chunk_size; 88 unsigned int chunk_size;
85 89
86 unsigned int buf_read_pos; 90 unsigned int buf_read_pos;
151 return len; 155 return len;
152 } 156 }
153 157
154 /* end ring buffer stuff */ 158 /* end ring buffer stuff */
155 159
156 /* The function that the CoreAudio thread calls when it wants more data */ 160 OSStatus ACComplexInputProc(AudioConverterRef inAudioConverter, UInt32 *ioNumberDataPackets, AudioBufferList *ioData, AudioStreamPacketDescription **outDataPacketDescription, void *inUserData)
157 static OSStatus audioDeviceIOProc(AudioDeviceID inDevice, const AudioTimeStamp *inNow, const AudioBufferList *inInputData, const AudioTimeStamp *inInputTime, AudioBufferList *outOutputData, const AudioTimeStamp *inOutputTime, void *inClientData) 161 {
158 { 162 int amt=buf_used();
159 outOutputData->mBuffers[0].mDataByteSize = 163 int req=(*ioNumberDataPackets)*ao->packetSize;
160 read_buffer((char *)outOutputData->mBuffers[0].mData, ao->chunk_size); 164
161 165
162 return 0; 166 ioData->mBuffers[0].mData = ao->chunk;
163 } 167 ioData->mBuffers[0].mDataByteSize = req;
164 168
169 // fprintf(stderr, "##### req=%d amt=%d #####\n", req, amt);
170
171 if(amt>req)
172 amt=req;
173
174 if(amt)
175 read_buffer((unsigned char *)ioData->mBuffers[0].mData, amt);
176
177 if(req-amt)
178 memset(ioData->mBuffers[0].mData+amt, 0, req-amt);
179
180 return noErr;
181 }
182
183 OSStatus theRenderProc(void *inRefCon, AudioUnitRenderActionFlags *inActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, UInt32 inNumFrames, AudioBufferList *ioData)
184 {
185 OSStatus err = noErr;
186 void *inInputDataProcUserData = NULL;
187 AudioStreamPacketDescription *outPacketDescription = NULL;
188
189
190 err = AudioConverterFillComplexBuffer(ao->theConverter, ACComplexInputProc, inInputDataProcUserData, &inNumFrames, ioData, outPacketDescription);
191
192 /*Parameters for AudioConverterFillComplexBuffer()
193 converter - the converter being used
194 ACComplexInputProc() - input procedure to supply data to the Audio Converter
195 inInputDataProcUserData - Used to hold any data that needs to be passed on.
196 inNumFrames - The amount of requested data. On output, this number is the amount actually received.
197 ioData - Buffer of the converted data recieved on return
198 outPacketDescription - contains the format of the returned data.
199 */
200
201 if(err)
202 ao_msg(MSGT_AO, MSGL_WARN, "AudioConverterFillComplexBuffer failed status %-8d\n", err);
203
204 return err;
205 }
165 206
166 static int control(int cmd,void *arg){ 207 static int control(int cmd,void *arg){
167 OSStatus status;
168 UInt32 propertySize;
169 ao_control_vol_t* vol = (ao_control_vol_t*)arg;
170 UInt32 stereoChannels[2];
171 static float volume=0.5;
172 switch (cmd) { 208 switch (cmd) {
173 case AOCONTROL_SET_DEVICE: 209 case AOCONTROL_SET_DEVICE:
174 case AOCONTROL_GET_DEVICE: 210 case AOCONTROL_GET_DEVICE:
175 /* unimplemented/meaningless */
176 return CONTROL_FALSE;
177 case AOCONTROL_GET_VOLUME: 211 case AOCONTROL_GET_VOLUME:
178 propertySize=sizeof(stereoChannels);
179 status = AudioDeviceGetProperty(ao->outputDeviceID, 0, false,
180 kAudioDevicePropertyPreferredChannelsForStereo, &propertySize,
181 &stereoChannels);
182 // printf("OSX: stereochannels %d ; %d \n",stereoChannels[0],stereoChannels[1]);
183 propertySize=sizeof(volume);
184 status = AudioDeviceGetProperty(ao->outputDeviceID, stereoChannels[0], false, kAudioDevicePropertyVolumeScalar, &propertySize, &volume);
185 // printf("OSX: get volume=%5.3f status=%d \n",volume,status);
186 vol->left=(int)(volume*100.0);
187 status = AudioDeviceGetProperty(ao->outputDeviceID, stereoChannels[1], false, kAudioDevicePropertyVolumeScalar, &propertySize, &volume);
188 vol->right=(int)(volume*100.0);
189 return CONTROL_TRUE;
190 case AOCONTROL_SET_VOLUME: 212 case AOCONTROL_SET_VOLUME:
191 propertySize=sizeof(stereoChannels);
192 status = AudioDeviceGetProperty(ao->outputDeviceID, 0, false,
193 kAudioDevicePropertyPreferredChannelsForStereo, &propertySize,
194 &stereoChannels);
195 // printf("OSX: stereochannels %d ; %d \n",stereoChannels[0],stereoChannels[1]);
196 propertySize=sizeof(volume);
197 volume=vol->left/100.0;
198 status = AudioDeviceSetProperty(ao->outputDeviceID, 0, stereoChannels[0], 0, kAudioDevicePropertyVolumeScalar, propertySize, &volume);
199 // printf("OSX: set volume=%5.3f status=%d\n",volume,status);
200 volume=vol->right/100.0;
201 status = AudioDeviceSetProperty(ao->outputDeviceID, 0, stereoChannels[1], 0, kAudioDevicePropertyVolumeScalar, propertySize, &volume);
202 return CONTROL_TRUE;
203 case AOCONTROL_QUERY_FORMAT: 213 case AOCONTROL_QUERY_FORMAT:
204 /* stick with what CoreAudio requests */ 214 /* Everything is currently unimplemented */
205 return CONTROL_FALSE; 215 return CONTROL_FALSE;
206 default: 216 default:
207 return CONTROL_FALSE; 217 return CONTROL_FALSE;
208 } 218 }
209 219
237 } 247 }
238 248
239 249
240 static int init(int rate,int channels,int format,int flags) 250 static int init(int rate,int channels,int format,int flags)
241 { 251 {
242 OSStatus status; 252 AudioStreamBasicDescription inDesc, outDesc;
243 UInt32 propertySize; 253 ComponentDescription desc;
244 int rc; 254 Component comp;
245 int i; 255 AURenderCallbackStruct renderCallback;
246 256 OSStatus err;
247 ao = (ao_macosx_t *)malloc(sizeof(ao_macosx_t)); 257 UInt32 size, maxFrames;
248 258
249 /* get default output device */ 259 ao = (ao_macosx_t *)malloc(sizeof(ao_macosx_t));
250 propertySize = sizeof(ao->outputDeviceID); 260
251 status = AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &propertySize, &(ao->outputDeviceID)); 261 // Build Description for the input format
252 if (status) { 262 memset(&inDesc, 0, sizeof(AudioStreamBasicDescription));
253 ao_msg(MSGT_AO,MSGL_WARN, 263 inDesc.mSampleRate=rate;
254 "AudioHardwareGetProperty returned %d\n", 264 inDesc.mFormatID=kAudioFormatLinearPCM;
255 (int)status); 265 inDesc.mChannelsPerFrame=channels;
256 return CONTROL_FALSE; 266 switch(format&AF_FORMAT_BITS_MASK){
267 case AF_FORMAT_8BIT:
268 inDesc.mBitsPerChannel=8;
269 break;
270 case AF_FORMAT_16BIT:
271 inDesc.mBitsPerChannel=16;
272 break;
273 case AF_FORMAT_24BIT:
274 inDesc.mBitsPerChannel=24;
275 break;
276 case AF_FORMAT_32BIT:
277 inDesc.mBitsPerChannel=32;
278 break;
279 default:
280 ao_msg(MSGT_AO, MSGL_WARN, "Unsupported format (0x%08x)\n", format);
281 return CONTROL_FALSE;
282 break;
283 }
284
285 if((format&AF_FORMAT_POINT_MASK)==AF_FORMAT_F) {
286 // float
287 inDesc.mFormatFlags = kAudioFormatFlagIsFloat|kAudioFormatFlagIsPacked;
257 } 288 }
258 289 else if((format&AF_FORMAT_SIGN_MASK)==AF_FORMAT_SI) {
259 if (ao->outputDeviceID == kAudioDeviceUnknown) { 290 // signed int
260 ao_msg(MSGT_AO,MSGL_WARN, "AudioHardwareGetProperty: ao->outputDeviceID is kAudioDeviceUnknown\n"); 291 inDesc.mFormatFlags = kAudioFormatFlagIsSignedInteger|kAudioFormatFlagIsPacked;
261 return CONTROL_FALSE;
262 } 292 }
263 293 else {
264 294 // unsigned int
265 propertySize = sizeof(ao->outputStreamBasicDescription); 295 inDesc.mFormatFlags = kAudioFormatFlagIsPacked;
266 status = AudioDeviceGetProperty(ao->outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &ao->outputStreamBasicDescription);
267 if(!status) print_format("default:",&ao->outputStreamBasicDescription);
268
269
270 #if 1
271 // dump supported format list:
272 { AudioStreamBasicDescription* p;
273 Boolean ow;
274 int i;
275 propertySize=0; //sizeof(p);
276 // status = AudioDeviceGetPropertyInfo(ao->outputDeviceID, 0, false, kAudioStreamPropertyPhysicalFormats, &propertySize, &ow);
277 status = AudioDeviceGetPropertyInfo(ao->outputDeviceID, 0, false, kAudioDevicePropertyStreamFormats, &propertySize, &ow);
278 if (status) {
279 ao_msg(MSGT_AO,MSGL_WARN, "AudioDeviceGetPropertyInfo returned 0x%X when getting kAudioDevicePropertyStreamFormats\n", (int)status);
280 }
281 p=malloc(propertySize);
282 // status = AudioDeviceGetProperty(ao->outputDeviceID, 0, false, kAudioStreamPropertyPhysicalFormats, &propertySize, p);
283 status = AudioDeviceGetProperty(ao->outputDeviceID, 0, false, kAudioDevicePropertyStreamFormats, &propertySize, p);
284 if (status) {
285 ao_msg(MSGT_AO,MSGL_WARN, "AudioDeviceGetProperty returned 0x%X when getting kAudioDevicePropertyStreamFormats\n", (int)status);
286 // return CONTROL_FALSE;
287 }
288 for(i=0;i<propertySize/sizeof(AudioStreamBasicDescription);i++)
289 print_format("support:",&p[i]);
290 // printf("FORMATS: (%d) %p %p %p %p\n",propertySize,p[0],p[1],p[2],p[3]);
291 free(p);
292 }
293 #endif
294
295 // fill in our wanted format, and let's see if the driver accepts it or
296 // offers some similar alternative:
297 propertySize = sizeof(ao->outputStreamBasicDescription);
298 memset(&ao->outputStreamBasicDescription,0,propertySize);
299 ao->outputStreamBasicDescription.mSampleRate=rate;
300 ao->outputStreamBasicDescription.mFormatID=kAudioFormatLinearPCM;
301 ao->outputStreamBasicDescription.mChannelsPerFrame=channels;
302
303 print_format("wanted: ",&ao->outputStreamBasicDescription);
304
305 // try 1: ask if it accepts our specific requirements?
306 propertySize = sizeof(ao->outputStreamBasicDescription);
307 // status = AudioDeviceGetProperty(ao->outputDeviceID, 0, false, kAudioStreamPropertyPhysicalFormatMatch, &propertySize, &ao->outputStreamBasicDescription);
308 status = AudioDeviceGetProperty(ao->outputDeviceID, 0, false, kAudioDevicePropertyStreamFormatMatch, &propertySize, &ao->outputStreamBasicDescription);
309 if (status) {
310 ao_msg(MSGT_AO,MSGL_V, "AudioDeviceGetProperty returned 0x%X when getting kAudioDevicePropertyStreamFormatMatch\n", (int)status);
311 return CONTROL_FALSE;
312 }
313
314 // propertySize = sizeof(ao->outputStreamBasicDescription);
315 // status = AudioDeviceGetProperty(ao->outputDeviceID, 0, false, kAudioDevicePropertyStreamFormatSupported, &propertySize, &ao->outputStreamBasicDescription);
316 // if (status) {
317 // ao_msg(MSGT_AO,MSGL_V, "AudioDeviceGetProperty returned 0x%X when getting kAudioDevicePropertyStreamFormatSupported\n", (int)status);
318 // }
319
320 // ok, now try to set the new (default or matched) audio format:
321 print_format("best: ",&ao->outputStreamBasicDescription);
322 propertySize = sizeof(ao->outputStreamBasicDescription);
323 status = AudioDeviceSetProperty(ao->outputDeviceID, 0, 0, false, kAudioDevicePropertyStreamFormat, propertySize, &ao->outputStreamBasicDescription);
324 if(status)
325 ao_msg(MSGT_AO,MSGL_WARN, "AudioDeviceSetProperty returned 0x%X when getting kAudioDevicePropertyStreamFormat\n", (int)status);
326
327 // see what did we get finally... we'll be forced to use this anyway :(
328 propertySize = sizeof(ao->outputStreamBasicDescription);
329 status = AudioDeviceGetProperty(ao->outputDeviceID, 0, false, kAudioDevicePropertyStreamFormat, &propertySize, &ao->outputStreamBasicDescription);
330 print_format("final: ",&ao->outputStreamBasicDescription);
331
332 /* get requested buffer length */
333 // TODO: set NUM_BUFS dinamically, based on buffer size!
334 propertySize = sizeof(ao->chunk_size);
335 status = AudioDeviceGetProperty(ao->outputDeviceID, 0, false, kAudioDevicePropertyBufferSize, &propertySize, &ao->chunk_size);
336 if (status) {
337 ao_msg(MSGT_AO,MSGL_WARN, "AudioDeviceGetProperty returned %d when getting kAudioDevicePropertyBufferSize\n", (int)status);
338 return CONTROL_FALSE;
339 }
340 ao_msg(MSGT_AO,MSGL_V, "%5d chunk size\n", (int)ao->chunk_size);
341
342 ao_data.samplerate = ao->outputStreamBasicDescription.mSampleRate;
343 ao_data.channels = channels;
344 ao_data.outburst = ao_data.buffersize = ao->chunk_size;
345 ao_data.bps =
346 ao_data.samplerate * ao->outputStreamBasicDescription.mBytesPerFrame;
347
348 if (ao->outputStreamBasicDescription.mFormatID == kAudioFormatLinearPCM) {
349 uint32_t flags = ao->outputStreamBasicDescription.mFormatFlags;
350 if (flags & kAudioFormatFlagIsFloat) {
351 ao_data.format = (flags&kAudioFormatFlagIsBigEndian) ? AF_FORMAT_FLOAT_BE : AF_FORMAT_FLOAT_LE;
352 } else {
353 ao_msg(MSGT_AO,MSGL_WARN, "Unsupported audio output "
354 "format 0x%X. Please report this to the developer\n", format);
355 return CONTROL_FALSE;
356 }
357
358 } else {
359 /* TODO: handle AFMT_AC3, AFMT_MPEG & friends */
360 ao_msg(MSGT_AO,MSGL_WARN, "Default Audio Device doesn't "
361 "support Linear PCM!\n");
362 return CONTROL_FALSE;
363 } 296 }
364 297
365 /* Allocate ring-buffer memory */ 298 if((format&AF_FORMAT_END_MASK)==AF_FORMAT_BE)
299 inDesc.mFormatFlags |= kAudioFormatFlagIsBigEndian;
300
301 inDesc.mFramesPerPacket = 1;
302 ao->packetSize = inDesc.mBytesPerPacket = inDesc.mBytesPerFrame = inDesc.mFramesPerPacket*channels*(inDesc.mBitsPerChannel/8);
303 print_format("source: ",&inDesc);
304
305 ao_data.samplerate = inDesc.mSampleRate;
306 ao_data.channels = inDesc.mChannelsPerFrame;
307 ao_data.outburst = ao_data.buffersize = ao->chunk_size;
308 ao_data.bps = ao_data.samplerate * inDesc.mBytesPerFrame;
309
310 desc.componentType = kAudioUnitType_Output;
311 desc.componentSubType = kAudioUnitSubType_DefaultOutput;
312 desc.componentManufacturer = kAudioUnitManufacturer_Apple;
313 desc.componentFlags = 0;
314 desc.componentFlagsMask = 0;
315
316 comp = FindNextComponent(NULL, &desc); //Finds an component that meets the desc spec's
317 if (comp == NULL) {
318 ao_msg(MSGT_AO, MSGL_WARN, "Unable to find Output Unit component\n");
319 return CONTROL_FALSE;
320 }
321
322 err = OpenAComponent(comp, &(ao->theOutputUnit)); //gains access to the services provided by the component
323 if (err) {
324 ao_msg(MSGT_AO, MSGL_WARN, "Unable to open Output Unit component (err=%d)\n", err);
325 return CONTROL_FALSE;
326 }
327
328 // Initialize AudioUnit
329 err = AudioUnitInitialize(ao->theOutputUnit);
330 if (err) {
331 ao_msg(MSGT_AO, MSGL_WARN, "Unable to initialize Output Unit component (err=%d)\n", err);
332 return CONTROL_FALSE;
333 }
334
335 size = sizeof(AudioStreamBasicDescription);
336 err = AudioUnitGetProperty(ao->theOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, 0, &outDesc, &size);
337 print_format("destination: ", &outDesc);
338 err = AudioUnitSetProperty(ao->theOutputUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &outDesc, size);
339
340 err = AudioConverterNew(&inDesc, &outDesc, &(ao->theConverter));
341 if (err) {
342 ao_msg(MSGT_AO, MSGL_WARN, "Unable to create the AudioConverter component (err=%d)\n", err);
343 return CONTROL_FALSE;
344 }
345
346 size=sizeof(UInt32);
347 maxFrames=8192; // This was calculated empirically. On MY system almost everything works more or less the same...
348 err = AudioUnitSetProperty(ao->theOutputUnit, kAudioUnitProperty_MaximumFramesPerSlice, kAudioUnitScope_Input, 0, &maxFrames, size);
349
350 if(err) {
351 ao_msg(MSGT_AO, MSGL_WARN, "Unable to set the maximum number of frames per slice!! (err=%d)\n", err);
352 return CONTROL_FALSE;
353 }
354
355 ao_msg(MSGT_AO, MSGL_DBG2, "Maximum number of frames per request %d (that is %d bytes)", err, maxFrames, maxFrames*inDesc.mBytesPerFrame);
356
357 ao->chunk_size = maxFrames*inDesc.mBytesPerFrame;
366 ao->num_chunks = NUM_BUFS; 358 ao->num_chunks = NUM_BUFS;
367 ao->buffer_len = (ao->num_chunks + 1) * ao->chunk_size; 359 ao->buffer_len = (ao->num_chunks + 1) * ao->chunk_size;
368 ao->buffer = (unsigned char *)malloc(ao->buffer_len); 360 ao->buffer = (unsigned char *)calloc(ao->num_chunks + 1, ao->chunk_size);
369 361 ao->chunk = (unsigned char*)calloc(1, ao->chunk_size);
370 362
371 /* Prepare for playback */ 363 memset(&renderCallback, 0, sizeof(AURenderCallbackStruct));
372 364 renderCallback.inputProc = theRenderProc;
373 /* Set the IO proc that CoreAudio will call when it needs data */ 365 renderCallback.inputProcRefCon = 0;
374 status = AudioDeviceAddIOProc(ao->outputDeviceID, audioDeviceIOProc, NULL); 366 err = AudioUnitSetProperty(ao->theOutputUnit, kAudioUnitProperty_SetRenderCallback, kAudioUnitScope_Input, 0, &renderCallback, sizeof(AURenderCallbackStruct));
375 if (status) { 367 if (err) {
376 ao_msg(MSGT_AO,MSGL_WARN, "AudioDeviceAddIOProc returned %d\n", (int)status); 368 ao_msg(MSGT_AO, MSGL_WARN, "Unable to set the render callback (err=%d)\n", err);
377 return CONTROL_FALSE; 369 return CONTROL_FALSE;
378 } 370 }
379 371
380 /* Start callback */
381 reset(); 372 reset();
382 373
383 return CONTROL_OK; 374 return CONTROL_OK;
384 } 375 }
385 376
424 int i; 415 int i;
425 OSErr status; 416 OSErr status;
426 417
427 reset(); 418 reset();
428 419
429 status = AudioDeviceRemoveIOProc(ao->outputDeviceID, audioDeviceIOProc); 420 AudioConverterDispose(ao->theConverter);
430 if (status) 421 AudioOutputUnitStop(ao->theOutputUnit);
431 ao_msg(MSGT_AO,MSGL_WARN, "AudioDeviceRemoveIOProc " 422 AudioUnitUninitialize(ao->theOutputUnit);
432 "returned %d\n", (int)status); 423 CloseComponent(ao->theOutputUnit);
433 424
425 free(ao->chunk);
434 free(ao->buffer); 426 free(ao->buffer);
435 free(ao); 427 free(ao);
436 } 428 }
437 429
438 430
439 /* stop playing, keep buffers (for pause) */ 431 /* stop playing, keep buffers (for pause) */
440 static void audio_pause() 432 static void audio_pause()
441 { 433 {
442 OSErr status; 434 OSErr status=noErr;
443 435
444 /* stop callback */ 436 /* stop callback */
445 status = AudioDeviceStop(ao->outputDeviceID, audioDeviceIOProc); 437 status=AudioOutputUnitStop(ao->theOutputUnit);
446 if (status) 438 if (status)
447 ao_msg(MSGT_AO,MSGL_WARN, "AudioDeviceStop returned %d\n", 439 ao_msg(MSGT_AO,MSGL_WARN, "AudioOutputUnitStop returned %d\n",
448 (int)status); 440 (int)status);
449 } 441 }
450 442
451 443
452 /* resume playing, after audio_pause() */ 444 /* resume playing, after audio_pause() */
453 static void audio_resume() 445 static void audio_resume()
454 { 446 {
455 OSErr status = AudioDeviceStart(ao->outputDeviceID, audioDeviceIOProc); 447 OSErr status=noErr;
448
449 status=AudioOutputUnitStart(ao->theOutputUnit);
456 if (status) 450 if (status)
457 ao_msg(MSGT_AO,MSGL_WARN, "AudioDeviceStart returned %d\n", 451 ao_msg(MSGT_AO,MSGL_WARN, "AudioOutputUnitStart returned %d\n",
458 (int)status); 452 (int)status);
459 } 453 }