aboutsummaryrefslogtreecommitdiff
path: root/audio/winwaveaudio.c
diff options
context:
space:
mode:
Diffstat (limited to 'audio/winwaveaudio.c')
-rw-r--r--audio/winwaveaudio.c360
1 files changed, 336 insertions, 24 deletions
diff --git a/audio/winwaveaudio.c b/audio/winwaveaudio.c
index bd64a0c4be..cdf483b89a 100644
--- a/audio/winwaveaudio.c
+++ b/audio/winwaveaudio.c
@@ -15,9 +15,13 @@
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 {
@@ -33,6 +37,19 @@ typedef struct {
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";
@@ -73,11 +90,11 @@ static void winwave_log_mmresult (MMRESULT mr)
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 (
@@ -107,7 +124,7 @@ static void winwave_anal_close_out (WaveVoiceOut *wave)
wave->hwo = NULL;
}
-static void CALLBACK winwave_callback (
+static void CALLBACK winwave_callback_out (
HWAVEOUT hwo,
UINT msg,
DWORD_PTR dwInstance,
@@ -130,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 ());
}
}
}
@@ -143,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);
}
}
@@ -159,13 +175,13 @@ 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");
@@ -198,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)", wave->curhdr);
+ winwave_logerr (mr, "waveOutPrepareHeader(%d)", i);
goto err4;
}
}
@@ -238,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) {
@@ -259,10 +275,10 @@ 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)
@@ -287,9 +303,9 @@ static void winwave_fini_out (HWVoiceOut *hw)
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;
}
@@ -319,17 +335,15 @@ 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 {
@@ -356,13 +370,294 @@ static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...)
}
}
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:
+ qemu_free (wave->pcm_buf);
+ err3:
+ qemu_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;
+ }
+
+ qemu_free (wave->pcm_buf);
+ wave->pcm_buf = NULL;
+
+ qemu_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,
+ &nominal_volume);
+
+ 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;
@@ -386,6 +681,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 */ }
};
@@ -394,7 +701,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 = {
@@ -406,7 +718,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)
};