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