1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25: #include <CoreAudio/CoreAudio.h>
26: #include <string.h>
27: #include <pthread.h>
28:
29: #include "qemu-common.h"
30: #include "audio.h"
31:
32: #define AUDIO_CAP "coreaudio"
33: #include "audio_int.h"
34:
35: struct {
36: int buffer_frames;
37: int nbuffers;
38: int isAtexit;
39: } conf = {
40: .buffer_frames = 512,
41: .nbuffers = 4,
42: .isAtexit = 0
43: };
44:
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;
56:
57: static void coreaudio_logstatus (OSStatus status)
58: {
59: char *str = "BUG";
60:
61: switch(status) {
62: case kAudioHardwareNoError:
63: str = "kAudioHardwareNoError";
64: break;
65:
66: case kAudioHardwareNotRunningError:
67: str = "kAudioHardwareNotRunningError";
68: break;
69:
70: case kAudioHardwareUnspecifiedError:
71: str = "kAudioHardwareUnspecifiedError";
72: break;
73:
74: case kAudioHardwareUnknownPropertyError:
75: str = "kAudioHardwareUnknownPropertyError";
76: break;
77:
78: case kAudioHardwareBadPropertySizeError:
79: str = "kAudioHardwareBadPropertySizeError";
80: break;
81:
82: case kAudioHardwareIllegalOperationError:
83: str = "kAudioHardwareIllegalOperationError";
84: break;
85:
86: case kAudioHardwareBadDeviceError:
87: str = "kAudioHardwareBadDeviceError";
88: break;
89:
90: case kAudioHardwareBadStreamError:
91: str = "kAudioHardwareBadStreamError";
92: break;
93:
94: case kAudioHardwareUnsupportedOperationError:
95: str = "kAudioHardwareUnsupportedOperationError";
96: break;
97:
98: case kAudioDeviceUnsupportedFormatError:
99: str = "kAudioDeviceUnsupportedFormatError";
100: break;
101:
102: case kAudioDevicePermissionsError:
103: str = "kAudioDevicePermissionsError";
104: break;
105:
106: default:
107: AUD_log (AUDIO_CAP, "Reason: status code %ld\n", status);
108: return;
109: }
110:
111: AUD_log (AUDIO_CAP, "Reason: %s\n", str);
112: }
113:
114: static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
115: OSStatus status,
116: const char *fmt,
117: ...
118: )
119: {
120: va_list ap;
121:
122: va_start (ap, fmt);
123: AUD_log (AUDIO_CAP, fmt, ap);
124: va_end (ap);
125:
126: coreaudio_logstatus (status);
127: }
128:
129: static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
130: OSStatus status,
131: const char *typ,
132: const char *fmt,
133: ...
134: )
135: {
136: va_list ap;
137:
138: AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
139:
140: va_start (ap, fmt);
141: AUD_vlog (AUDIO_CAP, fmt, ap);
142: va_end (ap);
143:
144: coreaudio_logstatus (status);
145: }
146:
147: static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
148: {
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");
158: }
159: return result;
160: }
161:
162: static void coreaudio_atexit (void)
163: {
164: conf.isAtexit = 1;
165: }
166:
167: static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
168: {
169: int err;
170:
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;
176: }
177: return 0;
178: }
179:
180: static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
181: {
182: int err;
183:
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;
189: }
190: return 0;
191: }
192:
193: static int coreaudio_run_out (HWVoiceOut *hw)
194: {
195: int live, decr;
196: coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
197:
198: if (coreaudio_lock (core, "coreaudio_run_out")) {
199: return 0;
200: }
201:
202: live = audio_pcm_hw_get_live_out (hw);
203:
204: if (core->decr > live) {
205: ldebug ("core->decr %d live %d core->live %d\n",
206: core->decr,
207: live,
208: core->live);
209: }
210:
211: decr = audio_MIN (core->decr, live);
212: core->decr -= decr;
213:
214: core->live = live - decr;
215: hw->rpos = core->rpos;
216:
217: coreaudio_unlock (core, "coreaudio_run_out");
218: return decr;
219: }
220:
221:
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)
230: {
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: st_sample_t *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
244:
245: if (coreaudio_lock (core, "audioDeviceIOProc")) {
246: inInputTime = 0;
247: return 0;
248: }
249:
250: frameCount = core->audioDevicePropertyBufferFrameSize;
251: live = core->live;
252:
253:
254: if (live < frameCount) {
255: inInputTime = 0;
256: coreaudio_unlock (core, "audioDeviceIOProc(empty)");
257: return 0;
258: }
259:
260: rpos = core->rpos;
261: src = hw->mix_buf + rpos;
262:
263:
264: for (frame = 0; frame < frameCount; frame++) {
265: #ifdef FLOAT_MIXENG
266: *out++ = src[frame].l;
267: *out++ = src[frame].r;
268: #else
269: #ifdef RECIPROCAL
270: *out++ = src[frame].l * scale;
271: *out++ = src[frame].r * scale;
272: #else
273: *out++ = src[frame].l / scale;
274: *out++ = src[frame].r / scale;
275: #endif
276: #endif
277: }
278:
279: rpos = (rpos + frameCount) % hw->samples;
280: core->decr += frameCount;
281: core->rpos = rpos;
282:
283: coreaudio_unlock (core, "audioDeviceIOProc");
284: return 0;
285: }
286:
287: static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
288: {
289: return audio_pcm_sw_write (sw, buf, len);
290: }
291:
292: static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
293: {
294: OSStatus status;
295: coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
296: UInt32 propertySize;
297: int err;
298: const char *typ = "playback";
299: AudioValueRange frameRange;
300:
301:
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;
306: }
307:
308: audio_pcm_init_info (&hw->info, as);
309:
310:
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;
320: }
321: if (core->outputDeviceID == kAudioDeviceUnknown) {
322: dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
323: return -1;
324: }
325:
326:
327: propertySize = sizeof(frameRange);
328: status = AudioDeviceGetProperty(
329: core->outputDeviceID,
330: 0,
331: 0,
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;
339: }
340:
341: if (frameRange.mMinimum > conf.buffer_frames) {
342: core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
343: dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
344: }
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);
348: }
349: else {
350: core->audioDevicePropertyBufferFrameSize = conf.buffer_frames;
351: }
352:
353:
354: propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
355: status = AudioDeviceSetProperty(
356: core->outputDeviceID,
357: NULL,
358: 0,
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;
368: }
369:
370:
371: propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
372: status = AudioDeviceGetProperty(
373: core->outputDeviceID,
374: 0,
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;
383: }
384: hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize;
385:
386:
387: propertySize = sizeof(core->outputStreamBasicDescription);
388: status = AudioDeviceGetProperty(
389: core->outputDeviceID,
390: 0,
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;
400: }
401:
402:
403: core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
404: propertySize = sizeof(core->outputStreamBasicDescription);
405: status = AudioDeviceSetProperty(
406: core->outputDeviceID,
407: 0,
408: 0,
409: 0,
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;
418: }
419:
420:
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;
426: }
427:
428:
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;
436: }
437: }
438:
439: return 0;
440: }
441:
442: static void coreaudio_fini_out (HWVoiceOut *hw)
443: {
444: OSStatus status;
445: int err;
446: coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
447:
448: if (!conf.isAtexit) {
449:
450: if (isPlaying(core->outputDeviceID)) {
451: status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
452: if (status != kAudioHardwareNoError) {
453: coreaudio_logerr (status, "Could not stop playback\n");
454: }
455: }
456:
457:
458: status = AudioDeviceRemoveIOProc(core->outputDeviceID,
459: audioDeviceIOProc);
460: if (status != kAudioHardwareNoError) {
461: coreaudio_logerr (status, "Could not remove IOProc\n");
462: }
463: }
464: core->outputDeviceID = kAudioDeviceUnknown;
465:
466:
467: err = pthread_mutex_destroy(&core->mutex);
468: if (err) {
469: dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
470: }
471: }
472:
473: static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
474: {
475: OSStatus status;
476: coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
477:
478: switch (cmd) {
479: case VOICE_ENABLE:
480:
481: if (!isPlaying(core->outputDeviceID)) {
482: status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
483: if (status != kAudioHardwareNoError) {
484: coreaudio_logerr (status, "Could not resume playback\n");
485: }
486: }
487: break;
488:
489: case VOICE_DISABLE:
490:
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");
496: }
497: }
498: }
499: break;
500: }
501: return 0;
502: }
503:
504: static void *coreaudio_audio_init (void)
505: {
506: atexit(coreaudio_atexit);
507: return &coreaudio_audio_init;
508: }
509:
510: static void coreaudio_audio_fini (void *opaque)
511: {
512: (void) opaque;
513: }
514:
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}
521: };
522:
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,
529:
530: NULL,
531: NULL,
532: NULL,
533: NULL,
534: NULL
535: };
536:
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
550: };