diff --git a/Makefile b/Makefile index 8d78dc1d9d..7d4d75c9b7 100644 --- a/Makefile +++ b/Makefile @@ -141,7 +141,9 @@ audio-obj-$(CONFIG_DSOUND) += dsoundaudio.o audio-obj-$(CONFIG_FMOD) += fmodaudio.o audio-obj-$(CONFIG_ESD) += esdaudio.o audio-obj-$(CONFIG_PA) += paaudio.o +audio-obj-$(CONFIG_WINWAVE) += winwaveaudio.o audio-obj-$(CONFIG_AUDIO_PT_INT) += audio_pt_int.o +audio-obj-$(CONFIG_AUDIO_WIN_INT) += audio_win_int.o audio-obj-y += wavcapture.o obj-y += $(addprefix audio/, $(audio-obj-y)) diff --git a/audio/audio_int.h b/audio/audio_int.h index 4acc8d58c4..317c9fc115 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -209,6 +209,7 @@ extern struct audio_driver coreaudio_audio_driver; extern struct audio_driver dsound_audio_driver; extern struct audio_driver esd_audio_driver; extern struct audio_driver pa_audio_driver; +extern struct audio_driver winwave_audio_driver; extern struct mixeng_volume nominal_volume; void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as); diff --git a/audio/audio_win_int.c b/audio/audio_win_int.c new file mode 100644 index 0000000000..58690524c4 --- /dev/null +++ b/audio/audio_win_int.c @@ -0,0 +1,108 @@ +/* public domain */ + +#include "qemu-common.h" +#include "audio.h" + +#define AUDIO_CAP "win-int" +#include +#include + +#include "audio.h" +#include "audio_int.h" +#include "audio_win_int.h" + +int waveformat_from_audio_settings (WAVEFORMATEX *wfx, + struct audsettings *as) +{ + memset (wfx, 0, sizeof (*wfx)); + + wfx->wFormatTag = WAVE_FORMAT_PCM; + wfx->nChannels = as->nchannels; + wfx->nSamplesPerSec = as->freq; + wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2); + wfx->nBlockAlign = 1 << (as->nchannels == 2); + wfx->cbSize = 0; + + switch (as->fmt) { + case AUD_FMT_S8: + case AUD_FMT_U8: + wfx->wBitsPerSample = 8; + break; + + case AUD_FMT_S16: + case AUD_FMT_U16: + wfx->wBitsPerSample = 16; + wfx->nAvgBytesPerSec <<= 1; + wfx->nBlockAlign <<= 1; + break; + + case AUD_FMT_S32: + case AUD_FMT_U32: + wfx->wBitsPerSample = 32; + wfx->nAvgBytesPerSec <<= 2; + wfx->nBlockAlign <<= 2; + break; + + default: + dolog ("Internal logic error: Bad audio format %d\n", as->freq); + return -1; + } + + return 0; +} + +int waveformat_to_audio_settings (WAVEFORMATEX *wfx, + struct audsettings *as) +{ + if (wfx->wFormatTag != WAVE_FORMAT_PCM) { + dolog ("Invalid wave format, tag is not PCM, but %d\n", + wfx->wFormatTag); + return -1; + } + + if (!wfx->nSamplesPerSec) { + dolog ("Invalid wave format, frequency is zero\n"); + return -1; + } + as->freq = wfx->nSamplesPerSec; + + switch (wfx->nChannels) { + case 1: + as->nchannels = 1; + break; + + case 2: + as->nchannels = 2; + break; + + default: + dolog ( + "Invalid wave format, number of channels is not 1 or 2, but %d\n", + wfx->nChannels + ); + return -1; + } + + switch (wfx->wBitsPerSample) { + case 8: + as->fmt = AUD_FMT_U8; + break; + + case 16: + as->fmt = AUD_FMT_S16; + break; + + case 32: + as->fmt = AUD_FMT_S32; + break; + + default: + dolog ("Invalid wave format, bits per sample is not " + "8, 16 or 32, but %d\n", + wfx->wBitsPerSample); + return -1; + } + + return 0; +} + diff --git a/audio/audio_win_int.h b/audio/audio_win_int.h new file mode 100644 index 0000000000..fa5b3cb802 --- /dev/null +++ b/audio/audio_win_int.h @@ -0,0 +1,10 @@ +#ifndef AUDIO_WIN_INT_H +#define AUDIO_WIN_INT_H + +int waveformat_from_audio_settings (WAVEFORMATEX *wfx, + struct audsettings *as); + +int waveformat_to_audio_settings (WAVEFORMATEX *wfx, + struct audsettings *as); + +#endif /* AUDIO_WIN_INT_H */ diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c index 5b255acd02..e5479555fa 100644 --- a/audio/dsoundaudio.c +++ b/audio/dsoundaudio.c @@ -37,6 +37,8 @@ #include #include +#include "audio_win_int.h" + /* #define DEBUG_DSOUND */ static struct { @@ -304,101 +306,6 @@ static int dsound_restore_out (LPDIRECTSOUNDBUFFER dsb) return -1; } -static int waveformat_from_audio_settings (WAVEFORMATEX *wfx, - struct audsettings *as) -{ - memset (wfx, 0, sizeof (*wfx)); - - wfx->wFormatTag = WAVE_FORMAT_PCM; - wfx->nChannels = as->nchannels; - wfx->nSamplesPerSec = as->freq; - wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2); - wfx->nBlockAlign = 1 << (as->nchannels == 2); - wfx->cbSize = 0; - - switch (as->fmt) { - case AUD_FMT_S8: - case AUD_FMT_U8: - wfx->wBitsPerSample = 8; - break; - - case AUD_FMT_S16: - case AUD_FMT_U16: - wfx->wBitsPerSample = 16; - wfx->nAvgBytesPerSec <<= 1; - wfx->nBlockAlign <<= 1; - break; - - case AUD_FMT_S32: - case AUD_FMT_U32: - wfx->wBitsPerSample = 32; - wfx->nAvgBytesPerSec <<= 2; - wfx->nBlockAlign <<= 2; - break; - - default: - dolog ("Internal logic error: Bad audio format %d\n", as->freq); - return -1; - } - - return 0; -} - -static int waveformat_to_audio_settings (WAVEFORMATEX *wfx, - struct audsettings *as) -{ - if (wfx->wFormatTag != WAVE_FORMAT_PCM) { - dolog ("Invalid wave format, tag is not PCM, but %d\n", - wfx->wFormatTag); - return -1; - } - - if (!wfx->nSamplesPerSec) { - dolog ("Invalid wave format, frequency is zero\n"); - return -1; - } - as->freq = wfx->nSamplesPerSec; - - switch (wfx->nChannels) { - case 1: - as->nchannels = 1; - break; - - case 2: - as->nchannels = 2; - break; - - default: - dolog ( - "Invalid wave format, number of channels is not 1 or 2, but %d\n", - wfx->nChannels - ); - return -1; - } - - switch (wfx->wBitsPerSample) { - case 8: - as->fmt = AUD_FMT_U8; - break; - - case 16: - as->fmt = AUD_FMT_S16; - break; - - case 32: - as->fmt = AUD_FMT_S32; - break; - - default: - dolog ("Invalid wave format, bits per sample is not " - "8, 16 or 32, but %d\n", - wfx->wBitsPerSample); - return -1; - } - - return 0; -} - #include "dsound_template.h" #define DSBTYPE_IN #include "dsound_template.h" diff --git a/audio/winwaveaudio.c b/audio/winwaveaudio.c new file mode 100644 index 0000000000..139c7ef470 --- /dev/null +++ b/audio/winwaveaudio.c @@ -0,0 +1,312 @@ +/* public domain */ + +#include "qemu-common.h" +#include "audio.h" + +#define AUDIO_CAP "winwave" +#include "audio_int.h" + +#include +#include + +#include "audio_win_int.h" + +static struct { + int dac_headers; + int dac_samples; +} conf = { + .dac_headers = 4, + .dac_samples = 1024 +}; + +typedef struct { + HWVoiceOut hw; + HWAVEOUT hwo; + WAVEHDR *hdrs; + void *pcm_buf; + int avail; + int pending; + int curhdr; + CRITICAL_SECTION crit_sect; +} WaveVoiceOut; + +static void winwave_log_mmresult (MMRESULT mr) +{ + const char *str = "BUG"; + + switch (mr) { + case MMSYSERR_NOERROR: + str = "Success"; + break; + + case MMSYSERR_INVALHANDLE: + str = "Specified device handle is invalid"; + break; + + case MMSYSERR_BADDEVICEID: + str = "Specified device id is out of range"; + break; + + case MMSYSERR_NODRIVER: + str = "No device driver is present"; + break; + + case MMSYSERR_NOMEM: + str = "Unable to allocate or locl memory"; + break; + + case WAVERR_SYNC: + str = "Device is synchronous but waveOutOpen was called " + "without using the WINWAVE_ALLOWSYNC flag"; + break; + + case WAVERR_UNPREPARED: + str = "The data block pointed to by the pwh parameter " + "hasn't been prepared"; + break; + + default: + AUD_log (AUDIO_CAP, "Reason: Unknown (MMRESULT %#x)\n", mr); + return; + } + + AUD_log (AUDIO_CAP, "Reason: %s\n", str); +} + +static void GCC_FMT_ATTR (2, 3) winwave_logerr ( + MMRESULT mr, + const char *fmt, + ... + ) +{ + va_list ap; + + va_start (ap, fmt); + AUD_vlog (AUDIO_CAP, fmt, ap); + va_end (ap); + + winwave_log_mmresult (mr); +} + +static void winwave_anal_close_out (WaveVoiceOut *wave) +{ + MMRESULT mr; + + mr = waveOutClose (wave->hwo); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveOutClose\n"); + } + wave->hwo = NULL; +} + +static void CALLBACK winwave_callback ( + HWAVEOUT hwo, + UINT msg, + DWORD_PTR dwInstance, + DWORD_PTR dwParam1, + DWORD_PTR dwParam2 + ) +{ + WaveVoiceOut *wave = (WaveVoiceOut *) dwInstance; + + switch (msg) { + case WOM_DONE: + { + WAVEHDR *h = (WAVEHDR *) dwParam1; + if (!h->dwUser) { + h->dwUser = 1; + EnterCriticalSection (&wave->crit_sect); + { + wave->avail += conf.dac_samples; + } + LeaveCriticalSection (&wave->crit_sect); + } + } + break; + + case WOM_CLOSE: + case WOM_OPEN: + break; + + default: + AUD_log (AUDIO_CAP, "unknown wave callback msg %x\n", msg); + } +} + +static int winwave_init_out (HWVoiceOut *hw, struct audsettings *as) +{ + int i; + int err; + MMRESULT mr; + WAVEFORMATEX wfx; + WaveVoiceOut *wave; + + wave = (WaveVoiceOut *) hw; + + InitializeCriticalSection (&wave->crit_sect); + + err = waveformat_from_audio_settings (&wfx, as); + if (err) { + goto err0; + } + + mr = waveOutOpen (&wave->hwo, WAVE_MAPPER, &wfx, + (DWORD_PTR) winwave_callback, + (DWORD_PTR) wave, CALLBACK_FUNCTION); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveOutOpen\n"); + 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.dac_samples * conf.dac_headers; + wave->avail = hw->samples; + + wave->pcm_buf = audio_calloc (AUDIO_FUNC, conf.dac_samples, + conf.dac_headers << hw->info.shift); + if (!wave->pcm_buf) { + goto err3; + } + + for (i = 0; i < conf.dac_headers; ++i) { + WAVEHDR *h = &wave->hdrs[i]; + + h->dwUser = 0; + h->dwBufferLength = conf.dac_samples << hw->info.shift; + h->lpData = advance (wave->pcm_buf, i * h->dwBufferLength); + h->dwFlags = 0; + + mr = waveOutPrepareHeader (wave->hwo, h, sizeof (*h)); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveOutPrepareHeader(%d)\n", wave->curhdr); + goto err4; + } + } + + return 0; + + err4: + qemu_free (wave->pcm_buf); + err3: + qemu_free (wave->hdrs); + err2: + winwave_anal_close_out (wave); + err1: + err0: + return -1; +} + +static int winwave_write (SWVoiceOut *sw, void *buf, int len) +{ + return audio_pcm_sw_write (sw, buf, len); +} + +static int winwave_run_out (HWVoiceOut *hw, int live) +{ + WaveVoiceOut *wave = (WaveVoiceOut *) hw; + int decr; + + EnterCriticalSection (&wave->crit_sect); + { + decr = audio_MIN (live, wave->avail); + decr = audio_pcm_hw_clip_out (hw, wave->pcm_buf, decr, wave->pending); + wave->pending += decr; + wave->avail -= decr; + } + LeaveCriticalSection (&wave->crit_sect); + + while (wave->pending >= conf.dac_samples) { + MMRESULT mr; + WAVEHDR *h = &wave->hdrs[wave->curhdr]; + + h->dwUser = 0; + mr = waveOutWrite (wave->hwo, h, sizeof (*h)); + if (mr != MMSYSERR_NOERROR) { + winwave_logerr (mr, "waveOutWrite(%d)\n", wave->curhdr); + break; + } + + wave->pending -= conf.dac_samples; + wave->curhdr = (wave->curhdr + 1) % conf.dac_headers; + } + return decr; +} + +static void winwave_fini_out (HWVoiceOut *hw) +{ + WaveVoiceOut *wave = (WaveVoiceOut *) hw; + + winwave_anal_close_out (wave); + + qemu_free (wave->pcm_buf); + wave->pcm_buf = NULL; + + qemu_free (wave->hdrs); + wave->hdrs = NULL; +} + +static int winwave_ctl_out (HWVoiceOut *hw, int cmd, ...) +{ + switch (cmd) { + case VOICE_ENABLE: + return 0; + + case VOICE_DISABLE: + return 0; + } + return -1; +} + +static void *winwave_audio_init (void) +{ + return &conf; +} + +static void winwave_audio_fini (void *opaque) +{ + (void) opaque; +} + +static struct audio_option winwave_options[] = { + { + .name = "DAC_HEADERS", + .tag = AUD_OPT_INT, + .valp = &conf.dac_headers, + .descr = "DAC number of headers", + }, + { + .name = "DAC_SAMPLES", + .tag = AUD_OPT_INT, + .valp = &conf.dac_samples, + .descr = "DAC number of samples per header", + }, + { /* End of list */ } +}; + +static struct audio_pcm_ops winwave_pcm_ops = { + .init_out = winwave_init_out, + .fini_out = winwave_fini_out, + .run_out = winwave_run_out, + .write = winwave_write, + .ctl_out = winwave_ctl_out +}; + +struct audio_driver winwave_audio_driver = { + .name = "winwave", + .descr = "Windows Waveform Audio http://msdn.microsoft.com", + .options = winwave_options, + .init = winwave_audio_init, + .fini = winwave_audio_fini, + .pcm_ops = &winwave_pcm_ops, + .can_be_default = 1, + .max_voices_out = INT_MAX, + .max_voices_in = 0, + .voice_size_out = sizeof (WaveVoiceOut), + .voice_size_in = 0 +}; diff --git a/configure b/configure index 39658ff50a..3840e187dd 100755 --- a/configure +++ b/configure @@ -48,6 +48,7 @@ helper_cflags="" libs_softmmu="" libs_tools="" audio_pt_int="" +audio_win_int="" # parse CC options first for opt do @@ -242,11 +243,13 @@ case $targetos in CYGWIN*) mingw32="yes" QEMU_CFLAGS="-mno-cygwin $QEMU_CFLAGS" - audio_possible_drivers="sdl" + audio_possible_drivers="winwave sdl" + audio_drv_list="winwave" ;; MINGW32*) mingw32="yes" - audio_possible_drivers="dsound sdl fmod" + audio_possible_drivers="winwave dsound sdl fmod" + audio_drv_list="winwave" ;; GNU/kFreeBSD) audio_drv_list="oss" @@ -1169,6 +1172,7 @@ for drv in $audio_drv_list; do dsound) libs_softmmu="-lole32 -ldxguid $libs_softmmu" + audio_win_int="yes" ;; oss) @@ -1179,6 +1183,11 @@ for drv in $audio_drv_list; do # XXX: Probes for CoreAudio, DirectSound, SDL(?) ;; + winwave) + libs_softmmu="-lwinmm $libs_softmmu" + audio_win_int="yes" + ;; + *) echo "$audio_possible_drivers" | grep -q "\<$drv\>" || { echo @@ -1885,6 +1894,9 @@ done if test "$audio_pt_int" = "yes" ; then echo "CONFIG_AUDIO_PT_INT=y" >> $config_host_mak fi +if test "$audio_win_int" = "yes" ; then + echo "CONFIG_AUDIO_WIN_INT=y" >> $config_host_mak +fi if test "$mixemu" = "yes" ; then echo "CONFIG_MIXEMU=y" >> $config_host_mak fi