blob: 6ca0d79c1f5e5916b5e3767760a56512a69351d9 [file] [log] [blame]
bellard1d14ffa2005-10-30 18:58:22 +00001/*
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
Peter Maydell6086a562016-01-18 17:33:52 +000025#include "qemu/osdep.h"
bellard1d14ffa2005-10-30 18:58:22 +000026#include <CoreAudio/CoreAudio.h>
bellard1d14ffa2005-10-30 18:58:22 +000027#include <pthread.h> /* pthread_X */
28
Markus Armbruster0b8fa322019-05-23 16:35:07 +020029#include "qemu/module.h"
pbrook749bc4b2007-11-17 17:35:54 +000030#include "audio.h"
bellard1d14ffa2005-10-30 18:58:22 +000031
32#define AUDIO_CAP "coreaudio"
33#include "audio_int.h"
34
Peter Maydell624d1fc2015-11-28 21:55:21 +000035#ifndef MAC_OS_X_VERSION_10_6
36#define MAC_OS_X_VERSION_10_6 1060
37#endif
38
bellard1d14ffa2005-10-30 18:58:22 +000039typedef struct coreaudioVoiceOut {
40 HWVoiceOut hw;
41 pthread_mutex_t mutex;
42 AudioDeviceID outputDeviceID;
bellard5e941d42005-11-20 16:24:09 +000043 UInt32 audioDevicePropertyBufferFrameSize;
bellard1d14ffa2005-10-30 18:58:22 +000044 AudioStreamBasicDescription outputStreamBasicDescription;
Peter Maydell2f79a182015-11-28 21:55:24 +000045 AudioDeviceIOProcID ioprocid;
bellard1d14ffa2005-10-30 18:58:22 +000046} coreaudioVoiceOut;
47
Peter Maydell624d1fc2015-11-28 21:55:21 +000048#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
51static 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}
Peter Maydell2d99f622015-11-28 21:55:23 +000067
68static 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
86static 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
103static 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
120static 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
138static 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
156static 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}
Peter Maydell624d1fc2015-11-28 21:55:21 +0000172#else
173/* Legacy versions of functions using deprecated APIs */
174
Peter Maydell88a0f832015-11-28 21:55:20 +0000175static OSStatus coreaudio_get_voice(AudioDeviceID *id)
176{
177 UInt32 size = sizeof(*id);
178
179 return AudioHardwareGetProperty(
180 kAudioHardwarePropertyDefaultOutputDevice,
181 &size,
182 id);
183}
184
Peter Maydell95a860f2015-11-28 21:55:22 +0000185static 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
199static 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
212static 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
226static 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
240static 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
255static 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}
Peter Maydell2d99f622015-11-28 21:55:23 +0000267#endif
Peter Maydell95a860f2015-11-28 21:55:22 +0000268
bellard1d14ffa2005-10-30 18:58:22 +0000269static void coreaudio_logstatus (OSStatus status)
270{
Alexandre Raymondd9cbb0f2011-05-27 13:22:28 -0400271 const char *str = "BUG";
bellard1d14ffa2005-10-30 18:58:22 +0000272
Zhang Han3c8de962021-01-15 09:24:26 +0800273 switch (status) {
bellard1d14ffa2005-10-30 18:58:22 +0000274 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:
Andreas Färber744d3642011-06-23 16:24:45 +0200319 AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
bellard1d14ffa2005-10-30 18:58:22 +0000320 return;
321 }
322
323 AUD_log (AUDIO_CAP, "Reason: %s\n", str);
324}
325
326static 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
341static 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
bellardc0fe3822005-11-05 18:55:28 +0000350 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
bellard1d14ffa2005-10-30 18:58:22 +0000351
352 va_start (ap, fmt);
353 AUD_vlog (AUDIO_CAP, fmt, ap);
354 va_end (ap);
355
356 coreaudio_logstatus (status);
357}
358
bellard5e941d42005-11-20 16:24:09 +0000359static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
360{
361 OSStatus status;
362 UInt32 result = 0;
Peter Maydell95a860f2015-11-28 21:55:22 +0000363 status = coreaudio_get_isrunning(outputDeviceID, &result);
bellard5e941d42005-11-20 16:24:09 +0000364 if (status != kAudioHardwareNoError) {
365 coreaudio_logerr(status,
366 "Could not determine whether Device is playing\n");
367 }
368 return result;
369}
370
bellard1d14ffa2005-10-30 18:58:22 +0000371static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
372{
373 int err;
374
375 err = pthread_mutex_lock (&core->mutex);
376 if (err) {
bellardc0fe3822005-11-05 18:55:28 +0000377 dolog ("Could not lock voice for %s\nReason: %s\n",
bellard1d14ffa2005-10-30 18:58:22 +0000378 fn_name, strerror (err));
379 return -1;
380 }
381 return 0;
382}
383
384static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
385{
386 int err;
387
388 err = pthread_mutex_unlock (&core->mutex);
389 if (err) {
bellardc0fe3822005-11-05 18:55:28 +0000390 dolog ("Could not unlock voice for %s\nReason: %s\n",
bellard1d14ffa2005-10-30 18:58:22 +0000391 fn_name, strerror (err));
392 return -1;
393 }
394 return 0;
395}
396
Kővágó, Zoltán2ceb8242019-09-19 23:24:11 +0200397#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; \
bellard1d14ffa2005-10-30 18:58:22 +0000411 }
Kővágó, Zoltán2ceb8242019-09-19 23:24:11 +0200412COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
413 (hw, size))
Volker Rümelinfdc8c5f2020-01-23 08:49:39 +0100414COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
Kővágó, Zoltán2ceb8242019-09-19 23:24:11 +0200415 (HWVoiceOut *hw, void *buf, size_t size),
416 (hw, buf, size))
417COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
418 (hw, buf, size))
419#undef COREAUDIO_WRAPPER_FUNC
bellard1d14ffa2005-10-30 18:58:22 +0000420
421/* callback to feed audiooutput buffer */
422static 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{
Kővágó, Zoltán2ceb8242019-09-19 23:24:11 +0200431 UInt32 frameCount, pending_frames;
432 void *out = outOutputData->mBuffers[0].mData;
bellard1d14ffa2005-10-30 18:58:22 +0000433 HWVoiceOut *hw = hwptr;
434 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
Kővágó, Zoltán2ceb8242019-09-19 23:24:11 +0200435 size_t len;
bellard1d14ffa2005-10-30 18:58:22 +0000436
437 if (coreaudio_lock (core, "audioDeviceIOProc")) {
438 inInputTime = 0;
439 return 0;
440 }
441
bellard5e941d42005-11-20 16:24:09 +0000442 frameCount = core->audioDevicePropertyBufferFrameSize;
Kővágó, Zoltán2b9cce82019-10-13 21:58:02 +0200443 pending_frames = hw->pending_emul / hw->info.bytes_per_frame;
bellard1d14ffa2005-10-30 18:58:22 +0000444
445 /* if there are not enough samples, set signal and return */
Kővágó, Zoltán2ceb8242019-09-19 23:24:11 +0200446 if (pending_frames < frameCount) {
bellard1d14ffa2005-10-30 18:58:22 +0000447 inInputTime = 0;
448 coreaudio_unlock (core, "audioDeviceIOProc(empty)");
449 return 0;
450 }
451
Kővágó, Zoltán2b9cce82019-10-13 21:58:02 +0200452 len = frameCount * hw->info.bytes_per_frame;
Kővágó, Zoltán2ceb8242019-09-19 23:24:11 +0200453 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);
bellard1d14ffa2005-10-30 18:58:22 +0000460
Kővágó, Zoltán2ceb8242019-09-19 23:24:11 +0200461 write_len = MIN(MIN(hw->pending_emul, len),
462 hw->size_emul - start);
463
464 memcpy(out, hw->buf_emul + start, write_len);
465 hw->pending_emul -= write_len;
466 len -= write_len;
467 out += write_len;
bellard1d14ffa2005-10-30 18:58:22 +0000468 }
469
bellard1d14ffa2005-10-30 18:58:22 +0000470 coreaudio_unlock (core, "audioDeviceIOProc");
471 return 0;
472}
473
Kővágó, Zoltán5706db12015-06-03 23:03:47 +0200474static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
475 void *drv_opaque)
bellard1d14ffa2005-10-30 18:58:22 +0000476{
477 OSStatus status;
478 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
bellard1d14ffa2005-10-30 18:58:22 +0000479 int err;
bellard5e941d42005-11-20 16:24:09 +0000480 const char *typ = "playback";
481 AudioValueRange frameRange;
Kővágó, Zoltán17c56dc2019-03-08 23:34:17 +0100482 Audiodev *dev = drv_opaque;
483 AudiodevCoreaudioPerDirectionOptions *cpdo = dev->u.coreaudio.out;
484 int frames;
Volker Rümelin1d470672020-12-13 14:05:25 +0100485 struct audsettings obt_as;
bellard1d14ffa2005-10-30 18:58:22 +0000486
487 /* create mutex */
488 err = pthread_mutex_init(&core->mutex, NULL);
489 if (err) {
bellardc0fe3822005-11-05 18:55:28 +0000490 dolog("Could not create mutex\nReason: %s\n", strerror (err));
bellard1d14ffa2005-10-30 18:58:22 +0000491 return -1;
492 }
493
Volker Rümelin1d470672020-12-13 14:05:25 +0100494 obt_as = *as;
495 as = &obt_as;
Kővágó, Zoltáned2a4a72020-02-02 20:38:07 +0100496 as->fmt = AUDIO_FORMAT_F32;
bellardd929eba2006-07-04 21:47:22 +0000497 audio_pcm_init_info (&hw->info, as);
bellard1d14ffa2005-10-30 18:58:22 +0000498
Peter Maydell88a0f832015-11-28 21:55:20 +0000499 status = coreaudio_get_voice(&core->outputDeviceID);
bellard1d14ffa2005-10-30 18:58:22 +0000500 if (status != kAudioHardwareNoError) {
501 coreaudio_logerr2 (status, typ,
bellardc0fe3822005-11-05 18:55:28 +0000502 "Could not get default output Device\n");
bellard1d14ffa2005-10-30 18:58:22 +0000503 return -1;
504 }
505 if (core->outputDeviceID == kAudioDeviceUnknown) {
bellardc0fe3822005-11-05 18:55:28 +0000506 dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
bellard1d14ffa2005-10-30 18:58:22 +0000507 return -1;
508 }
509
bellard5e941d42005-11-20 16:24:09 +0000510 /* get minimum and maximum buffer frame sizes */
Peter Maydell95a860f2015-11-28 21:55:22 +0000511 status = coreaudio_get_framesizerange(core->outputDeviceID,
512 &frameRange);
bellard5e941d42005-11-20 16:24:09 +0000513 if (status != kAudioHardwareNoError) {
514 coreaudio_logerr2 (status, typ,
515 "Could not get device buffer frame range\n");
516 return -1;
517 }
518
Kővágó, Zoltán17c56dc2019-03-08 23:34:17 +0100519 frames = audio_buffer_frames(
520 qapi_AudiodevCoreaudioPerDirectionOptions_base(cpdo), as, 11610);
521 if (frameRange.mMinimum > frames) {
bellard5e941d42005-11-20 16:24:09 +0000522 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
523 dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
Kővágó, Zoltán17c56dc2019-03-08 23:34:17 +0100524 } else if (frameRange.mMaximum < frames) {
bellard5e941d42005-11-20 16:24:09 +0000525 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
526 dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
Zhang Han6c6886b2021-01-15 09:24:25 +0800527 } else {
Kővágó, Zoltán17c56dc2019-03-08 23:34:17 +0100528 core->audioDevicePropertyBufferFrameSize = frames;
bellard5e941d42005-11-20 16:24:09 +0000529 }
530
531 /* set Buffer Frame Size */
Peter Maydell95a860f2015-11-28 21:55:22 +0000532 status = coreaudio_set_framesize(core->outputDeviceID,
533 &core->audioDevicePropertyBufferFrameSize);
bellard1d14ffa2005-10-30 18:58:22 +0000534 if (status != kAudioHardwareNoError) {
535 coreaudio_logerr2 (status, typ,
Andreas Färbercbc36cb2011-06-23 16:24:46 +0200536 "Could not set device buffer frame size %" PRIu32 "\n",
537 (uint32_t)core->audioDevicePropertyBufferFrameSize);
bellard1d14ffa2005-10-30 18:58:22 +0000538 return -1;
539 }
540
bellard5e941d42005-11-20 16:24:09 +0000541 /* get Buffer Frame Size */
Peter Maydell95a860f2015-11-28 21:55:22 +0000542 status = coreaudio_get_framesize(core->outputDeviceID,
543 &core->audioDevicePropertyBufferFrameSize);
bellard1d14ffa2005-10-30 18:58:22 +0000544 if (status != kAudioHardwareNoError) {
bellard5e941d42005-11-20 16:24:09 +0000545 coreaudio_logerr2 (status, typ,
546 "Could not get device buffer frame size\n");
bellard1d14ffa2005-10-30 18:58:22 +0000547 return -1;
548 }
Kővágó, Zoltán17c56dc2019-03-08 23:34:17 +0100549 hw->samples = (cpdo->has_buffer_count ? cpdo->buffer_count : 4) *
550 core->audioDevicePropertyBufferFrameSize;
bellard1d14ffa2005-10-30 18:58:22 +0000551
552 /* get StreamFormat */
Peter Maydell95a860f2015-11-28 21:55:22 +0000553 status = coreaudio_get_streamformat(core->outputDeviceID,
554 &core->outputStreamBasicDescription);
bellard1d14ffa2005-10-30 18:58:22 +0000555 if (status != kAudioHardwareNoError) {
556 coreaudio_logerr2 (status, typ,
bellardc0fe3822005-11-05 18:55:28 +0000557 "Could not get Device Stream properties\n");
bellard1d14ffa2005-10-30 18:58:22 +0000558 core->outputDeviceID = kAudioDeviceUnknown;
559 return -1;
560 }
561
562 /* set Samplerate */
bellard5e941d42005-11-20 16:24:09 +0000563 core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
Kővágó, Zoltán2ceb8242019-09-19 23:24:11 +0200564
Peter Maydell95a860f2015-11-28 21:55:22 +0000565 status = coreaudio_set_streamformat(core->outputDeviceID,
566 &core->outputStreamBasicDescription);
bellard1d14ffa2005-10-30 18:58:22 +0000567 if (status != kAudioHardwareNoError) {
bellard575b5dc2005-11-11 00:03:20 +0000568 coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
569 as->freq);
bellard1d14ffa2005-10-30 18:58:22 +0000570 core->outputDeviceID = kAudioDeviceUnknown;
571 return -1;
572 }
573
574 /* set Callback */
Peter Maydell2f79a182015-11-28 21:55:24 +0000575 core->ioprocid = NULL;
576 status = AudioDeviceCreateIOProcID(core->outputDeviceID,
577 audioDeviceIOProc,
578 hw,
579 &core->ioprocid);
580 if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
bellardc0fe3822005-11-05 18:55:28 +0000581 coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
bellard1d14ffa2005-10-30 18:58:22 +0000582 core->outputDeviceID = kAudioDeviceUnknown;
583 return -1;
584 }
585
bellard1d14ffa2005-10-30 18:58:22 +0000586 return 0;
587}
588
589static void coreaudio_fini_out (HWVoiceOut *hw)
590{
591 OSStatus status;
592 int err;
593 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
594
Volker Rümelinceb11652020-12-13 14:05:27 +0100595 /* stop playback */
596 if (isPlaying(core->outputDeviceID)) {
597 status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
bellard5e941d42005-11-20 16:24:09 +0000598 if (status != kAudioHardwareNoError) {
Volker Rümelinceb11652020-12-13 14:05:27 +0100599 coreaudio_logerr(status, "Could not stop playback\n");
bellard5e941d42005-11-20 16:24:09 +0000600 }
bellard1d14ffa2005-10-30 18:58:22 +0000601 }
Volker Rümelinceb11652020-12-13 14:05:27 +0100602
603 /* remove callback */
604 status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
605 core->ioprocid);
606 if (status != kAudioHardwareNoError) {
607 coreaudio_logerr(status, "Could not remove IOProc\n");
608 }
bellard1d14ffa2005-10-30 18:58:22 +0000609 core->outputDeviceID = kAudioDeviceUnknown;
610
611 /* destroy mutex */
612 err = pthread_mutex_destroy(&core->mutex);
613 if (err) {
bellardc0fe3822005-11-05 18:55:28 +0000614 dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
bellard1d14ffa2005-10-30 18:58:22 +0000615 }
616}
617
Kővágó, Zoltán571a8c52019-09-19 23:24:22 +0200618static void coreaudio_enable_out(HWVoiceOut *hw, bool enable)
bellard1d14ffa2005-10-30 18:58:22 +0000619{
620 OSStatus status;
621 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
622
Kővágó, Zoltán571a8c52019-09-19 23:24:22 +0200623 if (enable) {
bellard1d14ffa2005-10-30 18:58:22 +0000624 /* start playback */
bellard5e941d42005-11-20 16:24:09 +0000625 if (!isPlaying(core->outputDeviceID)) {
Peter Maydell2f79a182015-11-28 21:55:24 +0000626 status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
bellard1d14ffa2005-10-30 18:58:22 +0000627 if (status != kAudioHardwareNoError) {
bellard5e941d42005-11-20 16:24:09 +0000628 coreaudio_logerr (status, "Could not resume playback\n");
bellard1d14ffa2005-10-30 18:58:22 +0000629 }
bellard1d14ffa2005-10-30 18:58:22 +0000630 }
Kővágó, Zoltán571a8c52019-09-19 23:24:22 +0200631 } else {
bellard1d14ffa2005-10-30 18:58:22 +0000632 /* stop playback */
Volker Rümelinceb11652020-12-13 14:05:27 +0100633 if (isPlaying(core->outputDeviceID)) {
634 status = AudioDeviceStop(core->outputDeviceID,
635 core->ioprocid);
636 if (status != kAudioHardwareNoError) {
637 coreaudio_logerr(status, "Could not pause playback\n");
bellard1d14ffa2005-10-30 18:58:22 +0000638 }
bellard1d14ffa2005-10-30 18:58:22 +0000639 }
bellard1d14ffa2005-10-30 18:58:22 +0000640 }
bellard1d14ffa2005-10-30 18:58:22 +0000641}
642
Kővágó, Zoltán71830222019-03-08 23:34:15 +0100643static void *coreaudio_audio_init(Audiodev *dev)
bellard1d14ffa2005-10-30 18:58:22 +0000644{
Kővágó, Zoltán17c56dc2019-03-08 23:34:17 +0100645 return dev;
bellard1d14ffa2005-10-30 18:58:22 +0000646}
647
648static void coreaudio_audio_fini (void *opaque)
649{
bellard1d14ffa2005-10-30 18:58:22 +0000650}
651
blueswir135f4b582008-10-06 18:08:30 +0000652static struct audio_pcm_ops coreaudio_pcm_ops = {
Juan Quintela1dd3e4d2009-08-11 02:31:15 +0200653 .init_out = coreaudio_init_out,
654 .fini_out = coreaudio_fini_out,
Volker Rümelinfdc8c5f2020-01-23 08:49:39 +0100655 /* wrapper for audio_generic_write */
Kővágó, Zoltán2ceb8242019-09-19 23:24:11 +0200656 .write = coreaudio_write,
Volker Rümelinfdc8c5f2020-01-23 08:49:39 +0100657 /* wrapper for audio_generic_get_buffer_out */
Kővágó, Zoltán2ceb8242019-09-19 23:24:11 +0200658 .get_buffer_out = coreaudio_get_buffer_out,
Volker Rümelinfdc8c5f2020-01-23 08:49:39 +0100659 /* wrapper for audio_generic_put_buffer_out */
660 .put_buffer_out = coreaudio_put_buffer_out,
Kővágó, Zoltán571a8c52019-09-19 23:24:22 +0200661 .enable_out = coreaudio_enable_out
bellard1d14ffa2005-10-30 18:58:22 +0000662};
663
Gerd Hoffmannd3893a32018-03-06 08:40:47 +0100664static struct audio_driver coreaudio_audio_driver = {
Juan Quintelabee37f32009-08-11 02:31:14 +0200665 .name = "coreaudio",
666 .descr = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
Juan Quintelabee37f32009-08-11 02:31:14 +0200667 .init = coreaudio_audio_init,
668 .fini = coreaudio_audio_fini,
669 .pcm_ops = &coreaudio_pcm_ops,
670 .can_be_default = 1,
671 .max_voices_out = 1,
672 .max_voices_in = 0,
673 .voice_size_out = sizeof (coreaudioVoiceOut),
674 .voice_size_in = 0
bellard1d14ffa2005-10-30 18:58:22 +0000675};
Gerd Hoffmannd3893a32018-03-06 08:40:47 +0100676
677static void register_audio_coreaudio(void)
678{
679 audio_driver_register(&coreaudio_audio_driver);
680}
681type_init(register_audio_coreaudio);