]> git.proxmox.com Git - qemu.git/blame - audio/coreaudio.c
wav finalization fix (malc)
[qemu.git] / audio / coreaudio.c
CommitLineData
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
25#include <CoreAudio/CoreAudio.h>
26#include <string.h> /* strerror */
27#include <pthread.h> /* pthread_X */
28
29#include "vl.h"
30
31#define AUDIO_CAP "coreaudio"
32#include "audio_int.h"
33
1d14ffa9
FB
34struct {
35 int buffer_frames;
36} conf = {
37 .buffer_frames = 512
38};
39
40typedef struct coreaudioVoiceOut {
41 HWVoiceOut hw;
42 pthread_mutex_t mutex;
43 AudioDeviceID outputDeviceID;
44 UInt32 audioDevicePropertyBufferSize;
45 AudioStreamBasicDescription outputStreamBasicDescription;
46 int isPlaying;
47 int live;
48 int decr;
49 int rpos;
50} coreaudioVoiceOut;
51
52static void coreaudio_logstatus (OSStatus status)
53{
54 char *str = "BUG";
55
56 switch(status) {
57 case kAudioHardwareNoError:
58 str = "kAudioHardwareNoError";
59 break;
60
61 case kAudioHardwareNotRunningError:
62 str = "kAudioHardwareNotRunningError";
63 break;
64
65 case kAudioHardwareUnspecifiedError:
66 str = "kAudioHardwareUnspecifiedError";
67 break;
68
69 case kAudioHardwareUnknownPropertyError:
70 str = "kAudioHardwareUnknownPropertyError";
71 break;
72
73 case kAudioHardwareBadPropertySizeError:
74 str = "kAudioHardwareBadPropertySizeError";
75 break;
76
77 case kAudioHardwareIllegalOperationError:
78 str = "kAudioHardwareIllegalOperationError";
79 break;
80
81 case kAudioHardwareBadDeviceError:
82 str = "kAudioHardwareBadDeviceError";
83 break;
84
85 case kAudioHardwareBadStreamError:
86 str = "kAudioHardwareBadStreamError";
87 break;
88
89 case kAudioHardwareUnsupportedOperationError:
90 str = "kAudioHardwareUnsupportedOperationError";
91 break;
92
93 case kAudioDeviceUnsupportedFormatError:
94 str = "kAudioDeviceUnsupportedFormatError";
95 break;
96
97 case kAudioDevicePermissionsError:
98 str = "kAudioDevicePermissionsError";
99 break;
100
101 default:
102 AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status);
103 return;
104 }
105
106 AUD_log (AUDIO_CAP, "Reason: %s\n", str);
107}
108
109static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
110 OSStatus status,
111 const char *fmt,
112 ...
113 )
114{
115 va_list ap;
116
117 va_start (ap, fmt);
118 AUD_log (AUDIO_CAP, fmt, ap);
119 va_end (ap);
120
121 coreaudio_logstatus (status);
122}
123
124static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
125 OSStatus status,
126 const char *typ,
127 const char *fmt,
128 ...
129 )
130{
131 va_list ap;
132
c0fe3827 133 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
1d14ffa9
FB
134
135 va_start (ap, fmt);
136 AUD_vlog (AUDIO_CAP, fmt, ap);
137 va_end (ap);
138
139 coreaudio_logstatus (status);
140}
141
142static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
143{
144 int err;
145
146 err = pthread_mutex_lock (&core->mutex);
147 if (err) {
c0fe3827 148 dolog ("Could not lock voice for %s\nReason: %s\n",
1d14ffa9
FB
149 fn_name, strerror (err));
150 return -1;
151 }
152 return 0;
153}
154
155static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
156{
157 int err;
158
159 err = pthread_mutex_unlock (&core->mutex);
160 if (err) {
c0fe3827 161 dolog ("Could not unlock voice for %s\nReason: %s\n",
1d14ffa9
FB
162 fn_name, strerror (err));
163 return -1;
164 }
165 return 0;
166}
167
168static int coreaudio_run_out (HWVoiceOut *hw)
169{
170 int live, decr;
171 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
172
173 if (coreaudio_lock (core, "coreaudio_run_out")) {
174 return 0;
175 }
176
177 live = audio_pcm_hw_get_live_out (hw);
178
179 if (core->decr > live) {
180 ldebug ("core->decr %d live %d core->live %d\n",
181 core->decr,
182 live,
183 core->live);
184 }
185
186 decr = audio_MIN (core->decr, live);
187 core->decr -= decr;
188
189 core->live = live - decr;
190 hw->rpos = core->rpos;
191
192 coreaudio_unlock (core, "coreaudio_run_out");
193 return decr;
194}
195
196/* callback to feed audiooutput buffer */
197static OSStatus audioDeviceIOProc(
198 AudioDeviceID inDevice,
199 const AudioTimeStamp* inNow,
200 const AudioBufferList* inInputData,
201 const AudioTimeStamp* inInputTime,
202 AudioBufferList* outOutputData,
203 const AudioTimeStamp* inOutputTime,
204 void* hwptr)
205{
206 unsigned int frame, frameCount;
207 float *out = outOutputData->mBuffers[0].mData;
208 HWVoiceOut *hw = hwptr;
209 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
210 int rpos, live;
211 st_sample_t *src;
212#ifndef FLOAT_MIXENG
213#ifdef RECIPROCAL
214 const float scale = 1.f / UINT_MAX;
215#else
216 const float scale = UINT_MAX;
217#endif
218#endif
219
220 if (coreaudio_lock (core, "audioDeviceIOProc")) {
221 inInputTime = 0;
222 return 0;
223 }
224
225 frameCount = conf.buffer_frames;
226 live = core->live;
227
228 /* if there are not enough samples, set signal and return */
229 if (live < frameCount) {
230 inInputTime = 0;
231 coreaudio_unlock (core, "audioDeviceIOProc(empty)");
232 return 0;
233 }
234
235 rpos = core->rpos;
236 src = hw->mix_buf + rpos;
237
238 /* fill buffer */
239 for (frame = 0; frame < frameCount; frame++) {
240#ifdef FLOAT_MIXENG
241 *out++ = src[frame].l; /* left channel */
242 *out++ = src[frame].r; /* right channel */
243#else
244#ifdef RECIPROCAL
245 *out++ = src[frame].l * scale; /* left channel */
246 *out++ = src[frame].r * scale; /* right channel */
247#else
248 *out++ = src[frame].l / scale; /* left channel */
249 *out++ = src[frame].r / scale; /* right channel */
250#endif
251#endif
252 }
253
254 /* cleanup */
255 mixeng_clear (src, frameCount);
256 rpos = (rpos + frameCount) % hw->samples;
257 core->decr = frameCount;
258 core->rpos = rpos;
259
260 coreaudio_unlock (core, "audioDeviceIOProc");
261 return 0;
262}
263
264static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
265{
266 return audio_pcm_sw_write (sw, buf, len);
267}
268
c0fe3827 269static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
1d14ffa9
FB
270{
271 OSStatus status;
272 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
273 UInt32 propertySize;
274 int err;
275 int bits = 8;
276 int endianess = 0;
277 const char *typ = "DAC";
278
279 /* create mutex */
280 err = pthread_mutex_init(&core->mutex, NULL);
281 if (err) {
c0fe3827 282 dolog("Could not create mutex\nReason: %s\n", strerror (err));
1d14ffa9
FB
283 return -1;
284 }
285
c0fe3827 286 if (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) {
1d14ffa9
FB
287 bits = 16;
288 endianess = 1;
289 }
290
291 audio_pcm_init_info (
292 &hw->info,
c0fe3827 293 as,
1d14ffa9
FB
294 /* Following is irrelevant actually since we do not use
295 mixengs clipping routines */
296 audio_need_to_swap_endian (endianess)
297 );
1d14ffa9
FB
298
299 /* open default output device */
300 propertySize = sizeof(core->outputDeviceID);
301 status = AudioHardwareGetProperty(
302 kAudioHardwarePropertyDefaultOutputDevice,
303 &propertySize,
304 &core->outputDeviceID);
305 if (status != kAudioHardwareNoError) {
306 coreaudio_logerr2 (status, typ,
c0fe3827 307 "Could not get default output Device\n");
1d14ffa9
FB
308 return -1;
309 }
310 if (core->outputDeviceID == kAudioDeviceUnknown) {
c0fe3827 311 dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
1d14ffa9
FB
312 return -1;
313 }
314
315 /* set Buffersize to conf.buffer_frames frames */
316 propertySize = sizeof(core->audioDevicePropertyBufferSize);
317 core->audioDevicePropertyBufferSize =
c0fe3827 318 conf.buffer_frames * sizeof(float) << (as->nchannels == 2);
1d14ffa9
FB
319 status = AudioDeviceSetProperty(
320 core->outputDeviceID,
321 NULL,
322 0,
323 false,
324 kAudioDevicePropertyBufferSize,
325 propertySize,
326 &core->audioDevicePropertyBufferSize);
327 if (status != kAudioHardwareNoError) {
328 coreaudio_logerr2 (status, typ,
c0fe3827 329 "Could not set device buffer size %d\n",
1d14ffa9
FB
330 kAudioDevicePropertyBufferSize);
331 return -1;
332 }
333
334 /* get Buffersize */
335 propertySize = sizeof(core->audioDevicePropertyBufferSize);
336 status = AudioDeviceGetProperty(
337 core->outputDeviceID,
338 0,
339 false,
340 kAudioDevicePropertyBufferSize,
341 &propertySize,
342 &core->audioDevicePropertyBufferSize);
343 if (status != kAudioHardwareNoError) {
c0fe3827 344 coreaudio_logerr2 (status, typ, "Could not get device buffer size\n");
1d14ffa9
FB
345 return -1;
346 }
c0fe3827
FB
347 hw->samples = (core->audioDevicePropertyBufferSize / sizeof (float))
348 >> (as->nchannels == 2);
1d14ffa9
FB
349
350 /* get StreamFormat */
351 propertySize = sizeof(core->outputStreamBasicDescription);
352 status = AudioDeviceGetProperty(
353 core->outputDeviceID,
354 0,
355 false,
356 kAudioDevicePropertyStreamFormat,
357 &propertySize,
358 &core->outputStreamBasicDescription);
359 if (status != kAudioHardwareNoError) {
360 coreaudio_logerr2 (status, typ,
c0fe3827 361 "Could not get Device Stream properties\n");
1d14ffa9
FB
362 core->outputDeviceID = kAudioDeviceUnknown;
363 return -1;
364 }
365
366 /* set Samplerate */
c0fe3827 367 core->outputStreamBasicDescription.mSampleRate = (Float64)as->freq;
1d14ffa9
FB
368 propertySize = sizeof(core->outputStreamBasicDescription);
369 status = AudioDeviceSetProperty(
370 core->outputDeviceID,
371 0,
372 0,
373 0,
374 kAudioDevicePropertyStreamFormat,
375 propertySize,
376 &core->outputStreamBasicDescription);
377 if (status != kAudioHardwareNoError) {
575b5dc4
FB
378 coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
379 as->freq);
1d14ffa9
FB
380 core->outputDeviceID = kAudioDeviceUnknown;
381 return -1;
382 }
383
384 /* set Callback */
385 status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
386 if (status != kAudioHardwareNoError) {
c0fe3827 387 coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
1d14ffa9
FB
388 core->outputDeviceID = kAudioDeviceUnknown;
389 return -1;
390 }
391
392 /* start Playback */
393 if (!core->isPlaying) {
394 status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
395 if (status != kAudioHardwareNoError) {
c0fe3827 396 coreaudio_logerr2 (status, typ, "Could not start playback\n");
1d14ffa9
FB
397 AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
398 core->outputDeviceID = kAudioDeviceUnknown;
399 return -1;
400 }
401 core->isPlaying = 1;
402 }
403
404 return 0;
405}
406
407static void coreaudio_fini_out (HWVoiceOut *hw)
408{
409 OSStatus status;
410 int err;
411 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
412
413 /* stop playback */
414 if (core->isPlaying) {
415 status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
416 if (status != kAudioHardwareNoError) {
c0fe3827 417 coreaudio_logerr (status, "Could not stop playback\n");
1d14ffa9
FB
418 }
419 core->isPlaying = 0;
420 }
421
422 /* remove callback */
423 status = AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
424 if (status != kAudioHardwareNoError) {
c0fe3827 425 coreaudio_logerr (status, "Could not remove IOProc\n");
1d14ffa9
FB
426 }
427 core->outputDeviceID = kAudioDeviceUnknown;
428
429 /* destroy mutex */
430 err = pthread_mutex_destroy(&core->mutex);
431 if (err) {
c0fe3827 432 dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
1d14ffa9
FB
433 }
434}
435
436static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
437{
438 OSStatus status;
439 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
440
441 switch (cmd) {
442 case VOICE_ENABLE:
443 /* start playback */
444 if (!core->isPlaying) {
445 status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
446 if (status != kAudioHardwareNoError) {
c0fe3827 447 coreaudio_logerr (status, "Could not unpause playback\n");
1d14ffa9
FB
448 }
449 core->isPlaying = 1;
450 }
451 break;
452
453 case VOICE_DISABLE:
454 /* stop playback */
455 if (core->isPlaying) {
456 status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
457 if (status != kAudioHardwareNoError) {
c0fe3827 458 coreaudio_logerr (status, "Could not pause playback\n");
1d14ffa9
FB
459 }
460 core->isPlaying = 0;
461 }
462 break;
463 }
464 return 0;
465}
466
467static void *coreaudio_audio_init (void)
468{
469 return &coreaudio_audio_init;
470}
471
472static void coreaudio_audio_fini (void *opaque)
473{
474 (void) opaque;
475}
476
477static struct audio_option coreaudio_options[] = {
478 {"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_frames,
479 "Size of the buffer in frames", NULL, 0},
480 {NULL, 0, NULL, NULL, NULL, 0}
481};
482
483static struct audio_pcm_ops coreaudio_pcm_ops = {
484 coreaudio_init_out,
485 coreaudio_fini_out,
486 coreaudio_run_out,
487 coreaudio_write,
488 coreaudio_ctl_out,
489
490 NULL,
491 NULL,
492 NULL,
493 NULL,
494 NULL
495};
496
497struct audio_driver coreaudio_audio_driver = {
498 INIT_FIELD (name = ) "coreaudio",
499 INIT_FIELD (descr = )
500 "CoreAudio http://developer.apple.com/audio/coreaudio.html",
501 INIT_FIELD (options = ) coreaudio_options,
502 INIT_FIELD (init = ) coreaudio_audio_init,
503 INIT_FIELD (fini = ) coreaudio_audio_fini,
504 INIT_FIELD (pcm_ops = ) &coreaudio_pcm_ops,
505 INIT_FIELD (can_be_default = ) 1,
506 INIT_FIELD (max_voices_out = ) 1,
507 INIT_FIELD (max_voices_in = ) 0,
508 INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut),
509 INIT_FIELD (voice_size_in = ) 0
510};