]> git.proxmox.com Git - qemu.git/blobdiff - audio/winwaveaudio.c
Use glib memory allocation and free functions
[qemu.git] / audio / winwaveaudio.c
index 1486da1b958b83434ef75dec0bce65735f21dabe..87e7493270e541011823e759e55c19ba37a5fb3d 100644 (file)
 static struct {
     int dac_headers;
     int dac_samples;
+    int adc_headers;
+    int adc_samples;
 } conf = {
     .dac_headers = 4,
-    .dac_samples = 1024
+    .dac_samples = 1024,
+    .adc_headers = 4,
+    .adc_samples = 1024
 };
 
 typedef struct {
@@ -29,9 +33,23 @@ typedef struct {
     int avail;
     int pending;
     int curhdr;
+    int paused;
     CRITICAL_SECTION crit_sect;
 } WaveVoiceOut;
 
+typedef struct {
+    HWVoiceIn hw;
+    HWAVEIN hwi;
+    WAVEHDR *hdrs;
+    HANDLE event;
+    void *pcm_buf;
+    int curhdr;
+    int paused;
+    int rpos;
+    int avail;
+    CRITICAL_SECTION crit_sect;
+} WaveVoiceIn;
+
 static void winwave_log_mmresult (MMRESULT mr)
 {
     const char *str = "BUG";
@@ -67,12 +85,16 @@ static void winwave_log_mmresult (MMRESULT mr)
             "hasn't been prepared";
         break;
 
+    case WAVERR_STILLPLAYING:
+        str = "There are still buffers in the queue";
+        break;
+
     default:
-        AUD_log (AUDIO_CAP, "Reason: Unknown (MMRESULT %#x)\n", mr);
+        dolog ("Reason: Unknown (MMRESULT %#x)\n", mr);
         return;
     }
 
-    AUD_log (AUDIO_CAP, "Reason: %s\n", str);
+    dolog ("Reason: %s\n", str);
 }
 
 static void GCC_FMT_ATTR (2, 3) winwave_logerr (
@@ -87,6 +109,7 @@ static void GCC_FMT_ATTR (2, 3) winwave_logerr (
     AUD_vlog (AUDIO_CAP, fmt, ap);
     va_end (ap);
 
+    AUD_log (NULL, " failed\n");
     winwave_log_mmresult (mr);
 }
 
@@ -96,12 +119,12 @@ static void winwave_anal_close_out (WaveVoiceOut *wave)
 
     mr = waveOutClose (wave->hwo);
     if (mr != MMSYSERR_NOERROR) {
-        winwave_logerr (mr, "waveOutClose\n");
+        winwave_logerr (mr, "waveOutClose");
     }
     wave->hwo = NULL;
 }
 
-static void CALLBACK winwave_callback (
+static void CALLBACK winwave_callback_out (
     HWAVEOUT hwo,
     UINT msg,
     DWORD_PTR dwInstance,
@@ -124,8 +147,7 @@ static void CALLBACK winwave_callback (
                 LeaveCriticalSection (&wave->crit_sect);
                 if (wave->hw.poll_mode) {
                     if (!SetEvent (wave->event)) {
-                        AUD_log (AUDIO_CAP, "SetEvent failed %lx\n",
-                                 GetLastError ());
+                        dolog ("DAC SetEvent failed %lx\n", GetLastError ());
                     }
                 }
             }
@@ -137,7 +159,7 @@ static void CALLBACK winwave_callback (
         break;
 
     default:
-        AUD_log (AUDIO_CAP, "unknown wave callback msg %x\n", msg);
+        dolog ("unknown wave out callback msg %x\n", msg);
     }
 }
 
@@ -153,16 +175,16 @@ static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as)
 
     InitializeCriticalSection (&wave->crit_sect);
 
-    err =  waveformat_from_audio_settings (&wfx, as);
+    err = waveformat_from_audio_settings (&wfx, as);
     if (err) {
         goto err0;
     }
 
     mr = waveOutOpen (&wave->hwo, WAVE_MAPPER, &wfx,
-                      (DWORD_PTR) winwave_callback,
+                      (DWORD_PTR) winwave_callback_out,
                       (DWORD_PTR) wave, CALLBACK_FUNCTION);
     if (mr != MMSYSERR_NOERROR) {
-        winwave_logerr (mr, "waveOutOpen\n");
+        winwave_logerr (mr, "waveOutOpen");
         goto err1;
     }
 
@@ -192,7 +214,7 @@ static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as)
 
         mr = waveOutPrepareHeader (wave->hwo, h, sizeof (*h));
         if (mr != MMSYSERR_NOERROR) {
-            winwave_logerr (mr, "waveOutPrepareHeader(%d)\n", wave->curhdr);
+            winwave_logerr (mr, "waveOutPrepareHeader(%d)", i);
             goto err4;
         }
     }
@@ -200,9 +222,9 @@ static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as)
     return 0;
 
  err4:
-    qemu_free (wave->pcm_buf);
+    g_free (wave->pcm_buf);
  err3:
-    qemu_free (wave->hdrs);
+    g_free (wave->hdrs);
  err2:
     winwave_anal_close_out (wave);
  err1:
@@ -232,7 +254,7 @@ static int winwave_run_out (HWVoiceOut *hw, int live)
 
     doreset = hw->poll_mode && (wave->pending >= conf.dac_samples);
     if (doreset && !ResetEvent (wave->event)) {
-        AUD_log (AUDIO_CAP, "ResetEvent failed %lx\n", GetLastError ());
+        dolog ("DAC ResetEvent failed %lx\n", GetLastError ());
     }
 
     while (wave->pending >= conf.dac_samples) {
@@ -242,7 +264,7 @@ static int winwave_run_out (HWVoiceOut *hw, int live)
         h->dwUser = 0;
         mr = waveOutWrite (wave->hwo, h, sizeof (*h));
         if (mr != MMSYSERR_NOERROR) {
-            winwave_logerr (mr, "waveOutWrite(%d)\n", wave->curhdr);
+            winwave_logerr (mr, "waveOutWrite(%d)", wave->curhdr);
             break;
         }
 
@@ -253,35 +275,51 @@ static int winwave_run_out (HWVoiceOut *hw, int live)
     return decr;
 }
 
-static void winwave_poll_out (void *opaque)
+static void winwave_poll (void *opaque)
 {
     (void) opaque;
-    audio_run ("winwave_poll_out");
+    audio_run ("winwave_poll");
 }
 
 static void winwave_fini_out (HWVoiceOut *hw)
 {
+    int i;
+    MMRESULT mr;
     WaveVoiceOut *wave = (WaveVoiceOut *) hw;
 
+    mr = waveOutReset (wave->hwo);
+    if (mr != MMSYSERR_NOERROR) {
+        winwave_logerr (mr, "waveOutReset");
+    }
+
+    for (i = 0; i < conf.dac_headers; ++i) {
+        mr = waveOutUnprepareHeader (wave->hwo, &wave->hdrs[i],
+                                     sizeof (wave->hdrs[i]));
+        if (mr != MMSYSERR_NOERROR) {
+            winwave_logerr (mr, "waveOutUnprepareHeader(%d)", i);
+        }
+    }
+
     winwave_anal_close_out (wave);
 
     if (wave->event) {
-        qemu_del_wait_object (wave->event, winwave_poll_out, wave);
+        qemu_del_wait_object (wave->event, winwave_poll, wave);
         if (!CloseHandle (wave->event)) {
-            AUD_log (AUDIO_CAP, "CloseHandle failed %lx\n", GetLastError ());
+            dolog ("DAC CloseHandle failed %lx\n", GetLastError ());
         }
         wave->event = NULL;
     }
 
-    qemu_free (wave->pcm_buf);
+    g_free (wave->pcm_buf);
     wave->pcm_buf = NULL;
 
-    qemu_free (wave->hdrs);
+    g_free (wave->hdrs);
     wave->hdrs = NULL;
 }
 
 static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...)
 {
+    MMRESULT mr;
     WaveVoiceOut *wave = (WaveVoiceOut *) hw;
 
     switch (cmd) {
@@ -297,34 +335,328 @@ static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...)
             if (poll_mode && !wave->event) {
                 wave->event = CreateEvent (NULL, TRUE, TRUE, NULL);
                 if (!wave->event) {
-                    AUD_log (AUDIO_CAP,
-                             "CreateEvent: %lx, poll mode will be disabled\n",
-                             GetLastError ());
+                    dolog ("DAC CreateEvent: %lx, poll mode will be disabled\n",
+                           GetLastError ());
                 }
             }
 
             if (wave->event) {
                 int ret;
 
-                ret = qemu_add_wait_object (wave->event, winwave_poll_out,
-                                            wave);
+                ret = qemu_add_wait_object (wave->event, winwave_poll, wave);
                 hw->poll_mode = (ret == 0);
             }
             else {
                 hw->poll_mode = 0;
             }
+            if (wave->paused) {
+                mr = waveOutRestart (wave->hwo);
+                if (mr != MMSYSERR_NOERROR) {
+                    winwave_logerr (mr, "waveOutRestart");
+                }
+                wave->paused = 0;
+            }
         }
         return 0;
 
     case VOICE_DISABLE:
+        if (!wave->paused) {
+            mr = waveOutPause (wave->hwo);
+            if (mr != MMSYSERR_NOERROR) {
+                winwave_logerr (mr, "waveOutPause");
+            }
+            else {
+                wave->paused = 1;
+            }
+        }
         if (wave->event) {
-            qemu_del_wait_object (wave->event, winwave_poll_out, wave);
+            qemu_del_wait_object (wave->event, winwave_poll, wave);
         }
         return 0;
     }
     return -1;
 }
 
+static void winwave_anal_close_in (WaveVoiceIn *wave)
+{
+    MMRESULT mr;
+
+    mr = waveInClose (wave->hwi);
+    if (mr != MMSYSERR_NOERROR) {
+        winwave_logerr (mr, "waveInClose");
+    }
+    wave->hwi = NULL;
+}
+
+static void CALLBACK winwave_callback_in (
+    HWAVEIN *hwi,
+    UINT msg,
+    DWORD_PTR dwInstance,
+    DWORD_PTR dwParam1,
+    DWORD_PTR dwParam2
+    )
+{
+    WaveVoiceIn *wave = (WaveVoiceIn *) dwInstance;
+
+    switch (msg) {
+    case WIM_DATA:
+        {
+            WAVEHDR *h = (WAVEHDR *) dwParam1;
+            if (!h->dwUser) {
+                h->dwUser = 1;
+                EnterCriticalSection (&wave->crit_sect);
+                {
+                    wave->avail += conf.adc_samples;
+                }
+                LeaveCriticalSection (&wave->crit_sect);
+                if (wave->hw.poll_mode) {
+                    if (!SetEvent (wave->event)) {
+                        dolog ("ADC SetEvent failed %lx\n", GetLastError ());
+                    }
+                }
+            }
+        }
+        break;
+
+    case WIM_CLOSE:
+    case WIM_OPEN:
+        break;
+
+    default:
+        dolog ("unknown wave in callback msg %x\n", msg);
+    }
+}
+
+static void winwave_add_buffers (WaveVoiceIn *wave, int samples)
+{
+    int doreset;
+
+    doreset = wave->hw.poll_mode && (samples >= conf.adc_samples);
+    if (doreset && !ResetEvent (wave->event)) {
+        dolog ("ADC ResetEvent failed %lx\n", GetLastError ());
+    }
+
+    while (samples >= conf.adc_samples) {
+        MMRESULT mr;
+        WAVEHDR *h = &wave->hdrs[wave->curhdr];
+
+        h->dwUser = 0;
+        mr = waveInAddBuffer (wave->hwi, h, sizeof (*h));
+        if (mr != MMSYSERR_NOERROR) {
+            winwave_logerr (mr, "waveInAddBuffer(%d)", wave->curhdr);
+        }
+        wave->curhdr = (wave->curhdr + 1) % conf.adc_headers;
+        samples -= conf.adc_samples;
+    }
+}
+
+static int winwave_init_in (HWVoiceIn *hw, struct audsettings *as)
+{
+    int i;
+    int err;
+    MMRESULT mr;
+    WAVEFORMATEX wfx;
+    WaveVoiceIn *wave;
+
+    wave = (WaveVoiceIn *) hw;
+
+    InitializeCriticalSection (&wave->crit_sect);
+
+    err = waveformat_from_audio_settings (&wfx, as);
+    if (err) {
+        goto err0;
+    }
+
+    mr = waveInOpen (&wave->hwi, WAVE_MAPPER, &wfx,
+                     (DWORD_PTR) winwave_callback_in,
+                     (DWORD_PTR) wave, CALLBACK_FUNCTION);
+    if (mr != MMSYSERR_NOERROR) {
+        winwave_logerr (mr, "waveInOpen");
+        goto err1;
+    }
+
+    wave->hdrs = audio_calloc (AUDIO_FUNC, conf.dac_headers,
+                               sizeof (*wave->hdrs));
+    if (!wave->hdrs) {
+        goto err2;
+    }
+
+    audio_pcm_init_info (&hw->info, as);
+    hw->samples = conf.adc_samples * conf.adc_headers;
+    wave->avail = 0;
+
+    wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.adc_samples,
+                                  conf.adc_headers << hw->info.shift);
+    if (!wave->pcm_buf) {
+        goto err3;
+    }
+
+    for (i = 0; i < conf.adc_headers; ++i) {
+        WAVEHDR *h = &wave->hdrs[i];
+
+        h->dwUser = 0;
+        h->dwBufferLength = conf.adc_samples << hw->info.shift;
+        h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength);
+        h->dwFlags = 0;
+
+        mr = waveInPrepareHeader (wave->hwi, h, sizeof (*h));
+        if (mr != MMSYSERR_NOERROR) {
+            winwave_logerr (mr, "waveInPrepareHeader(%d)", i);
+            goto err4;
+        }
+    }
+
+    wave->paused = 1;
+    winwave_add_buffers (wave, hw->samples);
+    return 0;
+
+ err4:
+    g_free (wave->pcm_buf);
+ err3:
+    g_free (wave->hdrs);
+ err2:
+    winwave_anal_close_in (wave);
+ err1:
+ err0:
+    return -1;
+}
+
+static void winwave_fini_in (HWVoiceIn *hw)
+{
+    int i;
+    MMRESULT mr;
+    WaveVoiceIn *wave = (WaveVoiceIn *) hw;
+
+    mr = waveInReset (wave->hwi);
+    if (mr != MMSYSERR_NOERROR) {
+        winwave_logerr (mr, "waveInReset");
+    }
+
+    for (i = 0; i < conf.adc_headers; ++i) {
+        mr = waveInUnprepareHeader (wave->hwi, &wave->hdrs[i],
+                                     sizeof (wave->hdrs[i]));
+        if (mr != MMSYSERR_NOERROR) {
+            winwave_logerr (mr, "waveInUnprepareHeader(%d)", i);
+        }
+    }
+
+    winwave_anal_close_in (wave);
+
+    if (wave->event) {
+        qemu_del_wait_object (wave->event, winwave_poll, wave);
+        if (!CloseHandle (wave->event)) {
+            dolog ("ADC CloseHandle failed %lx\n", GetLastError ());
+        }
+        wave->event = NULL;
+    }
+
+    g_free (wave->pcm_buf);
+    wave->pcm_buf = NULL;
+
+    g_free (wave->hdrs);
+    wave->hdrs = NULL;
+}
+
+static int winwave_run_in (HWVoiceIn *hw)
+{
+    WaveVoiceIn *wave = (WaveVoiceIn *) hw;
+    int live = audio_pcm_hw_get_live_in (hw);
+    int dead = hw->samples - live;
+    int decr, ret;
+
+    if (!dead) {
+        return 0;
+    }
+
+    EnterCriticalSection (&wave->crit_sect);
+    {
+        decr = audio_MIN (dead, wave->avail);
+        wave->avail -= decr;
+    }
+    LeaveCriticalSection (&wave->crit_sect);
+
+    ret = decr;
+    while (decr) {
+        int left = hw->samples - hw->wpos;
+        int conv = audio_MIN (left, decr);
+        hw->conv (hw->conv_buf + hw->wpos,
+                  advance (wave->pcm_buf, wave->rpos << hw->info.shift),
+                  conv);
+
+        wave->rpos = (wave->rpos + conv) % hw->samples;
+        hw->wpos = (hw->wpos + conv) % hw->samples;
+        decr -= conv;
+    }
+
+    winwave_add_buffers (wave, ret);
+    return ret;
+}
+
+static int winwave_read (SWVoiceIn *sw, void *buf, int size)
+{
+    return audio_pcm_sw_read (sw, buf, size);
+}
+
+static int winwave_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+    MMRESULT mr;
+    WaveVoiceIn *wave = (WaveVoiceIn *) hw;
+
+    switch (cmd) {
+    case VOICE_ENABLE:
+        {
+            va_list ap;
+            int poll_mode;
+
+            va_start (ap, cmd);
+            poll_mode = va_arg (ap, int);
+            va_end (ap);
+
+            if (poll_mode && !wave->event) {
+                wave->event = CreateEvent (NULL, TRUE, TRUE, NULL);
+                if (!wave->event) {
+                    dolog ("ADC CreateEvent: %lx, poll mode will be disabled\n",
+                           GetLastError ());
+                }
+            }
+
+            if (wave->event) {
+                int ret;
+
+                ret = qemu_add_wait_object (wave->event, winwave_poll, wave);
+                hw->poll_mode = (ret == 0);
+            }
+            else {
+                hw->poll_mode = 0;
+            }
+            if (wave->paused) {
+                mr = waveInStart (wave->hwi);
+                if (mr != MMSYSERR_NOERROR) {
+                    winwave_logerr (mr, "waveInStart");
+                }
+                wave->paused = 0;
+            }
+        }
+        return 0;
+
+    case VOICE_DISABLE:
+        if (!wave->paused) {
+            mr = waveInStop (wave->hwi);
+            if (mr != MMSYSERR_NOERROR) {
+                winwave_logerr (mr, "waveInStop");
+            }
+            else {
+                wave->paused = 1;
+            }
+        }
+        if (wave->event) {
+            qemu_del_wait_object (wave->event, winwave_poll, wave);
+        }
+        return 0;
+    }
+    return 0;
+}
+
 static void *winwave_audio_init (void)
 {
     return &conf;
@@ -348,6 +680,18 @@ static struct audio_option winwave_options[] = {
         .valp        = &conf.dac_samples,
         .descr       = "DAC number of samples per header",
     },
+    {
+        .name        = "ADC_HEADERS",
+        .tag         = AUD_OPT_INT,
+        .valp        = &conf.adc_headers,
+        .descr       = "ADC number of headers",
+    },
+    {
+        .name        = "ADC_SAMPLES",
+        .tag         = AUD_OPT_INT,
+        .valp        = &conf.adc_samples,
+        .descr       = "ADC number of samples per header",
+    },
     { /* End of list */ }
 };
 
@@ -356,7 +700,12 @@ static struct audio_pcm_ops winwave_pcm_ops = {
     .fini_out = winwave_fini_out,
     .run_out  = winwave_run_out,
     .write    = winwave_write,
-    .ctl_out  = winwave_ctl_out
+    .ctl_out  = winwave_ctl_out,
+    .init_in  = winwave_init_in,
+    .fini_in  = winwave_fini_in,
+    .run_in   = winwave_run_in,
+    .read     = winwave_read,
+    .ctl_in   = winwave_ctl_in
 };
 
 struct audio_driver winwave_audio_driver = {
@@ -368,7 +717,7 @@ struct audio_driver winwave_audio_driver = {
     .pcm_ops        = &winwave_pcm_ops,
     .can_be_default = 1,
     .max_voices_out = INT_MAX,
-    .max_voices_in  = 0,
+    .max_voices_in  = INT_MAX,
     .voice_size_out = sizeof (WaveVoiceOut),
-    .voice_size_in  = 0
+    .voice_size_in  = sizeof (WaveVoiceIn)
 };