coreaudio: implement audio-in
authorSeokyeon Hwang <syeon.hwang@samsung.com>
Wed, 17 Aug 2016 08:56:23 +0000 (17:56 +0900)
committerSeokYeon Hwang <syeon.hwang@samsung.com>
Thu, 18 Aug 2016 02:41:46 +0000 (11:41 +0900)
Change-Id: I7b7c91d24444b7e772846b0a0c64fcf677cad74d
Signed-off-by: Seokyeon Hwang <syeon.hwang@samsung.com>
audio/coreaudio.c

index d4ad224..d9c43fb 100644 (file)
  * THE SOFTWARE.
  */
 
+/*
+ * Implemented audio-in
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ * Contact:
+ * SeokYeon Hwang <syeon.hwang@samsung.com>
+ */
+
 #include "qemu/osdep.h"
 #include <CoreAudio/CoreAudio.h>
 #include <pthread.h>            /* pthread_X */
@@ -43,26 +53,35 @@ typedef struct {
     int nbuffers;
 } CoreaudioConf;
 
-typedef struct coreaudioVoiceOut {
-    HWVoiceOut hw;
+typedef struct coreaudioVoice {
     pthread_mutex_t mutex;
-    AudioDeviceID outputDeviceID;
+    AudioDeviceID deviceID;
     UInt32 audioDevicePropertyBufferFrameSize;
-    AudioStreamBasicDescription outputStreamBasicDescription;
+    AudioStreamBasicDescription description;
     AudioDeviceIOProcID ioprocid;
     int live;
     int decr;
-    int rpos;
+    int pos;
+} coreaudioVoice;
+
+typedef struct coreaudioVoiceOut {
+    HWVoiceOut hw;
+    coreaudioVoice core[1];
 } coreaudioVoiceOut;
 
+typedef struct coreaudioVoiceIn {
+    HWVoiceIn hw;
+    coreaudioVoice core[1];
+} coreaudioVoiceIn;
+
 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
 /* The APIs used here only become available from 10.6 */
 
-static OSStatus coreaudio_get_voice(AudioDeviceID *id)
+static OSStatus coreaudio_get_voice(AudioDeviceID *id, bool isInput)
 {
     UInt32 size = sizeof(*id);
     AudioObjectPropertyAddress addr = {
-        kAudioHardwarePropertyDefaultOutputDevice,
+        isInput ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice,
         kAudioObjectPropertyScopeGlobal,
         kAudioObjectPropertyElementMaster
     };
@@ -76,12 +95,12 @@ static OSStatus coreaudio_get_voice(AudioDeviceID *id)
 }
 
 static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
-                                             AudioValueRange *framerange)
+                                             AudioValueRange *framerange, bool isInput)
 {
     UInt32 size = sizeof(*framerange);
     AudioObjectPropertyAddress addr = {
         kAudioDevicePropertyBufferFrameSizeRange,
-        kAudioDevicePropertyScopeOutput,
+        isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
         kAudioObjectPropertyElementMaster
     };
 
@@ -93,12 +112,12 @@ static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
                                       framerange);
 }
 
-static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
+static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize, bool isInput)
 {
     UInt32 size = sizeof(*framesize);
     AudioObjectPropertyAddress addr = {
         kAudioDevicePropertyBufferFrameSize,
-        kAudioDevicePropertyScopeOutput,
+        isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
         kAudioObjectPropertyElementMaster
     };
 
@@ -110,12 +129,12 @@ static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
                                       framesize);
 }
 
-static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
+static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize, bool isInput)
 {
     UInt32 size = sizeof(*framesize);
     AudioObjectPropertyAddress addr = {
         kAudioDevicePropertyBufferFrameSize,
-        kAudioDevicePropertyScopeOutput,
+        isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
         kAudioObjectPropertyElementMaster
     };
 
@@ -128,12 +147,12 @@ static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
 }
 
 static OSStatus coreaudio_get_streamformat(AudioDeviceID id,
-                                           AudioStreamBasicDescription *d)
+                                           AudioStreamBasicDescription *d, bool isInput)
 {
     UInt32 size = sizeof(*d);
     AudioObjectPropertyAddress addr = {
         kAudioDevicePropertyStreamFormat,
-        kAudioDevicePropertyScopeOutput,
+        isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
         kAudioObjectPropertyElementMaster
     };
 
@@ -146,12 +165,12 @@ static OSStatus coreaudio_get_streamformat(AudioDeviceID id,
 }
 
 static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
-                                           AudioStreamBasicDescription *d)
+                                           AudioStreamBasicDescription *d, bool isInput)
 {
     UInt32 size = sizeof(*d);
     AudioObjectPropertyAddress addr = {
         kAudioDevicePropertyStreamFormat,
-        kAudioDevicePropertyScopeOutput,
+        isInput ? kAudioDevicePropertyScopeInput : kAudioDevicePropertyScopeOutput,
         kAudioObjectPropertyElementMaster
     };
 
@@ -182,44 +201,44 @@ static OSStatus coreaudio_get_isrunning(AudioDeviceID id, UInt32 *result)
 #else
 /* Legacy versions of functions using deprecated APIs */
 
-static OSStatus coreaudio_get_voice(AudioDeviceID *id)
+static OSStatus coreaudio_get_voice(AudioDeviceID *id, bool isInput)
 {
     UInt32 size = sizeof(*id);
 
     return AudioHardwareGetProperty(
-        kAudioHardwarePropertyDefaultOutputDevice,
+        isInput ? kAudioHardwarePropertyDefaultInputDevice : kAudioHardwarePropertyDefaultOutputDevice,
         &size,
         id);
 }
 
 static OSStatus coreaudio_get_framesizerange(AudioDeviceID id,
-                                             AudioValueRange *framerange)
+                                             AudioValueRange *framerange, bool isInput)
 {
     UInt32 size = sizeof(*framerange);
 
     return AudioDeviceGetProperty(
         id,
         0,
-        0,
+        isInput,
         kAudioDevicePropertyBufferFrameSizeRange,
         &size,
         framerange);
 }
 
-static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize)
+static OSStatus coreaudio_get_framesize(AudioDeviceID id, UInt32 *framesize, bool isInput)
 {
     UInt32 size = sizeof(*framesize);
 
     return AudioDeviceGetProperty(
         id,
         0,
-        false,
+        isInput,
         kAudioDevicePropertyBufferFrameSize,
         &size,
         framesize);
 }
 
-static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
+static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize, bool isInput)
 {
     UInt32 size = sizeof(*framesize);
 
@@ -227,28 +246,28 @@ static OSStatus coreaudio_set_framesize(AudioDeviceID id, UInt32 *framesize)
         id,
         NULL,
         0,
-        false,
+        isInput,
         kAudioDevicePropertyBufferFrameSize,
         size,
         framesize);
 }
 
 static OSStatus coreaudio_get_streamformat(AudioDeviceID id,
-                                           AudioStreamBasicDescription *d)
+                                           AudioStreamBasicDescription *d, bool isInput)
 {
     UInt32 size = sizeof(*d);
 
     return AudioDeviceGetProperty(
         id,
         0,
-        false,
+        isInput,
         kAudioDevicePropertyStreamFormat,
         &size,
         d);
 }
 
 static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
-                                           AudioStreamBasicDescription *d)
+                                           AudioStreamBasicDescription *d, bool isInput)
 {
     UInt32 size = sizeof(*d);
 
@@ -256,7 +275,7 @@ static OSStatus coreaudio_set_streamformat(AudioDeviceID id,
         id,
         0,
         0,
-        0,
+        isInput,
         kAudioDevicePropertyStreamFormat,
         size,
         d);
@@ -366,11 +385,11 @@ static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
     coreaudio_logstatus (status);
 }
 
-static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
+static inline UInt32 isPlaying (AudioDeviceID deviceID)
 {
     OSStatus status;
     UInt32 result = 0;
-    status = coreaudio_get_isrunning(outputDeviceID, &result);
+    status = coreaudio_get_isrunning(deviceID, &result);
     if (status != kAudioHardwareNoError) {
         coreaudio_logerr(status,
                          "Could not determine whether Device is playing\n");
@@ -383,7 +402,7 @@ static void coreaudio_atexit (void)
     isAtexit = 1;
 }
 
-static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
+static int coreaudio_lock (coreaudioVoice *core, const char *fn_name)
 {
     int err;
 
@@ -396,7 +415,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;
 
@@ -409,112 +428,13 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
     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,
-                              void *drv_opaque)
+static int coreaudio_init_voice (coreaudioVoice *core, void *hw, struct audsettings *as,
+                              CoreaudioConf *conf, AudioDeviceIOProc inProc, bool isInput)
 {
     OSStatus status;
-    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
     int err;
-    const char *typ = "playback";
+    const char *typ = isInput ? "capture" : "playback";
     AudioValueRange frameRange;
-    CoreaudioConf *conf = drv_opaque;
 
     /* create mutex */
     err = pthread_mutex_init(&core->mutex, NULL);
@@ -523,22 +443,20 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
         return -1;
     }
 
-    audio_pcm_init_info (&hw->info, as);
-
-    status = coreaudio_get_voice(&core->outputDeviceID);
+    status = coreaudio_get_voice(&core->deviceID, isInput);
     if (status != kAudioHardwareNoError) {
         coreaudio_logerr2 (status, typ,
-                           "Could not get default output Device\n");
+                           "Could not get default device\n");
         return -1;
     }
-    if (core->outputDeviceID == kAudioDeviceUnknown) {
+    if (core->deviceID == kAudioDeviceUnknown) {
         dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
         return -1;
     }
 
     /* get minimum and maximum buffer frame sizes */
-    status = coreaudio_get_framesizerange(core->outputDeviceID,
-                                          &frameRange);
+    status = coreaudio_get_framesizerange(core->deviceID,
+                                          &frameRange, isInput);
     if (status != kAudioHardwareNoError) {
         coreaudio_logerr2 (status, typ,
                            "Could not get device buffer frame range\n");
@@ -558,8 +476,8 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
     }
 
     /* set Buffer Frame Size */
-    status = coreaudio_set_framesize(core->outputDeviceID,
-                                     &core->audioDevicePropertyBufferFrameSize);
+    status = coreaudio_set_framesize(core->deviceID,
+                                     &core->audioDevicePropertyBufferFrameSize, isInput);
     if (status != kAudioHardwareNoError) {
         coreaudio_logerr2 (status, typ,
                            "Could not set device buffer frame size %" PRIu32 "\n",
@@ -568,55 +486,54 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
     }
 
     /* get Buffer Frame Size */
-    status = coreaudio_get_framesize(core->outputDeviceID,
-                                     &core->audioDevicePropertyBufferFrameSize);
+    status = coreaudio_get_framesize(core->deviceID,
+                                     &core->audioDevicePropertyBufferFrameSize, isInput);
     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 */
-    status = coreaudio_get_streamformat(core->outputDeviceID,
-                                        &core->outputStreamBasicDescription);
+    status = coreaudio_get_streamformat(core->deviceID,
+                                        &core->description, isInput);
     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;
-    status = coreaudio_set_streamformat(core->outputDeviceID,
-                                        &core->outputStreamBasicDescription);
+    core->description.mSampleRate = (Float64) as->freq;
+    status = coreaudio_set_streamformat(core->deviceID,
+                                        &core->description, isInput);
     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 */
     core->ioprocid = NULL;
-    status = AudioDeviceCreateIOProcID(core->outputDeviceID,
-                                       audioDeviceIOProc,
+    status = AudioDeviceCreateIOProcID(core->deviceID,
+                                       inProc,
                                        hw,
                                        &core->ioprocid);
     if (status != kAudioHardwareNoError || core->ioprocid == NULL) {
         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, core->ioprocid);
+    /* start Playback/Capture */
+    if (!isPlaying(core->deviceID)) {
+        status = AudioDeviceStart(core->deviceID, core->ioprocid);
         if (status != kAudioHardwareNoError) {
-            coreaudio_logerr2 (status, typ, "Could not start playback\n");
-            AudioDeviceDestroyIOProcID(core->outputDeviceID, core->ioprocid);
-            core->outputDeviceID = kAudioDeviceUnknown;
+            coreaudio_logerr2 (status, typ, "Could not start %s\n", typ);
+            AudioDeviceDestroyIOProcID(core->deviceID, core->ioprocid);
+            core->deviceID = kAudioDeviceUnknown;
             return -1;
         }
     }
@@ -624,29 +541,29 @@ static int coreaudio_init_out(HWVoiceOut *hw, struct audsettings *as,
     return 0;
 }
 
-static void coreaudio_fini_out (HWVoiceOut *hw)
+static void coreaudio_fini_voice (coreaudioVoice *core, bool isInput)
 {
     OSStatus status;
+    const char *typ = isInput ? "capture" : "playback";
     int err;
-    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
 
     if (!isAtexit) {
-        /* stop playback */
-        if (isPlaying(core->outputDeviceID)) {
-            status = AudioDeviceStop(core->outputDeviceID, core->ioprocid);
+        /* stop playback/capture */
+        if (isPlaying(core->deviceID)) {
+            status = AudioDeviceStop(core->deviceID, core->ioprocid);
             if (status != kAudioHardwareNoError) {
-                coreaudio_logerr (status, "Could not stop playback\n");
+                coreaudio_logerr (status, "Could not stop %s\n", typ);
             }
         }
 
-        /* remove callback */
-        status = AudioDeviceDestroyIOProcID(core->outputDeviceID,
+        /* remove callback/capture */
+        status = AudioDeviceDestroyIOProcID(core->deviceID,
                                             core->ioprocid);
         if (status != kAudioHardwareNoError) {
             coreaudio_logerr (status, "Could not remove IOProc\n");
         }
     }
-    core->outputDeviceID = kAudioDeviceUnknown;
+    core->deviceID = kAudioDeviceUnknown;
 
     /* destroy mutex */
     err = pthread_mutex_destroy(&core->mutex);
@@ -655,30 +572,30 @@ static void coreaudio_fini_out (HWVoiceOut *hw)
     }
 }
 
-static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
+static int coreaudio_ctl_voice (coreaudioVoice *core, int cmd, bool isInput)
 {
     OSStatus status;
-    coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+    const char *typ = isInput ? "capture" : "playback";
 
     switch (cmd) {
     case VOICE_ENABLE:
-        /* start playback */
-        if (!isPlaying(core->outputDeviceID)) {
-            status = AudioDeviceStart(core->outputDeviceID, core->ioprocid);
+        /* start playback/capture */
+        if (!isPlaying(core->deviceID)) {
+            status = AudioDeviceStart(core->deviceID, core->ioprocid);
             if (status != kAudioHardwareNoError) {
-                coreaudio_logerr (status, "Could not resume playback\n");
+                coreaudio_logerr (status, "Could not resume %s\n", typ);
             }
         }
         break;
 
     case VOICE_DISABLE:
-        /* stop playback */
+        /* stop playback/capture */
         if (!isAtexit) {
-            if (isPlaying(core->outputDeviceID)) {
-                status = AudioDeviceStop(core->outputDeviceID,
+            if (isPlaying(core->deviceID)) {
+                status = AudioDeviceStop(core->deviceID,
                                          core->ioprocid);
                 if (status != kAudioHardwareNoError) {
-                    coreaudio_logerr (status, "Could not pause playback\n");
+                    coreaudio_logerr (status, "Could not pause %s\n", typ);
                 }
             }
         }
@@ -687,6 +604,252 @@ static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
     return 0;
 }
 
+static int coreaudio_run_out (HWVoiceOut *hw, int live)
+{
+    int decr;
+    coreaudioVoice *core = ((coreaudioVoiceOut *) hw)->core;
+
+    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->pos;
+
+    coreaudio_unlock (core, "coreaudio_run_out");
+    return decr;
+}
+
+/* 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)
+{
+    UInt32 frame, frameCount;
+    float *out = outOutputData->mBuffers[0].mData;
+    HWVoiceOut *hw = hwptr;
+    coreaudioVoice *core = ((coreaudioVoiceOut *) hw)->core;
+    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->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 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,
+                              void *drv_opaque)
+{
+    coreaudioVoice *core = ((coreaudioVoiceOut *) hw)->core;
+    CoreaudioConf *conf = drv_opaque;
+
+    if (coreaudio_init_voice(core, hw, as, conf, audioOutDeviceIOProc, false) < 0) {
+        return -1;
+    }
+
+    audio_pcm_init_info (&hw->info, as);
+    hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
+
+    return 0;
+}
+
+static void coreaudio_fini_out (HWVoiceOut *hw)
+{
+    coreaudioVoice *core = ((coreaudioVoiceOut *) hw)->core;
+
+    coreaudio_fini_voice(core, false);
+}
+
+static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+    coreaudioVoice *core = ((coreaudioVoiceOut *) hw)->core;
+
+    return coreaudio_ctl_voice (core, cmd, false);
+}
+
+static int coreaudio_run_in (HWVoiceIn *hw)
+{
+    int decr;
+    coreaudioVoice *core = ((coreaudioVoiceIn *) hw)->core;
+
+    if (coreaudio_lock (core, "coreaudio_run_in")) {
+        return 0;
+    }
+
+    decr        = core->decr;
+    core->decr -= decr;
+    hw->wpos    = core->pos;
+
+    coreaudio_unlock (core, "coreaudio_run_in");
+    return decr;
+}
+
+/* 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)
+{
+    UInt32 frame, frameCount;
+    float *in = inInputData->mBuffers[0].mData;
+    HWVoiceIn *hw = hwptr;
+    coreaudioVoice *core = ((coreaudioVoiceIn *) hw)->core;
+    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->audioDevicePropertyBufferFrameSize;
+    avail = hw->samples - hw->total_samples_captured - core->decr;
+
+    /* if there are not enough samples, set signal and return */
+    if (avail < frameCount) {
+        inInputTime = 0;
+        coreaudio_unlock (core, "audioDeviceIOProc(empty)");
+        return 0;
+    }
+
+    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;
+
+
+    coreaudio_unlock (core, "audioDeviceIOProc");
+    return 0;
+}
+
+static int coreaudio_read (SWVoiceIn *sw, void *buf, int len)
+{
+    return audio_pcm_sw_read (sw, buf, len);
+}
+
+static int coreaudio_init_in(HWVoiceIn *hw, struct audsettings *as,
+                              void *drv_opaque)
+{
+    coreaudioVoice *core = ((coreaudioVoiceIn *) hw)->core;
+    CoreaudioConf *conf = drv_opaque;
+
+    if (coreaudio_init_voice(core, hw, as, conf, audioInDeviceIOProc, true) < 0) {
+        return -1;
+    }
+
+    audio_pcm_init_info (&hw->info, as);
+    hw->samples = conf->nbuffers * core->audioDevicePropertyBufferFrameSize;
+
+    return 0;
+}
+
+static void coreaudio_fini_in (HWVoiceIn *hw)
+{
+    coreaudioVoice *core = ((coreaudioVoiceIn *) hw)->core;
+
+    coreaudio_fini_voice(core, true);
+}
+
+static int coreaudio_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+    coreaudioVoice *core = ((coreaudioVoiceIn *) hw)->core;
+
+    return coreaudio_ctl_voice (core, cmd, true);
+}
+
 static CoreaudioConf glob_conf = {
     .buffer_frames = 512,
     .nbuffers = 4,
@@ -727,7 +890,13 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
     .fini_out = coreaudio_fini_out,
     .run_out  = coreaudio_run_out,
     .write    = coreaudio_write,
-    .ctl_out  = coreaudio_ctl_out
+    .ctl_out  = coreaudio_ctl_out,
+
+    .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 = {
@@ -739,7 +908,7 @@ struct audio_driver coreaudio_audio_driver = {
     .pcm_ops        = &coreaudio_pcm_ops,
     .can_be_default = 1,
     .max_voices_out = 1,
-    .max_voices_in  = 0,
+    .max_voices_in  = 1,
     .voice_size_out = sizeof (coreaudioVoiceOut),
-    .voice_size_in  = 0
+    .voice_size_in  = sizeof (coreaudioVoiceIn),
 };