Implement the audio-in support in coreaudio
authorJun Tian <jun.j.tian@intel.com>
Thu, 24 Jan 2013 09:12:10 +0000 (17:12 +0800)
committerJun Tian <jun.j.tian@intel.com>
Thu, 24 Jan 2013 09:12:10 +0000 (17:12 +0800)
The auido-in implementation in the coreaudio is ported form the Android emulator.

audio/coreaudio.c

index 1b1020134065f71b2d1e663a7a4035ff14600944..d9bd8ff9c828d07cd73c94c4c6ee2dde8afe49bb 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * QEMU OS X CoreAudio audio driver
  *
+ * Copyright (c) 2008 The Android Open Source Project
  * Copyright (c) 2005 Mike Kronenberg
  *
  * Permission is hereby granted, free of charge, to any person obtaining a copy
 #include "audio_int.h"
 
 #ifdef CONFIG_MARU
-// FIXME: W/A for avoid guest block waiting for audio in
-// TODO: Implements audio in using CoreAudio
-#include "qemu-timer.h"
+#include "../tizen/src/debug_ch.h"
 #endif
 
 struct {
-    int buffer_frames;
-    int nbuffers;
+    int o_buffer_frames;
+    int o_nbuffers;
+    int i_buffer_frames;
+    int i_nbuffers;
     int isAtexit;
 } conf = {
-    .buffer_frames = 512,
-    .nbuffers = 4,
+    .o_buffer_frames = 512,
+    .o_nbuffers = 4,
+    .i_buffer_frames = 512,
+    .i_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;
-
-#ifdef CONFIG_MARU
-typedef struct NoVoiceIn {
-    HWVoiceIn hw;
-    int64_t old_ticks;
-} NoVoiceIn;
-#endif
-
 static void coreaudio_logstatus (OSStatus status)
 {
     const char *str = "BUG";
@@ -157,13 +141,26 @@ static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
     coreaudio_logstatus (status);
 }
 
-static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
+typedef struct coreAudioVoice {
+    pthread_mutex_t              mutex;
+    AudioDeviceID                deviceID;
+    Boolean                      isInput;
+    UInt32                       bufferFrameSize;
+    AudioStreamBasicDescription  streamBasicDescription;
+    AudioDeviceIOProc            ioproc;
+    int                          live;
+    int                          decr;
+    int                          pos;
+} coreaudioVoice;
+
+static inline UInt32
+coreaudio_voice_isPlaying (coreaudioVoice *core)
 {
     OSStatus status;
     UInt32 result = 0;
-    UInt32 propertySize = sizeof(outputDeviceID);
+    UInt32 propertySize = sizeof(core->deviceID);
     status = AudioDeviceGetProperty(
-        outputDeviceID, 0, 0,
+        core->deviceID, 0, core->isInput,
         kAudioDevicePropertyDeviceIsRunning, &propertySize, &result);
     if (status != kAudioHardwareNoError) {
         coreaudio_logerr(status,
@@ -177,7 +174,7 @@ static void coreaudio_atexit (void)
     conf.isAtexit = 1;
 }
 
-static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
+static int coreaudio_lock (coreaudioVoice *core, const char *fn_name)
 {
     int err;
 
@@ -190,7 +187,7 @@ static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
     return 0;
 }
 
-static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
+static int coreaudio_unlock (coreaudioVoice *core, const char *fn_name)
 {
     int err;
 
@@ -203,111 +200,81 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
     return 0;
 }
 
-static int coreaudio_run_out (HWVoiceOut *hw, int live)
+static int
+coreaudio_voice_ctl (coreaudioVoice*  core, int cmd)
 {
-    int decr;
-    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+    OSStatus status;
 
-    if (coreaudio_lock (core, "coreaudio_run_out")) {
-        return 0;
-    }
+    switch (cmd) {
+    case VOICE_ENABLE:
+        /* start playback */
+        printf("%s: %s started\n", __FUNCTION__, core->isInput ? "input" : "output");
+        if (!coreaudio_voice_isPlaying(core)) {
+            status = AudioDeviceStart(core->deviceID, core->ioproc);
+            if (status != kAudioHardwareNoError) {
+                coreaudio_logerr (status, "Could not resume playback\n");
+            }
+        }
+        break;
 
-    if (core->decr > live) {
-        ldebug ("core->decr %d live %d core->live %d\n",
-                core->decr,
-                live,
-                core->live);
+    case VOICE_DISABLE:
+        /* stop playback */
+        printf("%s: %s stopped\n", __FUNCTION__, core->isInput ? "input" : "output");
+        if (!conf.isAtexit) {
+            if (coreaudio_voice_isPlaying(core)) {
+                status = AudioDeviceStop(core->deviceID, core->ioproc);
+                if (status != kAudioHardwareNoError) {
+                    coreaudio_logerr (status, "Could not pause playback\n");
+                }
+            }
+        }
+        break;
     }
-
-    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;
+    return 0;
 }
 
-/* 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)
+static void
+coreaudio_voice_fini (coreaudioVoice*  core)
 {
-    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;
-    }
+    OSStatus status;
+    int err;
 
-    frameCount = core->audioDevicePropertyBufferFrameSize;
-    live = core->live;
+    if (!conf.isAtexit) {
+        /* stop playback */
+        coreaudio_voice_ctl(core, VOICE_DISABLE);
 
-    /* if there are not enough samples, set signal and return */
-    if (live < frameCount) {
-        inInputTime = 0;
-        coreaudio_unlock (core, "audioDeviceIOProc(empty)");
-        return 0;
+        /* remove callback */
+        status = AudioDeviceRemoveIOProc(core->deviceID, core->ioproc);
+        if (status != kAudioHardwareNoError) {
+            coreaudio_logerr (status, "Could not remove IOProc\n");
+        }
     }
+    core->deviceID = kAudioDeviceUnknown;
 
-    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
+    /* destroy mutex */
+    err = pthread_mutex_destroy(&core->mutex);
+    if (err) {
+        dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
     }
-
-    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)
+static int
+coreaudio_voice_init (coreaudioVoice*    core,
+                      struct audsettings*  as,
+                      int                frameSize,
+                      AudioDeviceIOProc  ioproc,
+                      void*              hw,
+                      int                input)
 {
-    OSStatus status;
-    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
-    UInt32 propertySize;
-    int err;
-    const char *typ = "playback";
+    OSStatus  status;
+    UInt32    propertySize;
+    int       err;
+    int       bits = 8;
     AudioValueRange frameRange;
+    const char*  typ = input ? "input" : "playback";
+
+    core->isInput = input ? true : false;
 
     /* create mutex */
     err = pthread_mutex_init(&core->mutex, NULL);
@@ -316,20 +283,28 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
         return -1;
     }
 
-    audio_pcm_init_info (&hw->info, as);
+    if (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) {
+        bits = 16;
+    }
 
+    // TODO: audio_pcm_init_info (&hw->info, as);
     /* open default output device */
-    propertySize = sizeof(core->outputDeviceID);
+   /* note: we use DefaultSystemOutputDevice because DefaultOutputDevice seems to
+    * always link to the internal speakers, and not the ones selected through system properties
+    * go figure...
+    */
+    propertySize = sizeof(core->deviceID);
     status = AudioHardwareGetProperty(
-        kAudioHardwarePropertyDefaultOutputDevice,
+        input ? kAudioHardwarePropertyDefaultInputDevice :
+                kAudioHardwarePropertyDefaultSystemOutputDevice,
         &propertySize,
-        &core->outputDeviceID);
+        &core->deviceID);
     if (status != kAudioHardwareNoError) {
         coreaudio_logerr2 (status, typ,
-                           "Could not get default output Device\n");
+                           "Could not get default %s device\n", typ);
         return -1;
     }
-    if (core->outputDeviceID == kAudioDeviceUnknown) {
+    if (core->deviceID == kAudioDeviceUnknown) {
         dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
         return -1;
     }
@@ -337,9 +312,9 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
     /* get minimum and maximum buffer frame sizes */
     propertySize = sizeof(frameRange);
     status = AudioDeviceGetProperty(
-        core->outputDeviceID,
-        0,
+        core->deviceID,
         0,
+        core->isInput,
         kAudioDevicePropertyBufferFrameSizeRange,
         &propertySize,
         &frameRange);
@@ -349,100 +324,101 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
         return -1;
     }
 
-    if (frameRange.mMinimum > conf.buffer_frames) {
-        core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
-        dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
+    if (frameRange.mMinimum > frameSize) {
+        core->bufferFrameSize = (UInt32) frameRange.mMinimum;
+        dolog ("warning: Upsizing Output 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 if (frameRange.mMaximum < frameSize) {
+        core->bufferFrameSize = (UInt32) frameRange.mMaximum;
+        dolog ("warning: Downsizing Output Buffer Frames to %f\n", frameRange.mMaximum);
     }
     else {
-        core->audioDevicePropertyBufferFrameSize = conf.buffer_frames;
+        core->bufferFrameSize = frameSize;
     }
 
     /* set Buffer Frame Size */
-    propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
+    propertySize = sizeof(core->bufferFrameSize);
     status = AudioDeviceSetProperty(
-        core->outputDeviceID,
+        core->deviceID,
         NULL,
         0,
-        false,
+        core->isInput,
         kAudioDevicePropertyBufferFrameSize,
         propertySize,
-        &core->audioDevicePropertyBufferFrameSize);
+        &core->bufferFrameSize);
     if (status != kAudioHardwareNoError) {
         coreaudio_logerr2 (status, typ,
-                           "Could not set device buffer frame size %" PRIu32 "\n",
-                           (uint32_t)core->audioDevicePropertyBufferFrameSize);
+                           "Could not set device buffer frame size %ld\n",
+                           core->bufferFrameSize);
         return -1;
     }
 
     /* get Buffer Frame Size */
-    propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
+    propertySize = sizeof(core->bufferFrameSize);
     status = AudioDeviceGetProperty(
-        core->outputDeviceID,
+        core->deviceID,
         0,
-        false,
+        core->isInput,
         kAudioDevicePropertyBufferFrameSize,
         &propertySize,
-        &core->audioDevicePropertyBufferFrameSize);
+        &core->bufferFrameSize);
     if (status != kAudioHardwareNoError) {
         coreaudio_logerr2 (status, typ,
                            "Could not get device buffer frame size\n");
         return -1;
     }
-    hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize;
+    // TODO: hw->samples = *pNBuffers * core->bufferFrameSize;
 
     /* get StreamFormat */
-    propertySize = sizeof(core->outputStreamBasicDescription);
+    propertySize = sizeof(core->streamBasicDescription);
     status = AudioDeviceGetProperty(
-        core->outputDeviceID,
+        core->deviceID,
         0,
-        false,
+        core->isInput,
         kAudioDevicePropertyStreamFormat,
         &propertySize,
-        &core->outputStreamBasicDescription);
+        &core->streamBasicDescription);
     if (status != kAudioHardwareNoError) {
         coreaudio_logerr2 (status, typ,
                            "Could not get Device Stream properties\n");
-        core->outputDeviceID = kAudioDeviceUnknown;
+        core->deviceID = kAudioDeviceUnknown;
         return -1;
     }
 
     /* set Samplerate */
-    core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
-    propertySize = sizeof(core->outputStreamBasicDescription);
+    core->streamBasicDescription.mSampleRate = (Float64) as->freq;
+    propertySize = sizeof(core->streamBasicDescription);
     status = AudioDeviceSetProperty(
-        core->outputDeviceID,
-        0,
+        core->deviceID,
         0,
         0,
+        core->isInput,
         kAudioDevicePropertyStreamFormat,
         propertySize,
-        &core->outputStreamBasicDescription);
+        &core->streamBasicDescription);
     if (status != kAudioHardwareNoError) {
         coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
                            as->freq);
-        core->outputDeviceID = kAudioDeviceUnknown;
+        core->deviceID = kAudioDeviceUnknown;
         return -1;
     }
 
     /* set Callback */
-    status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
+    core->ioproc = ioproc;
+    status = AudioDeviceAddIOProc(core->deviceID, ioproc, hw);
     if (status != kAudioHardwareNoError) {
         coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
-        core->outputDeviceID = kAudioDeviceUnknown;
+        core->deviceID = kAudioDeviceUnknown;
         return -1;
     }
 
     /* start Playback */
-    if (!isPlaying(core->outputDeviceID)) {
-        status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
+    if (!input && !coreaudio_voice_isPlaying(core)) {
+        status = AudioDeviceStart(core->deviceID, core->ioproc);
         if (status != kAudioHardwareNoError) {
             coreaudio_logerr2 (status, typ, "Could not start playback\n");
-            AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
-            core->outputDeviceID = kAudioDeviceUnknown;
+            AudioDeviceRemoveIOProc(core->deviceID, core->ioproc);
+            core->deviceID = kAudioDeviceUnknown;
             return -1;
         }
     }
@@ -450,169 +426,333 @@ static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
     return 0;
 }
 
-static void coreaudio_fini_out (HWVoiceOut *hw)
-{
-    OSStatus status;
-    int err;
-    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+typedef struct coreaudioVoiceOut {
+    HWVoiceOut                   hw;
+    coreaudioVoice               core[1];
+} coreaudioVoiceOut;
 
-    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");
-            }
-        }
+#define  CORE_OUT(hw)  ((coreaudioVoiceOut*)(hw))->core
 
-        /* remove callback */
-        status = AudioDeviceRemoveIOProc(core->outputDeviceID,
-                                         audioDeviceIOProc);
-        if (status != kAudioHardwareNoError) {
-            coreaudio_logerr (status, "Could not remove IOProc\n");
-        }
+
+static int coreaudio_run_out (HWVoiceOut *hw, int live)
+{
+    int decr;
+    coreaudioVoice *core = CORE_OUT(hw);
+
+    if (coreaudio_lock (core, "coreaudio_run_out")) {
+        return 0;
     }
-    core->outputDeviceID = kAudioDeviceUnknown;
 
-    /* destroy mutex */
-    err = pthread_mutex_destroy(&core->mutex);
-    if (err) {
-        dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
+    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->pos;
+
+    coreaudio_unlock (core, "coreaudio_run_out");
+    return decr;
 }
 
-static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
+/* callback to feed audiooutput buffer */
+static OSStatus audioOutDeviceIOProc(
+    AudioDeviceID inDevice,
+    const AudioTimeStamp* inNow,
+    const AudioBufferList* inInputData,
+    const AudioTimeStamp* inInputTime,
+    AudioBufferList* outOutputData,
+    const AudioTimeStamp* inOutputTime,
+    void* hwptr)
 {
-    OSStatus status;
-    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+    UInt32 frame, frameCount;
+    float *out = outOutputData->mBuffers[0].mData;
+    HWVoiceOut *hw = hwptr;
+    coreaudioVoice *core = CORE_OUT(hw);
+    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
 
-    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;
+    if (coreaudio_lock (core, "audioDeviceIOProc")) {
+        inInputTime = 0;
+        return 0;
+    }
 
-    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;
+    frameCount = core->bufferFrameSize;
+    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->pos;
+    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->pos = rpos;
+
+    coreaudio_unlock (core, "audioDeviceIOProc");
     return 0;
 }
 
-static void *coreaudio_audio_init (void)
+static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
 {
-    atexit(coreaudio_atexit);
-    return &coreaudio_audio_init;
+    return audio_pcm_sw_write (sw, buf, len);
 }
 
-static void coreaudio_audio_fini (void *opaque)
+static int coreaudio_init_out (HWVoiceOut *hw, struct audsettings *as)
 {
-    (void) opaque;
+    coreaudioVoice*  core = CORE_OUT(hw);
+    int err;
+
+    audio_pcm_init_info (&hw->info, as);
+
+    err = coreaudio_voice_init (core, as, conf.o_buffer_frames, audioOutDeviceIOProc, hw, 0);
+    if (err < 0)
+        return err;
+
+    hw->samples = core->bufferFrameSize * conf.o_nbuffers;
+    return 0;
 }
 
-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 void coreaudio_fini_out (HWVoiceOut *hw)
+{
+    coreaudioVoice *core = CORE_OUT(hw);
 
-#ifdef CONFIG_MARU
-static int no_init_in (HWVoiceIn *hw, struct audsettings *as)
+    coreaudio_voice_fini (core);
+}
+
+static int
+coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
 {
-    audio_pcm_init_info (&hw->info, as);
-    hw->samples = 1024;
-    return 0;
+    coreaudioVoice *core = CORE_OUT(hw);
+
+    return coreaudio_voice_ctl (core, cmd);
 }
 
-static void no_fini_in (HWVoiceIn *hw)
+//
+// Enable coreaudio audio-in
+//
+
+typedef struct coreaudioVoiceIn {
+    HWVoiceIn        hw;
+    coreaudioVoice   core[1];
+} coreaudioVoiceIn;
+
+#define  CORE_IN(hw)  ((coreaudioVoiceIn *) (hw))->core
+
+
+static int coreaudio_run_in (HWVoiceIn *hw, int live)
 {
-    (void) hw;
+    int decr;
+
+    coreaudioVoice *core = CORE_IN(hw);
+
+    if (coreaudio_lock (core, "coreaudio_run_in")) {
+        return 0;
+    }
+    printf("%s: core.decr=%d core.pos=%d\n", __FUNCTION__, core->decr, core->pos);
+    decr        = core->decr;
+    core->decr -= decr;
+    hw->wpos    = core->pos;
+
+    coreaudio_unlock (core, "coreaudio_run_in");
+    return decr;
 }
 
-static int no_run_in (HWVoiceIn *hw)
+
+/* callback to feed audiooutput buffer */
+static OSStatus audioInDeviceIOProc(
+    AudioDeviceID inDevice,
+    const AudioTimeStamp* inNow,
+    const AudioBufferList* inInputData,
+    const AudioTimeStamp* inInputTime,
+    AudioBufferList* outOutputData,
+    const AudioTimeStamp* inOutputTime,
+    void* hwptr)
 {
-    NoVoiceIn *no = (NoVoiceIn *) hw;
-    int live = audio_pcm_hw_get_live_in (hw);
-    int dead = hw->samples - live;
-    int samples = 0;
-
-    if (dead) {
-        int64_t now = qemu_get_clock_ns (vm_clock);
-        int64_t ticks = now - no->old_ticks;
-        int64_t bytes =
-            muldiv64 (ticks, hw->info.bytes_per_second, get_ticks_per_sec ());
-
-        no->old_ticks = now;
-        bytes = audio_MIN (bytes, INT_MAX);
-        samples = bytes >> hw->info.shift;
-        samples = audio_MIN (samples, dead);
+    UInt32 frame, frameCount;
+    float *in = inInputData->mBuffers[0].mData;
+    HWVoiceIn *hw = hwptr;
+    coreaudioVoice *core = CORE_IN(hw);
+    int wpos, avail;
+    struct st_sample *dst;
+#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->bufferFrameSize;
+    avail      = hw->samples - hw->total_samples_captured - core->decr;
+
+    printf("%s: enter avail=%d core.decr=%d core.pos=%d hw.samples=%d hw.total_samples_captured=%d frameCount=%d\n",
+      __FUNCTION__, avail, core->decr, core->pos, hw->samples, hw->total_samples_captured, (int)frameCount);
+
+    /* if there are not enough samples, set signal and return */
+    if (avail < frameCount) {
+        inInputTime = 0;
+        coreaudio_unlock (core, "audioDeviceIOProc(empty)");
+        return 0;
     }
-    return samples;
+
+    wpos = core->pos;
+    dst  = hw->conv_buf + wpos;
+
+    /* fill buffer */
+    for (frame = 0; frame < frameCount; frame++) {
+#ifdef FLOAT_MIXENG
+        dst[frame].l = *in++; /* left channel */
+        dst[frame].r = *in++; /* right channel */
+#else
+#ifdef RECIPROCAL
+        dst[frame].l = *in++ * scale; /* left channel */
+        dst[frame].r = *in++ * scale; /* right channel */
+#else
+        dst[frame].l = *in++ / scale; /* left channel */
+        dst[frame].r = *in++ / scale; /* right channel */
+#endif
+#endif
+    }
+
+    wpos = (wpos + frameCount) % hw->samples;
+    core->decr += frameCount;
+    core->pos   = wpos;
+
+    printf("exit: core.decr=%d core.pos=%d\n", core->decr, core->pos);
+    coreaudio_unlock (core, "audioDeviceIOProc");
+    return 0;
 }
 
-static int no_read (SWVoiceIn *sw, void *buf, int size)
+static int
+coreaudio_read (SWVoiceIn *sw, void *buf, int len)
 {
-    /* use custom code here instead of audio_pcm_sw_read() to avoid
-     * useless resampling/mixing */
-    int samples = size >> sw->info.shift;
-    int total = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
-    int to_clear = audio_MIN (samples, total);
-    sw->total_hw_samples_acquired += total;
-    audio_pcm_info_clear_buf (&sw->info, buf, to_clear);
-    return to_clear << sw->info.shift;
+    int  result = audio_pcm_sw_read (sw, buf, len);
+    printf("%s: audio_pcm_sw_read(%d) returned %d\n", __FUNCTION__, len, result);
+    return result;
 }
 
-static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
+static int
+coreaudio_init_in (HWVoiceIn *hw, struct audsettings *as)
 {
-    (void) hw;
-    (void) cmd;
+    coreaudioVoice*  core = CORE_IN(hw);
+    int              err;
+
+    audio_pcm_init_info (&hw->info, as);
+
+    err = coreaudio_voice_init (core, as, conf.i_buffer_frames, audioInDeviceIOProc, hw, 1);
+    if (err < 0) {
+        return err;
+    }
+
+    hw->samples = core->bufferFrameSize * conf.i_nbuffers;
     return 0;
 }
-#endif
+
+static void
+coreaudio_fini_in (HWVoiceIn *hw)
+{
+    coreaudioVoice*  core = CORE_IN(hw);
+    coreaudio_voice_fini(core);
+}
+
+static int
+coreaudio_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+    coreaudioVoice*  core = CORE_IN(hw);
+
+    return coreaudio_voice_ctl(core, cmd);
+}
+
+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  = "O_BUFFER_SIZE",
+        .tag   = AUD_OPT_INT,
+        .valp  = &conf.o_buffer_frames,
+        .descr = "Size of the output buffer in frames"
+    },
+    {
+        .name  = "O_BUFFER_COUNT",
+        .tag   = AUD_OPT_INT,
+        .valp  = &conf.o_nbuffers,
+        .descr = "Number of output buffers"
+    },
+    {
+        .name  = "I_BUFFER_SIZE",
+        .tag   = AUD_OPT_INT,
+        .valp  = &conf.i_buffer_frames,
+        .descr = "Size of the input buffer in frames"
+    },
+    {
+        .name  = "I_BUFFER_COUNT",
+        .tag   = AUD_OPT_INT,
+        .valp  = &conf.i_nbuffers,
+        .descr = "Number of input buffers"
+    },
+    { /* End of list */ }
+};
 
 static struct audio_pcm_ops coreaudio_pcm_ops = {
-#ifndef CONFIG_MARU
-    .init_out = coreaudio_init_out,
-    .fini_out = coreaudio_fini_out,
-    .run_out  = coreaudio_run_out,
-    .write    = coreaudio_write,
-    .ctl_out  = coreaudio_ctl_out
-#else
     .init_out = coreaudio_init_out,
     .fini_out = coreaudio_fini_out,
     .run_out  = coreaudio_run_out,
     .write    = coreaudio_write,
     .ctl_out  = coreaudio_ctl_out,
 
-    .init_in  = no_init_in,
-    .fini_in  = no_fini_in,
-    .run_in   = no_run_in,
-    .read     = no_read,
-    .ctl_in   = no_ctl_in
-#endif
+    .init_in = coreaudio_init_in,
+    .fini_in = coreaudio_fini_in,
+    .run_in  = coreaudio_run_in,
+    .read    = coreaudio_read,
+    .ctl_in  = coreaudio_ctl_in
 };
 
 struct audio_driver coreaudio_audio_driver = {
@@ -624,15 +764,7 @@ struct audio_driver coreaudio_audio_driver = {
     .pcm_ops        = &coreaudio_pcm_ops,
     .can_be_default = 1,
     .max_voices_out = 1,
-#ifdef CONFIG_MARU
-    .max_voices_in  = 1,
-#else
-    .max_voices_in  = 0,
-#endif
+    .max_voices_in  = 1,   // enable auido-in
     .voice_size_out = sizeof (coreaudioVoiceOut),
-#ifdef CONFIG_MARU
-    .voice_size_in  = sizeof (NoVoiceIn)
-#else
-    .voice_size_in  = 0
-#endif
+    .voice_size_in  = sizeof (coreaudioVoiceIn),
 };