diff options
Diffstat (limited to 'audio/winwaveaudio.c')
-rw-r--r-- | audio/winwaveaudio.c | 360 |
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) }; |