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