]>
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)) | |
414 | COREAUDIO_WRAPPER_FUNC(put_buffer_out_nowrite, size_t, | |
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; |
2ceb8240 | 443 | pending_frames = hw->pending_emul >> hw->info.shift; |
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 | ||
2ceb8240 KZ |
452 | len = frameCount << hw->info.shift; |
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 | ||
2ceb8240 KZ |
474 | static UInt32 coreaudio_get_flags(struct audio_pcm_info *info, |
475 | struct audsettings *as) | |
476 | { | |
477 | UInt32 flags = info->sign ? kAudioFormatFlagIsSignedInteger : 0; | |
478 | if (as->endianness) { /* 0 = little, 1 = big */ | |
479 | flags |= kAudioFormatFlagIsBigEndian; | |
480 | } | |
481 | ||
482 | if (flags == 0) { /* must not be 0 */ | |
483 | flags = kAudioFormatFlagsAreAllClear; | |
484 | } | |
485 | return flags; | |
486 | } | |
487 | ||
5706db1d KZ |
488 | static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as, |
489 | void *drv_opaque) | |
1d14ffa9 FB |
490 | { |
491 | OSStatus status; | |
492 | coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | |
1d14ffa9 | 493 | int err; |
5e941d4b FB |
494 | const char *typ = "playback"; |
495 | AudioValueRange frameRange; | |
17c56dc1 KZ |
496 | Audiodev *dev = drv_opaque; |
497 | AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out; | |
498 | int frames; | |
1d14ffa9 FB |
499 | |
500 | /* create mutex */ | |
501 | err = pthread_mutex_init(&core->mutex, NULL); | |
502 | if (err) { | |
c0fe3827 | 503 | dolog("Could not create mutex\nReason: %s\n", strerror (err)); |
1d14ffa9 FB |
504 | return -1; |
505 | } | |
506 | ||
d929eba5 | 507 | audio_pcm_init_info (&hw->info, as); |
1d14ffa9 | 508 | |
88a0f830 | 509 | status = coreaudio_get_voice(&core->outputDeviceID); |
1d14ffa9 FB |
510 | if (status != kAudioHardwareNoError) { |
511 | coreaudio_logerr2 (status, typ, | |
c0fe3827 | 512 | "Could not get default output Device\n"); |
1d14ffa9 FB |
513 | return -1; |
514 | } | |
515 | if (core->outputDeviceID == kAudioDeviceUnknown) { | |
c0fe3827 | 516 | dolog ("Could not initialize %s - Unknown Audiodevice\n", typ); |
1d14ffa9 FB |
517 | return -1; |
518 | } | |
519 | ||
5e941d4b | 520 | /* get minimum and maximum buffer frame sizes */ |
95a860f6 PM |
521 | status = coreaudio_get_framesizerange(core->outputDeviceID, |
522 | &frameRange); | |
5e941d4b FB |
523 | if (status != kAudioHardwareNoError) { |
524 | coreaudio_logerr2 (status, typ, | |
525 | "Could not get device buffer frame range\n"); | |
526 | return -1; | |
527 | } | |
528 | ||
17c56dc1 KZ |
529 | frames = audio_buffer_frames( |
530 | qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610); | |
531 | if (frameRange.mMinimum > frames) { | |
5e941d4b FB |
532 | core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum; |
533 | dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum); | |
17c56dc1 | 534 | } else if (frameRange.mMaximum < frames) { |
5e941d4b FB |
535 | core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum; |
536 | dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum); | |
537 | } | |
538 | else { | |
17c56dc1 | 539 | core->audioDevicePropertyBufferFrameSize = frames; |
5e941d4b FB |
540 | } |
541 | ||
542 | /* set Buffer Frame Size */ | |
95a860f6 PM |
543 | status = coreaudio_set_framesize(core->outputDeviceID, |
544 | &core->audioDevicePropertyBufferFrameSize); | |
1d14ffa9 FB |
545 | if (status != kAudioHardwareNoError) { |
546 | coreaudio_logerr2 (status, typ, | |
cbc36cb0 AF |
547 | "Could not set device buffer frame size %" PRIu32 "\n", |
548 | (uint32_t)core->audioDevicePropertyBufferFrameSize); | |
1d14ffa9 FB |
549 | return -1; |
550 | } | |
551 | ||
5e941d4b | 552 | /* get Buffer Frame Size */ |
95a860f6 PM |
553 | status = coreaudio_get_framesize(core->outputDeviceID, |
554 | &core->audioDevicePropertyBufferFrameSize); | |
1d14ffa9 | 555 | if (status != kAudioHardwareNoError) { |
5e941d4b FB |
556 | coreaudio_logerr2 (status, typ, |
557 | "Could not get device buffer frame size\n"); | |
1d14ffa9 FB |
558 | return -1; |
559 | } | |
17c56dc1 KZ |
560 | hw->samples = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) * |
561 | core->audioDevicePropertyBufferFrameSize; | |
1d14ffa9 FB |
562 | |
563 | /* get StreamFormat */ | |
95a860f6 PM |
564 | status = coreaudio_get_streamformat(core->outputDeviceID, |
565 | &core->outputStreamBasicDescription); | |
1d14ffa9 FB |
566 | if (status != kAudioHardwareNoError) { |
567 | coreaudio_logerr2 (status, typ, | |
c0fe3827 | 568 | "Could not get Device Stream properties\n"); |
1d14ffa9 FB |
569 | core->outputDeviceID = kAudioDeviceUnknown; |
570 | return -1; | |
571 | } | |
572 | ||
573 | /* set Samplerate */ | |
5e941d4b | 574 | core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq; |
2ceb8240 KZ |
575 | core->outputStreamBasicDescription.mFormatID = kAudioFormatLinearPCM; |
576 | core->outputStreamBasicDescription.mFormatFlags = | |
577 | coreaudio_get_flags(&hw->info, as); | |
578 | core->outputStreamBasicDescription.mBytesPerPacket = | |
579 | core->outputStreamBasicDescription.mBytesPerFrame = | |
580 | hw->info.nchannels * hw->info.bits / 8; | |
581 | core->outputStreamBasicDescription.mFramesPerPacket = 1; | |
582 | core->outputStreamBasicDescription.mChannelsPerFrame = hw->info.nchannels; | |
583 | core->outputStreamBasicDescription.mBitsPerChannel = hw->info.bits; | |
584 | ||
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 | ||
571a8c52 | 651 | static void coreaudio_enable_out(HWVoiceOut *hw, bool enable) |
1d14ffa9 FB |
652 | { |
653 | OSStatus status; | |
654 | coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw; | |
655 | ||
571a8c52 | 656 | if (enable) { |
1d14ffa9 | 657 | /* start playback */ |
5e941d4b | 658 | if (!isPlaying(core->outputDeviceID)) { |
2f79a18f | 659 | status = AudioDeviceStart(core->outputDeviceID, core->ioprocid); |
1d14ffa9 | 660 | if (status != kAudioHardwareNoError) { |
5e941d4b | 661 | coreaudio_logerr (status, "Could not resume playback\n"); |
1d14ffa9 | 662 | } |
1d14ffa9 | 663 | } |
571a8c52 | 664 | } else { |
1d14ffa9 | 665 | /* stop playback */ |
a384c205 | 666 | if (!audio_is_cleaning_up()) { |
5e941d4b | 667 | if (isPlaying(core->outputDeviceID)) { |
2f79a18f PM |
668 | status = AudioDeviceStop(core->outputDeviceID, |
669 | core->ioprocid); | |
5e941d4b FB |
670 | if (status != kAudioHardwareNoError) { |
671 | coreaudio_logerr (status, "Could not pause playback\n"); | |
672 | } | |
1d14ffa9 | 673 | } |
1d14ffa9 | 674 | } |
1d14ffa9 | 675 | } |
1d14ffa9 FB |
676 | } |
677 | ||
71830221 | 678 | static void *coreaudio_audio_init(Audiodev *dev) |
1d14ffa9 | 679 | { |
17c56dc1 | 680 | return dev; |
1d14ffa9 FB |
681 | } |
682 | ||
683 | static void coreaudio_audio_fini (void *opaque) | |
684 | { | |
1d14ffa9 FB |
685 | } |
686 | ||
35f4b58c | 687 | static struct audio_pcm_ops coreaudio_pcm_ops = { |
1dd3e4d1 JQ |
688 | .init_out = coreaudio_init_out, |
689 | .fini_out = coreaudio_fini_out, | |
2ceb8240 KZ |
690 | .write = coreaudio_write, |
691 | .get_buffer_out = coreaudio_get_buffer_out, | |
692 | .put_buffer_out = coreaudio_put_buffer_out_nowrite, | |
571a8c52 | 693 | .enable_out = coreaudio_enable_out |
1d14ffa9 FB |
694 | }; |
695 | ||
d3893a39 | 696 | static struct audio_driver coreaudio_audio_driver = { |
bee37f32 JQ |
697 | .name = "coreaudio", |
698 | .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html", | |
bee37f32 JQ |
699 | .init = coreaudio_audio_init, |
700 | .fini = coreaudio_audio_fini, | |
701 | .pcm_ops = &coreaudio_pcm_ops, | |
702 | .can_be_default = 1, | |
703 | .max_voices_out = 1, | |
704 | .max_voices_in = 0, | |
705 | .voice_size_out = sizeof (coreaudioVoiceOut), | |
706 | .voice_size_in = 0 | |
1d14ffa9 | 707 | }; |
d3893a39 GH |
708 | |
709 | static void register_audio_coreaudio(void) | |
710 | { | |
711 | audio_driver_register(&coreaudio_audio_driver); | |
712 | } | |
713 | type_init(register_audio_coreaudio); |