mirror of
				https://git.proxmox.com/git/qemu
				synced 2025-10-25 19:30:16 +00:00 
			
		
		
		
	 744d364418
			
		
	
	
		744d364418
		
	
	
	
	
		
			
			OSStatus type is defined as SInt32. That's signed int on __LP64__ and signed long otherwise. Since it is an explicit 32-bit-width type, cast to corresponsing POSIX type and use PRId32 format specifier. This avoids a warning on ppc64. Cc: malc <av1474@comtv.ru> Signed-off-by: Andreas Faerber <andreas.faerber@web.de> Signed-off-by: malc <av1474@comtv.ru>
		
			
				
	
	
		
			550 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			550 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * QEMU OS X CoreAudio audio driver
 | |
|  *
 | |
|  * Copyright (c) 2005 Mike Kronenberg
 | |
|  *
 | |
|  * Permission is hereby granted, free of charge, to any person obtaining a copy
 | |
|  * of this software and associated documentation files (the "Software"), to deal
 | |
|  * in the Software without restriction, including without limitation the rights
 | |
|  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | |
|  * copies of the Software, and to permit persons to whom the Software is
 | |
|  * furnished to do so, subject to the following conditions:
 | |
|  *
 | |
|  * The above copyright notice and this permission notice shall be included in
 | |
|  * all copies or substantial portions of the Software.
 | |
|  *
 | |
|  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | |
|  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | |
|  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 | |
|  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | |
|  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | |
|  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | |
|  * THE SOFTWARE.
 | |
|  */
 | |
| 
 | |
| #include <CoreAudio/CoreAudio.h>
 | |
| #include <string.h>             /* strerror */
 | |
| #include <pthread.h>            /* pthread_X */
 | |
| 
 | |
| #include "qemu-common.h"
 | |
| #include "audio.h"
 | |
| 
 | |
| #define AUDIO_CAP "coreaudio"
 | |
| #include "audio_int.h"
 | |
| 
 | |
| struct {
 | |
|     int buffer_frames;
 | |
|     int nbuffers;
 | |
|     int isAtexit;
 | |
| } conf = {
 | |
|     .buffer_frames = 512,
 | |
|     .nbuffers = 4,
 | |
|     .isAtexit = 0
 | |
| };
 | |
| 
 | |
| typedef struct coreaudioVoiceOut {
 | |
|     HWVoiceOut hw;
 | |
|     pthread_mutex_t mutex;
 | |
|     int isAtexit;
 | |
|     AudioDeviceID outputDeviceID;
 | |
|     UInt32 audioDevicePropertyBufferFrameSize;
 | |
|     AudioStreamBasicDescription outputStreamBasicDescription;
 | |
|     int live;
 | |
|     int decr;
 | |
|     int rpos;
 | |
| } coreaudioVoiceOut;
 | |
| 
 | |
| static void coreaudio_logstatus (OSStatus status)
 | |
| {
 | |
|     const char *str = "BUG";
 | |
| 
 | |
|     switch(status) {
 | |
|     case kAudioHardwareNoError:
 | |
|         str = "kAudioHardwareNoError";
 | |
|         break;
 | |
| 
 | |
|     case kAudioHardwareNotRunningError:
 | |
|         str = "kAudioHardwareNotRunningError";
 | |
|         break;
 | |
| 
 | |
|     case kAudioHardwareUnspecifiedError:
 | |
|         str = "kAudioHardwareUnspecifiedError";
 | |
|         break;
 | |
| 
 | |
|     case kAudioHardwareUnknownPropertyError:
 | |
|         str = "kAudioHardwareUnknownPropertyError";
 | |
|         break;
 | |
| 
 | |
|     case kAudioHardwareBadPropertySizeError:
 | |
|         str = "kAudioHardwareBadPropertySizeError";
 | |
|         break;
 | |
| 
 | |
|     case kAudioHardwareIllegalOperationError:
 | |
|         str = "kAudioHardwareIllegalOperationError";
 | |
|         break;
 | |
| 
 | |
|     case kAudioHardwareBadDeviceError:
 | |
|         str = "kAudioHardwareBadDeviceError";
 | |
|         break;
 | |
| 
 | |
|     case kAudioHardwareBadStreamError:
 | |
|         str = "kAudioHardwareBadStreamError";
 | |
|         break;
 | |
| 
 | |
|     case kAudioHardwareUnsupportedOperationError:
 | |
|         str = "kAudioHardwareUnsupportedOperationError";
 | |
|         break;
 | |
| 
 | |
|     case kAudioDeviceUnsupportedFormatError:
 | |
|         str = "kAudioDeviceUnsupportedFormatError";
 | |
|         break;
 | |
| 
 | |
|     case kAudioDevicePermissionsError:
 | |
|         str = "kAudioDevicePermissionsError";
 | |
|         break;
 | |
| 
 | |
|     default:
 | |
|         AUD_log (AUDIO_CAP, "Reason: status code %" PRId32 "\n", (int32_t)status);
 | |
|         return;
 | |
|     }
 | |
| 
 | |
|     AUD_log (AUDIO_CAP, "Reason: %s\n", str);
 | |
| }
 | |
| 
 | |
| static void GCC_FMT_ATTR (2, 3) coreaudio_logerr (
 | |
|     OSStatus status,
 | |
|     const char *fmt,
 | |
|     ...
 | |
|     )
 | |
| {
 | |
|     va_list ap;
 | |
| 
 | |
|     va_start (ap, fmt);
 | |
|     AUD_log (AUDIO_CAP, fmt, ap);
 | |
|     va_end (ap);
 | |
| 
 | |
|     coreaudio_logstatus (status);
 | |
| }
 | |
| 
 | |
| static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
 | |
|     OSStatus status,
 | |
|     const char *typ,
 | |
|     const char *fmt,
 | |
|     ...
 | |
|     )
 | |
| {
 | |
|     va_list ap;
 | |
| 
 | |
|     AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
 | |
| 
 | |
|     va_start (ap, fmt);
 | |
|     AUD_vlog (AUDIO_CAP, fmt, ap);
 | |
|     va_end (ap);
 | |
| 
 | |
|     coreaudio_logstatus (status);
 | |
| }
 | |
| 
 | |
| static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
 | |
| {
 | |
|     OSStatus status;
 | |
|     UInt32 result = 0;
 | |
|     UInt32 propertySize = sizeof(outputDeviceID);
 | |
|     status = AudioDeviceGetProperty(
 | |
|         outputDeviceID, 0, 0,
 | |
|         kAudioDevicePropertyDeviceIsRunning, &propertySize, &result);
 | |
|     if (status != kAudioHardwareNoError) {
 | |
|         coreaudio_logerr(status,
 | |
|                          "Could not determine whether Device is playing\n");
 | |
|     }
 | |
|     return result;
 | |
| }
 | |
| 
 | |
| static void coreaudio_atexit (void)
 | |
| {
 | |
|     conf.isAtexit = 1;
 | |
| }
 | |
| 
 | |
| static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
 | |
| {
 | |
|     int err;
 | |
| 
 | |
|     err = pthread_mutex_lock (&core->mutex);
 | |
|     if (err) {
 | |
|         dolog ("Could not lock voice for %s\nReason: %s\n",
 | |
|                fn_name, strerror (err));
 | |
|         return -1;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
 | |
| {
 | |
|     int err;
 | |
| 
 | |
|     err = pthread_mutex_unlock (&core->mutex);
 | |
|     if (err) {
 | |
|         dolog ("Could not unlock voice for %s\nReason: %s\n",
 | |
|                fn_name, strerror (err));
 | |
|         return -1;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int coreaudio_run_out (HWVoiceOut *hw, int live)
 | |
| {
 | |
|     int decr;
 | |
|     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
 | |
| 
 | |
|     if (coreaudio_lock (core, "coreaudio_run_out")) {
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     if (core->decr > live) {
 | |
|         ldebug ("core->decr %d live %d core->live %d\n",
 | |
|                 core->decr,
 | |
|                 live,
 | |
|                 core->live);
 | |
|     }
 | |
| 
 | |
|     decr = audio_MIN (core->decr, live);
 | |
|     core->decr -= decr;
 | |
| 
 | |
|     core->live = live - decr;
 | |
|     hw->rpos = core->rpos;
 | |
| 
 | |
|     coreaudio_unlock (core, "coreaudio_run_out");
 | |
|     return decr;
 | |
| }
 | |
| 
 | |
| /* callback to feed audiooutput buffer */
 | |
| static OSStatus audioDeviceIOProc(
 | |
|     AudioDeviceID inDevice,
 | |
|     const AudioTimeStamp* inNow,
 | |
|     const AudioBufferList* inInputData,
 | |
|     const AudioTimeStamp* inInputTime,
 | |
|     AudioBufferList* outOutputData,
 | |
|     const AudioTimeStamp* inOutputTime,
 | |
|     void* hwptr)
 | |
| {
 | |
|     UInt32 frame, frameCount;
 | |
|     float *out = outOutputData->mBuffers[0].mData;
 | |
|     HWVoiceOut *hw = hwptr;
 | |
|     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
 | |
|     int rpos, live;
 | |
|     struct st_sample *src;
 | |
| #ifndef FLOAT_MIXENG
 | |
| #ifdef RECIPROCAL
 | |
|     const float scale = 1.f / UINT_MAX;
 | |
| #else
 | |
|     const float scale = UINT_MAX;
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
|     if (coreaudio_lock (core, "audioDeviceIOProc")) {
 | |
|         inInputTime = 0;
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     frameCount = core->audioDevicePropertyBufferFrameSize;
 | |
|     live = core->live;
 | |
| 
 | |
|     /* if there are not enough samples, set signal and return */
 | |
|     if (live < frameCount) {
 | |
|         inInputTime = 0;
 | |
|         coreaudio_unlock (core, "audioDeviceIOProc(empty)");
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     rpos = core->rpos;
 | |
|     src = hw->mix_buf + rpos;
 | |
| 
 | |
|     /* fill buffer */
 | |
|     for (frame = 0; frame < frameCount; frame++) {
 | |
| #ifdef FLOAT_MIXENG
 | |
|         *out++ = src[frame].l; /* left channel */
 | |
|         *out++ = src[frame].r; /* right channel */
 | |
| #else
 | |
| #ifdef RECIPROCAL
 | |
|         *out++ = src[frame].l * scale; /* left channel */
 | |
|         *out++ = src[frame].r * scale; /* right channel */
 | |
| #else
 | |
|         *out++ = src[frame].l / scale; /* left channel */
 | |
|         *out++ = src[frame].r / scale; /* right channel */
 | |
| #endif
 | |
| #endif
 | |
|     }
 | |
| 
 | |
|     rpos = (rpos + frameCount) % hw->samples;
 | |
|     core->decr += frameCount;
 | |
|     core->rpos = rpos;
 | |
| 
 | |
|     coreaudio_unlock (core, "audioDeviceIOProc");
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
 | |
| {
 | |
|     return audio_pcm_sw_write (sw, buf, len);
 | |
| }
 | |
| 
 | |
| static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
 | |
| {
 | |
|     OSStatus status;
 | |
|     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
 | |
|     UInt32 propertySize;
 | |
|     int err;
 | |
|     const char *typ = "playback";
 | |
|     AudioValueRange frameRange;
 | |
| 
 | |
|     /* create mutex */
 | |
|     err = pthread_mutex_init(&core->mutex, NULL);
 | |
|     if (err) {
 | |
|         dolog("Could not create mutex\nReason: %s\n", strerror (err));
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     audio_pcm_init_info (&hw->info, as);
 | |
| 
 | |
|     /* open default output device */
 | |
|     propertySize = sizeof(core->outputDeviceID);
 | |
|     status = AudioHardwareGetProperty(
 | |
|         kAudioHardwarePropertyDefaultOutputDevice,
 | |
|         &propertySize,
 | |
|         &core->outputDeviceID);
 | |
|     if (status != kAudioHardwareNoError) {
 | |
|         coreaudio_logerr2 (status, typ,
 | |
|                            "Could not get default output Device\n");
 | |
|         return -1;
 | |
|     }
 | |
|     if (core->outputDeviceID == kAudioDeviceUnknown) {
 | |
|         dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* get minimum and maximum buffer frame sizes */
 | |
|     propertySize = sizeof(frameRange);
 | |
|     status = AudioDeviceGetProperty(
 | |
|         core->outputDeviceID,
 | |
|         0,
 | |
|         0,
 | |
|         kAudioDevicePropertyBufferFrameSizeRange,
 | |
|         &propertySize,
 | |
|         &frameRange);
 | |
|     if (status != kAudioHardwareNoError) {
 | |
|         coreaudio_logerr2 (status, typ,
 | |
|                            "Could not get device buffer frame range\n");
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     if (frameRange.mMinimum > conf.buffer_frames) {
 | |
|         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
 | |
|         dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
 | |
|     }
 | |
|     else if (frameRange.mMaximum < conf.buffer_frames) {
 | |
|         core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
 | |
|         dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
 | |
|     }
 | |
|     else {
 | |
|         core->audioDevicePropertyBufferFrameSize = conf.buffer_frames;
 | |
|     }
 | |
| 
 | |
|     /* set Buffer Frame Size */
 | |
|     propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
 | |
|     status = AudioDeviceSetProperty(
 | |
|         core->outputDeviceID,
 | |
|         NULL,
 | |
|         0,
 | |
|         false,
 | |
|         kAudioDevicePropertyBufferFrameSize,
 | |
|         propertySize,
 | |
|         &core->audioDevicePropertyBufferFrameSize);
 | |
|     if (status != kAudioHardwareNoError) {
 | |
|         coreaudio_logerr2 (status, typ,
 | |
|                            "Could not set device buffer frame size %" PRIu32 "\n",
 | |
|                            (uint32_t)core->audioDevicePropertyBufferFrameSize);
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* get Buffer Frame Size */
 | |
|     propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
 | |
|     status = AudioDeviceGetProperty(
 | |
|         core->outputDeviceID,
 | |
|         0,
 | |
|         false,
 | |
|         kAudioDevicePropertyBufferFrameSize,
 | |
|         &propertySize,
 | |
|         &core->audioDevicePropertyBufferFrameSize);
 | |
|     if (status != kAudioHardwareNoError) {
 | |
|         coreaudio_logerr2 (status, typ,
 | |
|                            "Could not get device buffer frame size\n");
 | |
|         return -1;
 | |
|     }
 | |
|     hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize;
 | |
| 
 | |
|     /* get StreamFormat */
 | |
|     propertySize = sizeof(core->outputStreamBasicDescription);
 | |
|     status = AudioDeviceGetProperty(
 | |
|         core->outputDeviceID,
 | |
|         0,
 | |
|         false,
 | |
|         kAudioDevicePropertyStreamFormat,
 | |
|         &propertySize,
 | |
|         &core->outputStreamBasicDescription);
 | |
|     if (status != kAudioHardwareNoError) {
 | |
|         coreaudio_logerr2 (status, typ,
 | |
|                            "Could not get Device Stream properties\n");
 | |
|         core->outputDeviceID = kAudioDeviceUnknown;
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* set Samplerate */
 | |
|     core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
 | |
|     propertySize = sizeof(core->outputStreamBasicDescription);
 | |
|     status = AudioDeviceSetProperty(
 | |
|         core->outputDeviceID,
 | |
|         0,
 | |
|         0,
 | |
|         0,
 | |
|         kAudioDevicePropertyStreamFormat,
 | |
|         propertySize,
 | |
|         &core->outputStreamBasicDescription);
 | |
|     if (status != kAudioHardwareNoError) {
 | |
|         coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
 | |
|                            as->freq);
 | |
|         core->outputDeviceID = kAudioDeviceUnknown;
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* set Callback */
 | |
|     status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
 | |
|     if (status != kAudioHardwareNoError) {
 | |
|         coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
 | |
|         core->outputDeviceID = kAudioDeviceUnknown;
 | |
|         return -1;
 | |
|     }
 | |
| 
 | |
|     /* start Playback */
 | |
|     if (!isPlaying(core->outputDeviceID)) {
 | |
|         status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
 | |
|         if (status != kAudioHardwareNoError) {
 | |
|             coreaudio_logerr2 (status, typ, "Could not start playback\n");
 | |
|             AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
 | |
|             core->outputDeviceID = kAudioDeviceUnknown;
 | |
|             return -1;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static void coreaudio_fini_out (HWVoiceOut *hw)
 | |
| {
 | |
|     OSStatus status;
 | |
|     int err;
 | |
|     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
 | |
| 
 | |
|     if (!conf.isAtexit) {
 | |
|         /* stop playback */
 | |
|         if (isPlaying(core->outputDeviceID)) {
 | |
|             status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
 | |
|             if (status != kAudioHardwareNoError) {
 | |
|                 coreaudio_logerr (status, "Could not stop playback\n");
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         /* remove callback */
 | |
|         status = AudioDeviceRemoveIOProc(core->outputDeviceID,
 | |
|                                          audioDeviceIOProc);
 | |
|         if (status != kAudioHardwareNoError) {
 | |
|             coreaudio_logerr (status, "Could not remove IOProc\n");
 | |
|         }
 | |
|     }
 | |
|     core->outputDeviceID = kAudioDeviceUnknown;
 | |
| 
 | |
|     /* destroy mutex */
 | |
|     err = pthread_mutex_destroy(&core->mutex);
 | |
|     if (err) {
 | |
|         dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
 | |
|     }
 | |
| }
 | |
| 
 | |
| static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
 | |
| {
 | |
|     OSStatus status;
 | |
|     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
 | |
| 
 | |
|     switch (cmd) {
 | |
|     case VOICE_ENABLE:
 | |
|         /* start playback */
 | |
|         if (!isPlaying(core->outputDeviceID)) {
 | |
|             status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
 | |
|             if (status != kAudioHardwareNoError) {
 | |
|                 coreaudio_logerr (status, "Could not resume playback\n");
 | |
|             }
 | |
|         }
 | |
|         break;
 | |
| 
 | |
|     case VOICE_DISABLE:
 | |
|         /* stop playback */
 | |
|         if (!conf.isAtexit) {
 | |
|             if (isPlaying(core->outputDeviceID)) {
 | |
|                 status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
 | |
|                 if (status != kAudioHardwareNoError) {
 | |
|                     coreaudio_logerr (status, "Could not pause playback\n");
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         break;
 | |
|     }
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| static void *coreaudio_audio_init (void)
 | |
| {
 | |
|     atexit(coreaudio_atexit);
 | |
|     return &coreaudio_audio_init;
 | |
| }
 | |
| 
 | |
| static void coreaudio_audio_fini (void *opaque)
 | |
| {
 | |
|     (void) opaque;
 | |
| }
 | |
| 
 | |
| static struct audio_option coreaudio_options[] = {
 | |
|     {
 | |
|         .name  = "BUFFER_SIZE",
 | |
|         .tag   = AUD_OPT_INT,
 | |
|         .valp  = &conf.buffer_frames,
 | |
|         .descr = "Size of the buffer in frames"
 | |
|     },
 | |
|     {
 | |
|         .name  = "BUFFER_COUNT",
 | |
|         .tag   = AUD_OPT_INT,
 | |
|         .valp  = &conf.nbuffers,
 | |
|         .descr = "Number of buffers"
 | |
|     },
 | |
|     { /* End of list */ }
 | |
| };
 | |
| 
 | |
| static struct audio_pcm_ops coreaudio_pcm_ops = {
 | |
|     .init_out = coreaudio_init_out,
 | |
|     .fini_out = coreaudio_fini_out,
 | |
|     .run_out  = coreaudio_run_out,
 | |
|     .write    = coreaudio_write,
 | |
|     .ctl_out  = coreaudio_ctl_out
 | |
| };
 | |
| 
 | |
| struct audio_driver coreaudio_audio_driver = {
 | |
|     .name           = "coreaudio",
 | |
|     .descr          = "CoreAudio http://developer.apple.com/audio/coreaudio.html",
 | |
|     .options        = coreaudio_options,
 | |
|     .init           = coreaudio_audio_init,
 | |
|     .fini           = coreaudio_audio_fini,
 | |
|     .pcm_ops        = &coreaudio_pcm_ops,
 | |
|     .can_be_default = 1,
 | |
|     .max_voices_out = 1,
 | |
|     .max_voices_in  = 0,
 | |
|     .voice_size_out = sizeof (coreaudioVoiceOut),
 | |
|     .voice_size_in  = 0
 | |
| };
 |