|
|
|
@ -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); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
@ -165,7 +181,7 @@ static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as) |
|
|
|
} |
|
|
|
|
|
|
|
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,8 +335,7 @@ 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", |
|
|
|
dolog ("DAC CreateEvent: %lx, poll mode will be disabled\n", |
|
|
|
GetLastError ()); |
|
|
|
} |
|
|
|
} |
|
|
|
@ -328,8 +343,7 @@ static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...) |
|
|
|
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) |
|
|
|
}; |
|
|
|
|