Abort on attempts to allocate zero bytes
[qemu/mini2440.git] / audio / coreaudio.c
blob96714297e51e7f72add37fdc87c31667883bd622
1 /*
2 * QEMU OS X CoreAudio audio driver
4 * Copyright (c) 2005 Mike Kronenberg
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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.
25 #include <CoreAudio/CoreAudio.h>
26 #include <string.h> /* strerror */
27 #include <pthread.h> /* pthread_X */
29 #include "qemu-common.h"
30 #include "audio.h"
32 #define AUDIO_CAP "coreaudio"
33 #include "audio_int.h"
35 struct {
36 int buffer_frames;
37 int nbuffers;
38 int isAtexit;
39 } conf = {
40 .buffer_frames = 512,
41 .nbuffers = 4,
42 .isAtexit = 0
45 typedef struct coreaudioVoiceOut {
46 HWVoiceOut hw;
47 pthread_mutex_t mutex;
48 int isAtexit;
49 AudioDeviceID outputDeviceID;
50 UInt32 audioDevicePropertyBufferFrameSize;
51 AudioStreamBasicDescription outputStreamBasicDescription;
52 int live;
53 int decr;
54 int rpos;
55 } coreaudioVoiceOut;
57 static void coreaudio_logstatus (OSStatus status)
59 char *str = "BUG";
61 switch(status) {
62 case kAudioHardwareNoError:
63 str = "kAudioHardwareNoError";
64 break;
66 case kAudioHardwareNotRunningError:
67 str = "kAudioHardwareNotRunningError";
68 break;
70 case kAudioHardwareUnspecifiedError:
71 str = "kAudioHardwareUnspecifiedError";
72 break;
74 case kAudioHardwareUnknownPropertyError:
75 str = "kAudioHardwareUnknownPropertyError";
76 break;
78 case kAudioHardwareBadPropertySizeError:
79 str = "kAudioHardwareBadPropertySizeError";
80 break;
82 case kAudioHardwareIllegalOperationError:
83 str = "kAudioHardwareIllegalOperationError";
84 break;
86 case kAudioHardwareBadDeviceError:
87 str = "kAudioHardwareBadDeviceError";
88 break;
90 case kAudioHardwareBadStreamError:
91 str = "kAudioHardwareBadStreamError";
92 break;
94 case kAudioHardwareUnsupportedOperationError:
95 str = "kAudioHardwareUnsupportedOperationError";
96 break;
98 case kAudioDeviceUnsupportedFormatError:
99 str = "kAudioDeviceUnsupportedFormatError";
100 break;
102 case kAudioDevicePermissionsError:
103 str = "kAudioDevicePermissionsError";
104 break;
106 default:
107 AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status);
108 return;
111 AUD_log (AUDIO_CAP, "Reason: %s\n", str);
114 static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
115 OSStatus status,
116 const char *fmt,
120 va_list ap;
122 va_start (ap, fmt);
123 AUD_log (AUDIO_CAP, fmt, ap);
124 va_end (ap);
126 coreaudio_logstatus (status);
129 static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
130 OSStatus status,
131 const char *typ,
132 const char *fmt,
136 va_list ap;
138 AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
140 va_start (ap, fmt);
141 AUD_vlog (AUDIO_CAP, fmt, ap);
142 va_end (ap);
144 coreaudio_logstatus (status);
147 static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
149 OSStatus status;
150 UInt32 result = 0;
151 UInt32 propertySize = sizeof(outputDeviceID);
152 status = AudioDeviceGetProperty(
153 outputDeviceID, 0, 0,
154 kAudioDevicePropertyDeviceIsRunning, &propertySize, &result);
155 if (status != kAudioHardwareNoError) {
156 coreaudio_logerr(status,
157 "Could not determine whether Device is playing\n");
159 return result;
162 static void coreaudio_atexit (void)
164 conf.isAtexit = 1;
167 static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
169 int err;
171 err = pthread_mutex_lock (&core->mutex);
172 if (err) {
173 dolog ("Could not lock voice for %s\nReason: %s\n",
174 fn_name, strerror (err));
175 return -1;
177 return 0;
180 static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
182 int err;
184 err = pthread_mutex_unlock (&core->mutex);
185 if (err) {
186 dolog ("Could not unlock voice for %s\nReason: %s\n",
187 fn_name, strerror (err));
188 return -1;
190 return 0;
193 static int coreaudio_run_out (HWVoiceOut *hw)
195 int live, decr;
196 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
198 if (coreaudio_lock (core, "coreaudio_run_out")) {
199 return 0;
202 live = audio_pcm_hw_get_live_out (hw);
204 if (core->decr > live) {
205 ldebug ("core->decr %d live %d core->live %d\n",
206 core->decr,
207 live,
208 core->live);
211 decr = audio_MIN (core->decr, live);
212 core->decr -= decr;
214 core->live = live - decr;
215 hw->rpos = core->rpos;
217 coreaudio_unlock (core, "coreaudio_run_out");
218 return decr;
221 /* callback to feed audiooutput buffer */
222 static OSStatus audioDeviceIOProc(
223 AudioDeviceID inDevice,
224 const AudioTimeStamp* inNow,
225 const AudioBufferList* inInputData,
226 const AudioTimeStamp* inInputTime,
227 AudioBufferList* outOutputData,
228 const AudioTimeStamp* inOutputTime,
229 void* hwptr)
231 UInt32 frame, frameCount;
232 float *out = outOutputData->mBuffers[0].mData;
233 HWVoiceOut *hw = hwptr;
234 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
235 int rpos, live;
236 struct st_sample *src;
237 #ifndef FLOAT_MIXENG
238 #ifdef RECIPROCAL
239 const float scale = 1.f / UINT_MAX;
240 #else
241 const float scale = UINT_MAX;
242 #endif
243 #endif
245 if (coreaudio_lock (core, "audioDeviceIOProc")) {
246 inInputTime = 0;
247 return 0;
250 frameCount = core->audioDevicePropertyBufferFrameSize;
251 live = core->live;
253 /* if there are not enough samples, set signal and return */
254 if (live < frameCount) {
255 inInputTime = 0;
256 coreaudio_unlock (core, "audioDeviceIOProc(empty)");
257 return 0;
260 rpos = core->rpos;
261 src = hw->mix_buf + rpos;
263 /* fill buffer */
264 for (frame = 0; frame < frameCount; frame++) {
265 #ifdef FLOAT_MIXENG
266 *out++ = src[frame].l; /* left channel */
267 *out++ = src[frame].r; /* right channel */
268 #else
269 #ifdef RECIPROCAL
270 *out++ = src[frame].l * scale; /* left channel */
271 *out++ = src[frame].r * scale; /* right channel */
272 #else
273 *out++ = src[frame].l / scale; /* left channel */
274 *out++ = src[frame].r / scale; /* right channel */
275 #endif
276 #endif
279 rpos = (rpos + frameCount) % hw->samples;
280 core->decr += frameCount;
281 core->rpos = rpos;
283 coreaudio_unlock (core, "audioDeviceIOProc");
284 return 0;
287 static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
289 return audio_pcm_sw_write (sw, buf, len);
292 static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
294 OSStatus status;
295 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
296 UInt32 propertySize;
297 int err;
298 const char *typ = "playback";
299 AudioValueRange frameRange;
301 /* create mutex */
302 err = pthread_mutex_init(&core->mutex, NULL);
303 if (err) {
304 dolog("Could not create mutex\nReason: %s\n", strerror (err));
305 return -1;
308 audio_pcm_init_info (&hw->info, as);
310 /* open default output device */
311 propertySize = sizeof(core->outputDeviceID);
312 status = AudioHardwareGetProperty(
313 kAudioHardwarePropertyDefaultOutputDevice,
314 &propertySize,
315 &core->outputDeviceID);
316 if (status != kAudioHardwareNoError) {
317 coreaudio_logerr2 (status, typ,
318 "Could not get default output Device\n");
319 return -1;
321 if (core->outputDeviceID == kAudioDeviceUnknown) {
322 dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
323 return -1;
326 /* get minimum and maximum buffer frame sizes */
327 propertySize = sizeof(frameRange);
328 status = AudioDeviceGetProperty(
329 core->outputDeviceID,
332 kAudioDevicePropertyBufferFrameSizeRange,
333 &propertySize,
334 &frameRange);
335 if (status != kAudioHardwareNoError) {
336 coreaudio_logerr2 (status, typ,
337 "Could not get device buffer frame range\n");
338 return -1;
341 if (frameRange.mMinimum > conf.buffer_frames) {
342 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
343 dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
345 else if (frameRange.mMaximum < conf.buffer_frames) {
346 core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
347 dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
349 else {
350 core->audioDevicePropertyBufferFrameSize = conf.buffer_frames;
353 /* set Buffer Frame Size */
354 propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
355 status = AudioDeviceSetProperty(
356 core->outputDeviceID,
357 NULL,
359 false,
360 kAudioDevicePropertyBufferFrameSize,
361 propertySize,
362 &core->audioDevicePropertyBufferFrameSize);
363 if (status != kAudioHardwareNoError) {
364 coreaudio_logerr2 (status, typ,
365 "Could not set device buffer frame size %ld\n",
366 core->audioDevicePropertyBufferFrameSize);
367 return -1;
370 /* get Buffer Frame Size */
371 propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
372 status = AudioDeviceGetProperty(
373 core->outputDeviceID,
375 false,
376 kAudioDevicePropertyBufferFrameSize,
377 &propertySize,
378 &core->audioDevicePropertyBufferFrameSize);
379 if (status != kAudioHardwareNoError) {
380 coreaudio_logerr2 (status, typ,
381 "Could not get device buffer frame size\n");
382 return -1;
384 hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize;
386 /* get StreamFormat */
387 propertySize = sizeof(core->outputStreamBasicDescription);
388 status = AudioDeviceGetProperty(
389 core->outputDeviceID,
391 false,
392 kAudioDevicePropertyStreamFormat,
393 &propertySize,
394 &core->outputStreamBasicDescription);
395 if (status != kAudioHardwareNoError) {
396 coreaudio_logerr2 (status, typ,
397 "Could not get Device Stream properties\n");
398 core->outputDeviceID = kAudioDeviceUnknown;
399 return -1;
402 /* set Samplerate */
403 core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
404 propertySize = sizeof(core->outputStreamBasicDescription);
405 status = AudioDeviceSetProperty(
406 core->outputDeviceID,
410 kAudioDevicePropertyStreamFormat,
411 propertySize,
412 &core->outputStreamBasicDescription);
413 if (status != kAudioHardwareNoError) {
414 coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
415 as->freq);
416 core->outputDeviceID = kAudioDeviceUnknown;
417 return -1;
420 /* set Callback */
421 status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
422 if (status != kAudioHardwareNoError) {
423 coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
424 core->outputDeviceID = kAudioDeviceUnknown;
425 return -1;
428 /* start Playback */
429 if (!isPlaying(core->outputDeviceID)) {
430 status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
431 if (status != kAudioHardwareNoError) {
432 coreaudio_logerr2 (status, typ, "Could not start playback\n");
433 AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
434 core->outputDeviceID = kAudioDeviceUnknown;
435 return -1;
439 return 0;
442 static void coreaudio_fini_out (HWVoiceOut *hw)
444 OSStatus status;
445 int err;
446 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
448 if (!conf.isAtexit) {
449 /* stop playback */
450 if (isPlaying(core->outputDeviceID)) {
451 status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
452 if (status != kAudioHardwareNoError) {
453 coreaudio_logerr (status, "Could not stop playback\n");
457 /* remove callback */
458 status = AudioDeviceRemoveIOProc(core->outputDeviceID,
459 audioDeviceIOProc);
460 if (status != kAudioHardwareNoError) {
461 coreaudio_logerr (status, "Could not remove IOProc\n");
464 core->outputDeviceID = kAudioDeviceUnknown;
466 /* destroy mutex */
467 err = pthread_mutex_destroy(&core->mutex);
468 if (err) {
469 dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
473 static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
475 OSStatus status;
476 coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
478 switch (cmd) {
479 case VOICE_ENABLE:
480 /* start playback */
481 if (!isPlaying(core->outputDeviceID)) {
482 status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
483 if (status != kAudioHardwareNoError) {
484 coreaudio_logerr (status, "Could not resume playback\n");
487 break;
489 case VOICE_DISABLE:
490 /* stop playback */
491 if (!conf.isAtexit) {
492 if (isPlaying(core->outputDeviceID)) {
493 status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
494 if (status != kAudioHardwareNoError) {
495 coreaudio_logerr (status, "Could not pause playback\n");
499 break;
501 return 0;
504 static void *coreaudio_audio_init (void)
506 atexit(coreaudio_atexit);
507 return &coreaudio_audio_init;
510 static void coreaudio_audio_fini (void *opaque)
512 (void) opaque;
515 static struct audio_option coreaudio_options[] = {
516 {"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_frames,
517 "Size of the buffer in frames", NULL, 0},
518 {"BUFFER_COUNT", AUD_OPT_INT, &conf.nbuffers,
519 "Number of buffers", NULL, 0},
520 {NULL, 0, NULL, NULL, NULL, 0}
523 static struct audio_pcm_ops coreaudio_pcm_ops = {
524 coreaudio_init_out,
525 coreaudio_fini_out,
526 coreaudio_run_out,
527 coreaudio_write,
528 coreaudio_ctl_out,
530 NULL,
531 NULL,
532 NULL,
533 NULL,
534 NULL
537 struct audio_driver coreaudio_audio_driver = {
538 INIT_FIELD (name = ) "coreaudio",
539 INIT_FIELD (descr = )
540 "CoreAudio http://developer.apple.com/audio/coreaudio.html",
541 INIT_FIELD (options = ) coreaudio_options,
542 INIT_FIELD (init = ) coreaudio_audio_init,
543 INIT_FIELD (fini = ) coreaudio_audio_fini,
544 INIT_FIELD (pcm_ops = ) &coreaudio_pcm_ops,
545 INIT_FIELD (can_be_default = ) 1,
546 INIT_FIELD (max_voices_out = ) 1,
547 INIT_FIELD (max_voices_in = ) 0,
548 INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut),
549 INIT_FIELD (voice_size_in = ) 0