Mercurial > mplayer.hg
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 } |