|
1 /* |
|
2 * QEMU OS X CoreAudio audio driver |
|
3 * |
|
4 * Copyright (c) 2005 Mike Kronenberg |
|
5 * |
|
6 * Permission is hereby granted, free of charge, to any person obtaining a copy |
|
7 * of this software and associated documentation files (the "Software"), to deal |
|
8 * in the Software without restriction, including without limitation the rights |
|
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
10 * copies of the Software, and to permit persons to whom the Software is |
|
11 * furnished to do so, subject to the following conditions: |
|
12 * |
|
13 * The above copyright notice and this permission notice shall be included in |
|
14 * all copies or substantial portions of the Software. |
|
15 * |
|
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
|
19 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
|
22 * THE SOFTWARE. |
|
23 */ |
|
24 |
|
25 #include <CoreAudio/CoreAudio.h> |
|
26 #include <string.h> /* strerror */ |
|
27 #include <pthread.h> /* pthread_X */ |
|
28 |
|
29 #include "qemu-common.h" |
|
30 #include "audio.h" |
|
31 |
|
32 #define AUDIO_CAP "coreaudio" |
|
33 #include "audio_int.h" |
|
34 |
|
35 struct { |
|
36 int buffer_frames; |
|
37 int nbuffers; |
|
38 int isAtexit; |
|
39 } conf = { |
|
40 .buffer_frames = 512, |
|
41 .nbuffers = 4, |
|
42 .isAtexit = 0 |
|
43 }; |
|
44 |
|
45 typedef struct coreaudioVoiceOut { |
|
46 HWVoiceOut hw; |
|
47 pthread_mutex_t mutex; |
|
48 int isAtexit; |
|
49 AudioDeviceID outputDeviceID; |
|
50 UInt32 audioDevicePropertyBufferFrameSize; |
|
51 AudioStreamBasicDescription outputStreamBasicDescription; |
|
52 int live; |
|
53 int decr; |
|
54 int rpos; |
|
55 } coreaudioVoiceOut; |
|
56 |
|
57 static void coreaudio_logstatus (OSStatus status) |
|
58 { |
|
59 char *str = "BUG"; |
|
60 |
|
61 switch(status) { |
|
62 case kAudioHardwareNoError: |
|
63 str = "kAudioHardwareNoError"; |
|
64 break; |
|
65 |
|
66 case kAudioHardwareNotRunningError: |
|
67 str = "kAudioHardwareNotRunningError"; |
|
68 break; |
|
69 |
|
70 case kAudioHardwareUnspecifiedError: |
|
71 str = "kAudioHardwareUnspecifiedError"; |
|
72 break; |
|
73 |
|
74 case kAudioHardwareUnknownPropertyError: |
|
75 str = "kAudioHardwareUnknownPropertyError"; |
|
76 break; |
|
77 |
|
78 case kAudioHardwareBadPropertySizeError: |
|
79 str = "kAudioHardwareBadPropertySizeError"; |
|
80 break; |
|
81 |
|
82 case kAudioHardwareIllegalOperationError: |
|
83 str = "kAudioHardwareIllegalOperationError"; |
|
84 break; |
|
85 |
|
86 case kAudioHardwareBadDeviceError: |
|
87 str = "kAudioHardwareBadDeviceError"; |
|
88 break; |
|
89 |
|
90 case kAudioHardwareBadStreamError: |
|
91 str = "kAudioHardwareBadStreamError"; |
|
92 break; |
|
93 |
|
94 case kAudioHardwareUnsupportedOperationError: |
|
95 str = "kAudioHardwareUnsupportedOperationError"; |
|
96 break; |
|
97 |
|
98 case kAudioDeviceUnsupportedFormatError: |
|
99 str = "kAudioDeviceUnsupportedFormatError"; |
|
100 break; |
|
101 |
|
102 case kAudioDevicePermissionsError: |
|
103 str = "kAudioDevicePermissionsError"; |
|
104 break; |
|
105 |
|
106 default: |
|
107 AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status); |
|
108 return; |
|
109 } |
|
110 |
|
111 AUD_log (AUDIO_CAP, "Reason: %s\n", str); |
|
112 } |
|
113 |
|
114 static void GCC_FMT_ATTR (2, 3) coreaudio_logerr ( |
|
115 OSStatus status, |
|
116 const char *fmt, |
|
117 ... |
|
118 ) |
|
119 { |
|
120 va_list ap; |
|
121 |
|
122 va_start (ap, fmt); |
|
123 AUD_log (AUDIO_CAP, fmt, ap); |
|
124 va_end (ap); |
|
125 |
|
126 coreaudio_logstatus (status); |
|
127 } |
|
128 |
|
129 static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 ( |
|
130 OSStatus status, |
|
131 const char *typ, |
|
132 const char *fmt, |
|
133 ... |
|
134 ) |
|
135 { |
|
136 va_list ap; |
|
137 |
|
138 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); |
|
139 |
|
140 va_start (ap, fmt); |
|
141 AUD_vlog (AUDIO_CAP, fmt, ap); |
|
142 va_end (ap); |
|
143 |
|
144 coreaudio_logstatus (status); |
|
145 } |
|
146 |
|
147 static inline UInt32 isPlaying (AudioDeviceID outputDeviceID) |
|
148 { |
|
149 OSStatus status; |
|
150 UInt32 result = 0; |
|
151 UInt32 propertySize = sizeof(outputDeviceID); |
|
152 status = AudioDeviceGetProperty( |
|
153 outputDeviceID, 0, 0, |
|
154 kAudioDevicePropertyDeviceIsRunning, &propertySize, &result); |
|
155 if (status != kAudioHardwareNoError) { |
|
156 coreaudio_logerr(status, |
|
157 "Could not determine whether Device is playing\n"); |
|
158 } |
|
159 return result; |
|
160 } |
|
161 |
|
162 static void coreaudio_atexit (void) |
|
163 { |
|
164 conf.isAtexit = 1; |
|
165 } |
|
166 |
|
167 static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name) |
|
168 { |
|
169 int err; |
|
170 |
|
171 err = pthread_mutex_lock (&core->mutex); |
|
172 if (err) { |
|
173 dolog ("Could not lock voice for %s\nReason: %s\n", |
|
174 fn_name, strerror (err)); |
|
175 return -1; |
|
176 } |
|
177 return 0; |
|
178 } |
|
179 |
|
180 static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name) |
|
181 { |
|
182 int err; |
|
183 |
|
184 err = pthread_mutex_unlock (&core->mutex); |
|
185 if (err) { |
|
186 dolog ("Could not unlock voice for %s\nReason: %s\n", |
|
187 fn_name, strerror (err)); |
|
188 return -1; |
|
189 } |
|
190 return 0; |
|
191 } |
|
192 |
|
193 static int coreaudio_run_out (HWVoiceOut *hw) |
|
194 { |
|
195 int live, decr; |
|
196 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; |
|
197 |
|
198 if (coreaudio_lock (core, "coreaudio_run_out")) { |
|
199 return 0; |
|
200 } |
|
201 |
|
202 live = audio_pcm_hw_get_live_out (hw); |
|
203 |
|
204 if (core->decr > live) { |
|
205 ldebug ("core->decr %d live %d core->live %d\n", |
|
206 core->decr, |
|
207 live, |
|
208 core->live); |
|
209 } |
|
210 |
|
211 decr = audio_MIN (core->decr, live); |
|
212 core->decr -= decr; |
|
213 |
|
214 core->live = live - decr; |
|
215 hw->rpos = core->rpos; |
|
216 |
|
217 coreaudio_unlock (core, "coreaudio_run_out"); |
|
218 return decr; |
|
219 } |
|
220 |
|
221 /* callback to feed audiooutput buffer */ |
|
222 static OSStatus audioDeviceIOProc( |
|
223 AudioDeviceID inDevice, |
|
224 const AudioTimeStamp* inNow, |
|
225 const AudioBufferList* inInputData, |
|
226 const AudioTimeStamp* inInputTime, |
|
227 AudioBufferList* outOutputData, |
|
228 const AudioTimeStamp* inOutputTime, |
|
229 void* hwptr) |
|
230 { |
|
231 UInt32 frame, frameCount; |
|
232 float *out = outOutputData->mBuffers[0].mData; |
|
233 HWVoiceOut *hw = hwptr; |
|
234 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr; |
|
235 int rpos, live; |
|
236 struct st_sample *src; |
|
237 #ifndef FLOAT_MIXENG |
|
238 #ifdef RECIPROCAL |
|
239 const float scale = 1.f / UINT_MAX; |
|
240 #else |
|
241 const float scale = UINT_MAX; |
|
242 #endif |
|
243 #endif |
|
244 |
|
245 if (coreaudio_lock (core, "audioDeviceIOProc")) { |
|
246 inInputTime = 0; |
|
247 return 0; |
|
248 } |
|
249 |
|
250 frameCount = core->audioDevicePropertyBufferFrameSize; |
|
251 live = core->live; |
|
252 |
|
253 /* if there are not enough samples, set signal and return */ |
|
254 if (live < frameCount) { |
|
255 inInputTime = 0; |
|
256 coreaudio_unlock (core, "audioDeviceIOProc(empty)"); |
|
257 return 0; |
|
258 } |
|
259 |
|
260 rpos = core->rpos; |
|
261 src = hw->mix_buf + rpos; |
|
262 |
|
263 /* fill buffer */ |
|
264 for (frame = 0; frame < frameCount; frame++) { |
|
265 #ifdef FLOAT_MIXENG |
|
266 *out++ = src[frame].l; /* left channel */ |
|
267 *out++ = src[frame].r; /* right channel */ |
|
268 #else |
|
269 #ifdef RECIPROCAL |
|
270 *out++ = src[frame].l * scale; /* left channel */ |
|
271 *out++ = src[frame].r * scale; /* right channel */ |
|
272 #else |
|
273 *out++ = src[frame].l / scale; /* left channel */ |
|
274 *out++ = src[frame].r / scale; /* right channel */ |
|
275 #endif |
|
276 #endif |
|
277 } |
|
278 |
|
279 rpos = (rpos + frameCount) % hw->samples; |
|
280 core->decr += frameCount; |
|
281 core->rpos = rpos; |
|
282 |
|
283 coreaudio_unlock (core, "audioDeviceIOProc"); |
|
284 return 0; |
|
285 } |
|
286 |
|
287 static int coreaudio_write (SWVoiceOut *sw, void *buf, int len) |
|
288 { |
|
289 return audio_pcm_sw_write (sw, buf, len); |
|
290 } |
|
291 |
|
292 static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as) |
|
293 { |
|
294 OSStatus status; |
|
295 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; |
|
296 UInt32 propertySize; |
|
297 int err; |
|
298 const char *typ = "playback"; |
|
299 AudioValueRange frameRange; |
|
300 |
|
301 /* create mutex */ |
|
302 err = pthread_mutex_init(&core->mutex, NULL); |
|
303 if (err) { |
|
304 dolog("Could not create mutex\nReason: %s\n", strerror (err)); |
|
305 return -1; |
|
306 } |
|
307 |
|
308 audio_pcm_init_info (&hw->info, as); |
|
309 |
|
310 /* open default output device */ |
|
311 propertySize = sizeof(core->outputDeviceID); |
|
312 status = AudioHardwareGetProperty( |
|
313 kAudioHardwarePropertyDefaultOutputDevice, |
|
314 &propertySize, |
|
315 &core->outputDeviceID); |
|
316 if (status != kAudioHardwareNoError) { |
|
317 coreaudio_logerr2 (status, typ, |
|
318 "Could not get default output Device\n"); |
|
319 return -1; |
|
320 } |
|
321 if (core->outputDeviceID == kAudioDeviceUnknown) { |
|
322 dolog ("Could not initialize %s - Unknown Audiodevice\n", typ); |
|
323 return -1; |
|
324 } |
|
325 |
|
326 /* get minimum and maximum buffer frame sizes */ |
|
327 propertySize = sizeof(frameRange); |
|
328 status = AudioDeviceGetProperty( |
|
329 core->outputDeviceID, |
|
330 0, |
|
331 0, |
|
332 kAudioDevicePropertyBufferFrameSizeRange, |
|
333 &propertySize, |
|
334 &frameRange); |
|
335 if (status != kAudioHardwareNoError) { |
|
336 coreaudio_logerr2 (status, typ, |
|
337 "Could not get device buffer frame range\n"); |
|
338 return -1; |
|
339 } |
|
340 |
|
341 if (frameRange.mMinimum > conf.buffer_frames) { |
|
342 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; |
|
343 dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); |
|
344 } |
|
345 else if (frameRange.mMaximum < conf.buffer_frames) { |
|
346 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; |
|
347 dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); |
|
348 } |
|
349 else { |
|
350 core->audioDevicePropertyBufferFrameSize = conf.buffer_frames; |
|
351 } |
|
352 |
|
353 /* set Buffer Frame Size */ |
|
354 propertySize = sizeof(core->audioDevicePropertyBufferFrameSize); |
|
355 status = AudioDeviceSetProperty( |
|
356 core->outputDeviceID, |
|
357 NULL, |
|
358 0, |
|
359 false, |
|
360 kAudioDevicePropertyBufferFrameSize, |
|
361 propertySize, |
|
362 &core->audioDevicePropertyBufferFrameSize); |
|
363 if (status != kAudioHardwareNoError) { |
|
364 coreaudio_logerr2 (status, typ, |
|
365 "Could not set device buffer frame size %ld\n", |
|
366 core->audioDevicePropertyBufferFrameSize); |
|
367 return -1; |
|
368 } |
|
369 |
|
370 /* get Buffer Frame Size */ |
|
371 propertySize = sizeof(core->audioDevicePropertyBufferFrameSize); |
|
372 status = AudioDeviceGetProperty( |
|
373 core->outputDeviceID, |
|
374 0, |
|
375 false, |
|
376 kAudioDevicePropertyBufferFrameSize, |
|
377 &propertySize, |
|
378 &core->audioDevicePropertyBufferFrameSize); |
|
379 if (status != kAudioHardwareNoError) { |
|
380 coreaudio_logerr2 (status, typ, |
|
381 "Could not get device buffer frame size\n"); |
|
382 return -1; |
|
383 } |
|
384 hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize; |
|
385 |
|
386 /* get StreamFormat */ |
|
387 propertySize = sizeof(core->outputStreamBasicDescription); |
|
388 status = AudioDeviceGetProperty( |
|
389 core->outputDeviceID, |
|
390 0, |
|
391 false, |
|
392 kAudioDevicePropertyStreamFormat, |
|
393 &propertySize, |
|
394 &core->outputStreamBasicDescription); |
|
395 if (status != kAudioHardwareNoError) { |
|
396 coreaudio_logerr2 (status, typ, |
|
397 "Could not get Device Stream properties\n"); |
|
398 core->outputDeviceID = kAudioDeviceUnknown; |
|
399 return -1; |
|
400 } |
|
401 |
|
402 /* set Samplerate */ |
|
403 core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; |
|
404 propertySize = sizeof(core->outputStreamBasicDescription); |
|
405 status = AudioDeviceSetProperty( |
|
406 core->outputDeviceID, |
|
407 0, |
|
408 0, |
|
409 0, |
|
410 kAudioDevicePropertyStreamFormat, |
|
411 propertySize, |
|
412 &core->outputStreamBasicDescription); |
|
413 if (status != kAudioHardwareNoError) { |
|
414 coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n", |
|
415 as->freq); |
|
416 core->outputDeviceID = kAudioDeviceUnknown; |
|
417 return -1; |
|
418 } |
|
419 |
|
420 /* set Callback */ |
|
421 status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw); |
|
422 if (status != kAudioHardwareNoError) { |
|
423 coreaudio_logerr2 (status, typ, "Could not set IOProc\n"); |
|
424 core->outputDeviceID = kAudioDeviceUnknown; |
|
425 return -1; |
|
426 } |
|
427 |
|
428 /* start Playback */ |
|
429 if (!isPlaying(core->outputDeviceID)) { |
|
430 status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); |
|
431 if (status != kAudioHardwareNoError) { |
|
432 coreaudio_logerr2 (status, typ, "Could not start playback\n"); |
|
433 AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc); |
|
434 core->outputDeviceID = kAudioDeviceUnknown; |
|
435 return -1; |
|
436 } |
|
437 } |
|
438 |
|
439 return 0; |
|
440 } |
|
441 |
|
442 static void coreaudio_fini_out (HWVoiceOut *hw) |
|
443 { |
|
444 OSStatus status; |
|
445 int err; |
|
446 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; |
|
447 |
|
448 if (!conf.isAtexit) { |
|
449 /* stop playback */ |
|
450 if (isPlaying(core->outputDeviceID)) { |
|
451 status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); |
|
452 if (status != kAudioHardwareNoError) { |
|
453 coreaudio_logerr (status, "Could not stop playback\n"); |
|
454 } |
|
455 } |
|
456 |
|
457 /* remove callback */ |
|
458 status = AudioDeviceRemoveIOProc(core->outputDeviceID, |
|
459 audioDeviceIOProc); |
|
460 if (status != kAudioHardwareNoError) { |
|
461 coreaudio_logerr (status, "Could not remove IOProc\n"); |
|
462 } |
|
463 } |
|
464 core->outputDeviceID = kAudioDeviceUnknown; |
|
465 |
|
466 /* destroy mutex */ |
|
467 err = pthread_mutex_destroy(&core->mutex); |
|
468 if (err) { |
|
469 dolog("Could not destroy mutex\nReason: %s\n", strerror (err)); |
|
470 } |
|
471 } |
|
472 |
|
473 static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) |
|
474 { |
|
475 OSStatus status; |
|
476 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; |
|
477 |
|
478 switch (cmd) { |
|
479 case VOICE_ENABLE: |
|
480 /* start playback */ |
|
481 if (!isPlaying(core->outputDeviceID)) { |
|
482 status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc); |
|
483 if (status != kAudioHardwareNoError) { |
|
484 coreaudio_logerr (status, "Could not resume playback\n"); |
|
485 } |
|
486 } |
|
487 break; |
|
488 |
|
489 case VOICE_DISABLE: |
|
490 /* stop playback */ |
|
491 if (!conf.isAtexit) { |
|
492 if (isPlaying(core->outputDeviceID)) { |
|
493 status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc); |
|
494 if (status != kAudioHardwareNoError) { |
|
495 coreaudio_logerr (status, "Could not pause playback\n"); |
|
496 } |
|
497 } |
|
498 } |
|
499 break; |
|
500 } |
|
501 return 0; |
|
502 } |
|
503 |
|
504 static void *coreaudio_audio_init (void) |
|
505 { |
|
506 atexit(coreaudio_atexit); |
|
507 return &coreaudio_audio_init; |
|
508 } |
|
509 |
|
510 static void coreaudio_audio_fini (void *opaque) |
|
511 { |
|
512 (void) opaque; |
|
513 } |
|
514 |
|
515 static struct audio_option coreaudio_options[] = { |
|
516 {"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_frames, |
|
517 "Size of the buffer in frames", NULL, 0}, |
|
518 {"BUFFER_COUNT", AUD_OPT_INT, &conf.nbuffers, |
|
519 "Number of buffers", NULL, 0}, |
|
520 {NULL, 0, NULL, NULL, NULL, 0} |
|
521 }; |
|
522 |
|
523 static struct audio_pcm_ops coreaudio_pcm_ops = { |
|
524 coreaudio_init_out, |
|
525 coreaudio_fini_out, |
|
526 coreaudio_run_out, |
|
527 coreaudio_write, |
|
528 coreaudio_ctl_out, |
|
529 |
|
530 NULL, |
|
531 NULL, |
|
532 NULL, |
|
533 NULL, |
|
534 NULL |
|
535 }; |
|
536 |
|
537 struct audio_driver coreaudio_audio_driver = { |
|
538 INIT_FIELD (name = ) "coreaudio", |
|
539 INIT_FIELD (descr = ) |
|
540 "CoreAudio http://developer.apple.com/audio/coreaudio.html", |
|
541 INIT_FIELD (options = ) coreaudio_options, |
|
542 INIT_FIELD (init = ) coreaudio_audio_init, |
|
543 INIT_FIELD (fini = ) coreaudio_audio_fini, |
|
544 INIT_FIELD (pcm_ops = ) &coreaudio_pcm_ops, |
|
545 INIT_FIELD (can_be_default = ) 1, |
|
546 INIT_FIELD (max_voices_out = ) 1, |
|
547 INIT_FIELD (max_voices_in = ) 0, |
|
548 INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut), |
|
549 INIT_FIELD (voice_size_in = ) 0 |
|
550 }; |