]>
Commit | Line | Data |
---|---|---|
1d14ffa9 FB |
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 | ||
6086a565 | 25 | #include "qemu/osdep.h" |
1d14ffa9 | 26 | #include <CoreAudio/CoreAudio.h> |
1d14ffa9 FB |
27 | #include <pthread.h> /* pthread_X */ |
28 | ||
749bc4bf PB |
29 | #include "qemu-common.h" |
30 | #include "audio.h" | |
1d14ffa9 FB |
31 | |
32 | #define AUDIO_CAP "coreaudio" | |
33 | #include "audio_int.h" | |
34 | ||
624d1fc3 PM |
35 | #ifndef MAC_OS_X_VERSION_10_6 |
36 | #define MAC_OS_X_VERSION_10_6 1060 | |
37 | #endif | |
38 | ||
d1f52a1d | 39 | typedef struct { |
1d14ffa9 | 40 | int buffer_frames; |
e59c1139 | 41 | int nbuffers; |
d1f52a1d | 42 | } CoreaudioConf; |
1d14ffa9 FB |
43 | |
44 | typedef struct coreaudioVoiceOut { | |
45 | HWVoiceOut hw; | |
46 | pthread_mutex_t mutex; | |
47 | AudioDeviceID outputDeviceID; | |
5e941d4b | 48 | UInt32 audioDevicePropertyBufferFrameSize; |
1d14ffa9 | 49 | AudioStreamBasicDescription outputStreamBasicDescription; |
2f79a18f | 50 | AudioDeviceIOProcID ioprocid; |
1d14ffa9 FB |
51 | int live; |
52 | int decr; | |
53 | int rpos; | |
54 | } coreaudioVoiceOut; | |
55 | ||
624d1fc3 PM |
56 | #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6 |
57 | /* The APIs used here only become available from 10.6 */ | |
58 | ||
59 | static OSStatus coreaudio_get_voice(AudioDeviceID *id) | |
60 | { | |
61 | UInt32 size = sizeof(*id); | |
62 | AudioObjectPropertyAddress addr = { | |
63 | kAudioHardwarePropertyDefaultOutputDevice, | |
64 | kAudioObjectPropertyScopeGlobal, | |
65 | kAudioObjectPropertyElementMaster | |
66 | }; | |
67 | ||
68 | return AudioObjectGetPropertyData(kAudioObjectSystemObject, | |
69 | &addr, | |
70 | 0, | |
71 | NULL, | |
72 | &size, | |
73 | id); | |
74 | } | |
2d99f629 PM |
75 | |
76 | static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, | |
77 | AudioValueRange *framerange) | |
78 | { | |
79 | UInt32 size = sizeof(*framerange); | |
80 | AudioObjectPropertyAddress addr = { | |
81 | kAudioDevicePropertyBufferFrameSizeRange, | |
82 | kAudioDevicePropertyScopeOutput, | |
83 | kAudioObjectPropertyElementMaster | |
84 | }; | |
85 | ||
86 | return AudioObjectGetPropertyData(id, | |
87 | &addr, | |
88 | 0, | |
89 | NULL, | |
90 | &size, | |
91 | framerange); | |
92 | } | |
93 | ||
94 | static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) | |
95 | { | |
96 | UInt32 size = sizeof(*framesize); | |
97 | AudioObjectPropertyAddress addr = { | |
98 | kAudioDevicePropertyBufferFrameSize, | |
99 | kAudioDevicePropertyScopeOutput, | |
100 | kAudioObjectPropertyElementMaster | |
101 | }; | |
102 | ||
103 | return AudioObjectGetPropertyData(id, | |
104 | &addr, | |
105 | 0, | |
106 | NULL, | |
107 | &size, | |
108 | framesize); | |
109 | } | |
110 | ||
111 | static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) | |
112 | { | |
113 | UInt32 size = sizeof(*framesize); | |
114 | AudioObjectPropertyAddress addr = { | |
115 | kAudioDevicePropertyBufferFrameSize, | |
116 | kAudioDevicePropertyScopeOutput, | |
117 | kAudioObjectPropertyElementMaster | |
118 | }; | |
119 | ||
120 | return AudioObjectSetPropertyData(id, | |
121 | &addr, | |
122 | 0, | |
123 | NULL, | |
124 | size, | |
125 | framesize); | |
126 | } | |
127 | ||
128 | static OSStatus coreaudio_get_streamformat(AudioDeviceID id, | |
129 | AudioStreamBasicDescription *d) | |
130 | { | |
131 | UInt32 size = sizeof(*d); | |
132 | AudioObjectPropertyAddress addr = { | |
133 | kAudioDevicePropertyStreamFormat, | |
134 | kAudioDevicePropertyScopeOutput, | |
135 | kAudioObjectPropertyElementMaster | |
136 | }; | |
137 | ||
138 | return AudioObjectGetPropertyData(id, | |
139 | &addr, | |
140 | 0, | |
141 | NULL, | |
142 | &size, | |
143 | d); | |
144 | } | |
145 | ||
146 | static OSStatus coreaudio_set_streamformat(AudioDeviceID id, | |
147 | AudioStreamBasicDescription *d) | |
148 | { | |
149 | UInt32 size = sizeof(*d); | |
150 | AudioObjectPropertyAddress addr = { | |
151 | kAudioDevicePropertyStreamFormat, | |
152 | kAudioDevicePropertyScopeOutput, | |
153 | kAudioObjectPropertyElementMaster | |
154 | }; | |
155 | ||
156 | return AudioObjectSetPropertyData(id, | |
157 | &addr, | |
158 | 0, | |
159 | NULL, | |
160 | size, | |
161 | d); | |
162 | } | |
163 | ||
164 | static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) | |
165 | { | |
166 | UInt32 size = sizeof(*result); | |
167 | AudioObjectPropertyAddress addr = { | |
168 | kAudioDevicePropertyDeviceIsRunning, | |
169 | kAudioDevicePropertyScopeOutput, | |
170 | kAudioObjectPropertyElementMaster | |
171 | }; | |
172 | ||
173 | return AudioObjectGetPropertyData(id, | |
174 | &addr, | |
175 | 0, | |
176 | NULL, | |
177 | &size, | |
178 | result); | |
179 | } | |
624d1fc3 PM |
180 | #else |
181 | /* Legacy versions of functions using deprecated APIs */ | |
182 | ||
88a0f830 PM |
183 | static OSStatus coreaudio_get_voice(AudioDeviceID *id) |
184 | { | |
185 | UInt32 size = sizeof(*id); | |
186 | ||
187 | return AudioHardwareGetProperty( | |
188 | kAudioHardwarePropertyDefaultOutputDevice, | |
189 | &size, | |
190 | id); | |
191 | } | |
192 | ||
95a860f6 PM |
193 | static OSStatus coreaudio_get_framesizerange(AudioDeviceID id, |
194 | AudioValueRange *framerange) | |
195 | { | |
196 | UInt32 size = sizeof(*framerange); | |
197 | ||
198 | return AudioDeviceGetProperty( | |
199 | id, | |
200 | 0, | |
201 | 0, | |
202 | kAudioDevicePropertyBufferFrameSizeRange, | |
203 | &size, | |
204 | framerange); | |
205 | } | |
206 | ||
207 | static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize) | |
208 | { | |
209 | UInt32 size = sizeof(*framesize); | |
210 | ||
211 | return AudioDeviceGetProperty( | |
212 | id, | |
213 | 0, | |
214 | false, | |
215 | kAudioDevicePropertyBufferFrameSize, | |
216 | &size, | |
217 | framesize); | |
218 | } | |
219 | ||
220 | static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize) | |
221 | { | |
222 | UInt32 size = sizeof(*framesize); | |
223 | ||
224 | return AudioDeviceSetProperty( | |
225 | id, | |
226 | NULL, | |
227 | 0, | |
228 | false, | |
229 | kAudioDevicePropertyBufferFrameSize, | |
230 | size, | |
231 | framesize); | |
232 | } | |
233 | ||
234 | static OSStatus coreaudio_get_streamformat(AudioDeviceID id, | |
235 | AudioStreamBasicDescription *d) | |
236 | { | |
237 | UInt32 size = sizeof(*d); | |
238 | ||
239 | return AudioDeviceGetProperty( | |
240 | id, | |
241 | 0, | |
242 | false, | |
243 | kAudioDevicePropertyStreamFormat, | |
244 | &size, | |
245 | d); | |
246 | } | |
247 | ||
248 | static OSStatus coreaudio_set_streamformat(AudioDeviceID id, | |
249 | AudioStreamBasicDescription *d) | |
250 | { | |
251 | UInt32 size = sizeof(*d); | |
252 | ||
253 | return AudioDeviceSetProperty( | |
254 | id, | |
255 | 0, | |
256 | 0, | |
257 | 0, | |
258 | kAudioDevicePropertyStreamFormat, | |
259 | size, | |
260 | d); | |
261 | } | |
262 | ||
263 | static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result) | |
264 | { | |
265 | UInt32 size = sizeof(*result); | |
266 | ||
267 | return AudioDeviceGetProperty( | |
268 | id, | |
269 | 0, | |
270 | 0, | |
271 | kAudioDevicePropertyDeviceIsRunning, | |
272 | &size, | |
273 | result); | |
274 | } | |
2d99f629 | 275 | #endif |
95a860f6 | 276 | |
1d14ffa9 FB |
277 | static void coreaudio_logstatus (OSStatus status) |
278 | { | |
d9cbb0f3 | 279 | const char *str = "BUG"; |
1d14ffa9 FB |
280 | |
281 | switch(status) { | |
282 | case kAudioHardwareNoError: | |
283 | str = "kAudioHardwareNoError"; | |
284 | break; | |
285 | ||
286 | case kAudioHardwareNotRunningError: | |
287 | str = "kAudioHardwareNotRunningError"; | |
288 | break; | |
289 | ||
290 | case kAudioHardwareUnspecifiedError: | |
291 | str = "kAudioHardwareUnspecifiedError"; | |
292 | break; | |
293 | ||
294 | case kAudioHardwareUnknownPropertyError: | |
295 | str = "kAudioHardwareUnknownPropertyError"; | |
296 | break; | |
297 | ||
298 | case kAudioHardwareBadPropertySizeError: | |
299 | str = "kAudioHardwareBadPropertySizeError"; | |
300 | break; | |
301 | ||
302 | case kAudioHardwareIllegalOperationError: | |
303 | str = "kAudioHardwareIllegalOperationError"; | |
304 | break; | |
305 | ||
306 | case kAudioHardwareBadDeviceError: | |
307 | str = "kAudioHardwareBadDeviceError"; | |
308 | break; | |
309 | ||
310 | case kAudioHardwareBadStreamError: | |
311 | str = "kAudioHardwareBadStreamError"; | |
312 | break; | |
313 | ||
314 | case kAudioHardwareUnsupportedOperationError: | |
315 | str = "kAudioHardwareUnsupportedOperationError"; | |
316 | break; | |
317 | ||
318 | case kAudioDeviceUnsupportedFormatError: | |
319 | str = "kAudioDeviceUnsupportedFormatError"; | |
320 | break; | |
321 | ||
322 | case kAudioDevicePermissionsError: | |
323 | str = "kAudioDevicePermissionsError"; | |
324 | break; | |
325 | ||
326 | default: | |
744d3644 | 327 | AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status); |
1d14ffa9 FB |
328 | return; |
329 | } | |
330 | ||
331 | AUD_log (AUDIO_CAP, "Reason: %s\n", str); | |
332 | } | |
333 | ||
334 | static void GCC_FMT_ATTR (2, 3) coreaudio_logerr ( | |
335 | OSStatus status, | |
336 | const char *fmt, | |
337 | ... | |
338 | ) | |
339 | { | |
340 | va_list ap; | |
341 | ||
342 | va_start (ap, fmt); | |
343 | AUD_log (AUDIO_CAP, fmt, ap); | |
344 | va_end (ap); | |
345 | ||
346 | coreaudio_logstatus (status); | |
347 | } | |
348 | ||
349 | static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 ( | |
350 | OSStatus status, | |
351 | const char *typ, | |
352 | const char *fmt, | |
353 | ... | |
354 | ) | |
355 | { | |
356 | va_list ap; | |
357 | ||
c0fe3827 | 358 | AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ); |
1d14ffa9 FB |
359 | |
360 | va_start (ap, fmt); | |
361 | AUD_vlog (AUDIO_CAP, fmt, ap); | |
362 | va_end (ap); | |
363 | ||
364 | coreaudio_logstatus (status); | |
365 | } | |
366 | ||
5e941d4b FB |
367 | static inline UInt32 isPlaying (AudioDeviceID outputDeviceID) |
368 | { | |
369 | OSStatus status; | |
370 | UInt32 result = 0; | |
95a860f6 | 371 | status = coreaudio_get_isrunning(outputDeviceID, &result); |
5e941d4b FB |
372 | if (status != kAudioHardwareNoError) { |
373 | coreaudio_logerr(status, | |
374 | "Could not determine whether Device is playing\n"); | |
375 | } | |
376 | return result; | |
377 | } | |
378 | ||
1d14ffa9 FB |
379 | static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name) |
380 | { | |
381 | int err; | |
382 | ||
383 | err = pthread_mutex_lock (&core->mutex); | |
384 | if (err) { | |
c0fe3827 | 385 | dolog ("Could not lock voice for %s\nReason: %s\n", |
1d14ffa9 FB |
386 | fn_name, strerror (err)); |
387 | return -1; | |
388 | } | |
389 | return 0; | |
390 | } | |
391 | ||
392 | static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name) | |
393 | { | |
394 | int err; | |
395 | ||
396 | err = pthread_mutex_unlock (&core->mutex); | |
397 | if (err) { | |
c0fe3827 | 398 | dolog ("Could not unlock voice for %s\nReason: %s\n", |
1d14ffa9 FB |
399 | fn_name, strerror (err)); |
400 | return -1; | |
401 | } | |
402 | return 0; | |
403 | } | |
404 | ||
bdff253c | 405 | static int coreaudio_run_out (HWVoiceOut *hw, int live) |
1d14ffa9 | 406 | { |
bdff253c | 407 | int decr; |
1d14ffa9 FB |
408 | coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; |
409 | ||
410 | if (coreaudio_lock (core, "coreaudio_run_out")) { | |
411 | return 0; | |
412 | } | |
413 | ||
1d14ffa9 FB |
414 | if (core->decr > live) { |
415 | ldebug ("core->decr %d live %d core->live %d\n", | |
416 | core->decr, | |
417 | live, | |
418 | core->live); | |
419 | } | |
420 | ||
421 | decr = audio_MIN (core->decr, live); | |
422 | core->decr -= decr; | |
423 | ||
424 | core->live = live - decr; | |
425 | hw->rpos = core->rpos; | |
426 | ||
427 | coreaudio_unlock (core, "coreaudio_run_out"); | |
428 | return decr; | |
429 | } | |
430 | ||
431 | /* callback to feed audiooutput buffer */ | |
432 | static OSStatus audioDeviceIOProc( | |
433 | AudioDeviceID inDevice, | |
434 | const AudioTimeStamp* inNow, | |
435 | const AudioBufferList* inInputData, | |
436 | const AudioTimeStamp* inInputTime, | |
437 | AudioBufferList* outOutputData, | |
438 | const AudioTimeStamp* inOutputTime, | |
439 | void* hwptr) | |
440 | { | |
5e941d4b | 441 | UInt32 frame, frameCount; |
1d14ffa9 FB |
442 | float *out = outOutputData->mBuffers[0].mData; |
443 | HWVoiceOut *hw = hwptr; | |
444 | coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr; | |
445 | int rpos, live; | |
1ea879e5 | 446 | struct st_sample *src; |
1d14ffa9 FB |
447 | #ifndef FLOAT_MIXENG |
448 | #ifdef RECIPROCAL | |
449 | const float scale = 1.f / UINT_MAX; | |
450 | #else | |
451 | const float scale = UINT_MAX; | |
452 | #endif | |
453 | #endif | |
454 | ||
455 | if (coreaudio_lock (core, "audioDeviceIOProc")) { | |
456 | inInputTime = 0; | |
457 | return 0; | |
458 | } | |
459 | ||
5e941d4b | 460 | frameCount = core->audioDevicePropertyBufferFrameSize; |
1d14ffa9 FB |
461 | live = core->live; |
462 | ||
463 | /* if there are not enough samples, set signal and return */ | |
464 | if (live < frameCount) { | |
465 | inInputTime = 0; | |
466 | coreaudio_unlock (core, "audioDeviceIOProc(empty)"); | |
467 | return 0; | |
468 | } | |
469 | ||
470 | rpos = core->rpos; | |
471 | src = hw->mix_buf + rpos; | |
472 | ||
473 | /* fill buffer */ | |
474 | for (frame = 0; frame < frameCount; frame++) { | |
475 | #ifdef FLOAT_MIXENG | |
476 | *out++ = src[frame].l; /* left channel */ | |
477 | *out++ = src[frame].r; /* right channel */ | |
478 | #else | |
479 | #ifdef RECIPROCAL | |
480 | *out++ = src[frame].l * scale; /* left channel */ | |
481 | *out++ = src[frame].r * scale; /* right channel */ | |
482 | #else | |
483 | *out++ = src[frame].l / scale; /* left channel */ | |
484 | *out++ = src[frame].r / scale; /* right channel */ | |
485 | #endif | |
486 | #endif | |
487 | } | |
488 | ||
1d14ffa9 | 489 | rpos = (rpos + frameCount) % hw->samples; |
5e941d4b | 490 | core->decr += frameCount; |
1d14ffa9 FB |
491 | core->rpos = rpos; |
492 | ||
493 | coreaudio_unlock (core, "audioDeviceIOProc"); | |
494 | return 0; | |
495 | } | |
496 | ||
497 | static int coreaudio_write (SWVoiceOut *sw, void *buf, int len) | |
498 | { | |
499 | return audio_pcm_sw_write (sw, buf, len); | |
500 | } | |
501 | ||
5706db1d KZ |
502 | static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, |
503 | void *drv_opaque) | |
1d14ffa9 FB |
504 | { |
505 | OSStatus status; | |
506 | coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | |
1d14ffa9 | 507 | int err; |
5e941d4b FB |
508 | const char *typ = "playback"; |
509 | AudioValueRange frameRange; | |
d1f52a1d | 510 | CoreaudioConf *conf = drv_opaque; |
1d14ffa9 FB |
511 | |
512 | /* create mutex */ | |
513 | err = pthread_mutex_init(&core->mutex, NULL); | |
514 | if (err) { | |
c0fe3827 | 515 | dolog("Could not create mutex\nReason: %s\n", strerror (err)); |
1d14ffa9 FB |
516 | return -1; |
517 | } | |
518 | ||
d929eba5 | 519 | audio_pcm_init_info (&hw->info, as); |
1d14ffa9 | 520 | |
88a0f830 | 521 | status = coreaudio_get_voice(&core->outputDeviceID); |
1d14ffa9 FB |
522 | if (status != kAudioHardwareNoError) { |
523 | coreaudio_logerr2 (status, typ, | |
c0fe3827 | 524 | "Could not get default output Device\n"); |
1d14ffa9 FB |
525 | return -1; |
526 | } | |
527 | if (core->outputDeviceID == kAudioDeviceUnknown) { | |
c0fe3827 | 528 | dolog ("Could not initialize %s - Unknown Audiodevice\n", typ); |
1d14ffa9 FB |
529 | return -1; |
530 | } | |
531 | ||
5e941d4b | 532 | /* get minimum and maximum buffer frame sizes */ |
95a860f6 PM |
533 | status = coreaudio_get_framesizerange(core->outputDeviceID, |
534 | &frameRange); | |
5e941d4b FB |
535 | if (status != kAudioHardwareNoError) { |
536 | coreaudio_logerr2 (status, typ, | |
537 | "Could not get device buffer frame range\n"); | |
538 | return -1; | |
539 | } | |
540 | ||
d1f52a1d | 541 | if (frameRange.mMinimum > conf->buffer_frames) { |
5e941d4b FB |
542 | core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; |
543 | dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); | |
544 | } | |
d1f52a1d | 545 | else if (frameRange.mMaximum < conf->buffer_frames) { |
5e941d4b FB |
546 | core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; |
547 | dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); | |
548 | } | |
549 | else { | |
d1f52a1d | 550 | core->audioDevicePropertyBufferFrameSize = conf->buffer_frames; |
5e941d4b FB |
551 | } |
552 | ||
553 | /* set Buffer Frame Size */ | |
95a860f6 PM |
554 | status = coreaudio_set_framesize(core->outputDeviceID, |
555 | &core->audioDevicePropertyBufferFrameSize); | |
1d14ffa9 FB |
556 | if (status != kAudioHardwareNoError) { |
557 | coreaudio_logerr2 (status, typ, | |
cbc36cb0 AF |
558 | "Could not set device buffer frame size %" PRIu32 "\n", |
559 | (uint32_t)core->audioDevicePropertyBufferFrameSize); | |
1d14ffa9 FB |
560 | return -1; |
561 | } | |
562 | ||
5e941d4b | 563 | /* get Buffer Frame Size */ |
95a860f6 PM |
564 | status = coreaudio_get_framesize(core->outputDeviceID, |
565 | &core->audioDevicePropertyBufferFrameSize); | |
1d14ffa9 | 566 | if (status != kAudioHardwareNoError) { |
5e941d4b FB |
567 | coreaudio_logerr2 (status, typ, |
568 | "Could not get device buffer frame size\n"); | |
1d14ffa9 FB |
569 | return -1; |
570 | } | |
d1f52a1d | 571 | hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize; |
1d14ffa9 FB |
572 | |
573 | /* get StreamFormat */ | |
95a860f6 PM |
574 | status = coreaudio_get_streamformat(core->outputDeviceID, |
575 | &core->outputStreamBasicDescription); | |
1d14ffa9 FB |
576 | if (status != kAudioHardwareNoError) { |
577 | coreaudio_logerr2 (status, typ, | |
c0fe3827 | 578 | "Could not get Device Stream properties\n"); |
1d14ffa9 FB |
579 | core->outputDeviceID = kAudioDeviceUnknown; |
580 | return -1; | |
581 | } | |
582 | ||
583 | /* set Samplerate */ | |
5e941d4b | 584 | core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; |
95a860f6 PM |
585 | status = coreaudio_set_streamformat(core->outputDeviceID, |
586 | &core->outputStreamBasicDescription); | |
1d14ffa9 | 587 | if (status != kAudioHardwareNoError) { |
575b5dc4 FB |
588 | coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n", |
589 | as->freq); | |
1d14ffa9 FB |
590 | core->outputDeviceID = kAudioDeviceUnknown; |
591 | return -1; | |
592 | } | |
593 | ||
594 | /* set Callback */ | |
2f79a18f PM |
595 | core->ioprocid = NULL; |
596 | status = AudioDeviceCreateIOProcID(core->outputDeviceID, | |
597 | audioDeviceIOProc, | |
598 | hw, | |
599 | &core->ioprocid); | |
600 | if (status != kAudioHardwareNoError || core->ioprocid == NULL) { | |
c0fe3827 | 601 | coreaudio_logerr2 (status, typ, "Could not set IOProc\n"); |
1d14ffa9 FB |
602 | core->outputDeviceID = kAudioDeviceUnknown; |
603 | return -1; | |
604 | } | |
605 | ||
606 | /* start Playback */ | |
5e941d4b | 607 | if (!isPlaying(core->outputDeviceID)) { |
2f79a18f | 608 | status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); |
1d14ffa9 | 609 | if (status != kAudioHardwareNoError) { |
c0fe3827 | 610 | coreaudio_logerr2 (status, typ, "Could not start playback\n"); |
2f79a18f | 611 | AudioDeviceDestroyIOProcID(core->outputDeviceID, core->ioprocid); |
1d14ffa9 FB |
612 | core->outputDeviceID = kAudioDeviceUnknown; |
613 | return -1; | |
614 | } | |
1d14ffa9 FB |
615 | } |
616 | ||
617 | return 0; | |
618 | } | |
619 | ||
620 | static void coreaudio_fini_out (HWVoiceOut *hw) | |
621 | { | |
622 | OSStatus status; | |
623 | int err; | |
624 | coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | |
625 | ||
a384c205 | 626 | if (!audio_is_cleaning_up()) { |
5e941d4b FB |
627 | /* stop playback */ |
628 | if (isPlaying(core->outputDeviceID)) { | |
2f79a18f | 629 | status = AudioDeviceStop(core->outputDeviceID, core->ioprocid); |
5e941d4b FB |
630 | if (status != kAudioHardwareNoError) { |
631 | coreaudio_logerr (status, "Could not stop playback\n"); | |
632 | } | |
1d14ffa9 | 633 | } |
1d14ffa9 | 634 | |
5e941d4b | 635 | /* remove callback */ |
2f79a18f PM |
636 | status = AudioDeviceDestroyIOProcID(core->outputDeviceID, |
637 | core->ioprocid); | |
5e941d4b FB |
638 | if (status != kAudioHardwareNoError) { |
639 | coreaudio_logerr (status, "Could not remove IOProc\n"); | |
640 | } | |
1d14ffa9 FB |
641 | } |
642 | core->outputDeviceID = kAudioDeviceUnknown; | |
643 | ||
644 | /* destroy mutex */ | |
645 | err = pthread_mutex_destroy(&core->mutex); | |
646 | if (err) { | |
c0fe3827 | 647 | dolog("Could not destroy mutex\nReason: %s\n", strerror (err)); |
1d14ffa9 FB |
648 | } |
649 | } | |
650 | ||
651 | static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...) | |
652 | { | |
653 | OSStatus status; | |
654 | coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | |
655 | ||
656 | switch (cmd) { | |
657 | case VOICE_ENABLE: | |
658 | /* start playback */ | |
5e941d4b | 659 | if (!isPlaying(core->outputDeviceID)) { |
2f79a18f | 660 | status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); |
1d14ffa9 | 661 | if (status != kAudioHardwareNoError) { |
5e941d4b | 662 | coreaudio_logerr (status, "Could not resume playback\n"); |
1d14ffa9 | 663 | } |
1d14ffa9 FB |
664 | } |
665 | break; | |
666 | ||
667 | case VOICE_DISABLE: | |
668 | /* stop playback */ | |
a384c205 | 669 | if (!audio_is_cleaning_up()) { |
5e941d4b | 670 | if (isPlaying(core->outputDeviceID)) { |
2f79a18f PM |
671 | status = AudioDeviceStop(core->outputDeviceID, |
672 | core->ioprocid); | |
5e941d4b FB |
673 | if (status != kAudioHardwareNoError) { |
674 | coreaudio_logerr (status, "Could not pause playback\n"); | |
675 | } | |
1d14ffa9 | 676 | } |
1d14ffa9 FB |
677 | } |
678 | break; | |
679 | } | |
680 | return 0; | |
681 | } | |
682 | ||
d1f52a1d KZ |
683 | static CoreaudioConf glob_conf = { |
684 | .buffer_frames = 512, | |
685 | .nbuffers = 4, | |
686 | }; | |
687 | ||
1d14ffa9 FB |
688 | static void *coreaudio_audio_init (void) |
689 | { | |
d1f52a1d KZ |
690 | CoreaudioConf *conf = g_malloc(sizeof(CoreaudioConf)); |
691 | *conf = glob_conf; | |
692 | ||
d1f52a1d | 693 | return conf; |
1d14ffa9 FB |
694 | } |
695 | ||
696 | static void coreaudio_audio_fini (void *opaque) | |
697 | { | |
d1f52a1d | 698 | g_free(opaque); |
1d14ffa9 FB |
699 | } |
700 | ||
701 | static struct audio_option coreaudio_options[] = { | |
98f9f48c | 702 | { |
703 | .name = "BUFFER_SIZE", | |
704 | .tag = AUD_OPT_INT, | |
d1f52a1d | 705 | .valp = &glob_conf.buffer_frames, |
98f9f48c | 706 | .descr = "Size of the buffer in frames" |
707 | }, | |
708 | { | |
709 | .name = "BUFFER_COUNT", | |
710 | .tag = AUD_OPT_INT, | |
d1f52a1d | 711 | .valp = &glob_conf.nbuffers, |
98f9f48c | 712 | .descr = "Number of buffers" |
713 | }, | |
2700efa3 | 714 | { /* End of list */ } |
1d14ffa9 FB |
715 | }; |
716 | ||
35f4b58c | 717 | static struct audio_pcm_ops coreaudio_pcm_ops = { |
1dd3e4d1 JQ |
718 | .init_out = coreaudio_init_out, |
719 | .fini_out = coreaudio_fini_out, | |
720 | .run_out = coreaudio_run_out, | |
721 | .write = coreaudio_write, | |
722 | .ctl_out = coreaudio_ctl_out | |
1d14ffa9 FB |
723 | }; |
724 | ||
725 | struct audio_driver coreaudio_audio_driver = { | |
bee37f32 JQ |
726 | .name = "coreaudio", |
727 | .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html", | |
728 | .options = coreaudio_options, | |
729 | .init = coreaudio_audio_init, | |
730 | .fini = coreaudio_audio_fini, | |
731 | .pcm_ops = &coreaudio_pcm_ops, | |
732 | .can_be_default = 1, | |
733 | .max_voices_out = 1, | |
734 | .max_voices_in = 0, | |
735 | .voice_size_out = sizeof (coreaudioVoiceOut), | |
736 | .voice_size_in = 0 | |
1d14ffa9 | 737 | }; |