Windows Waveform Audio driver (no ADC support yet)

Signed-off-by: malc <av1474@comtv.ru>
This commit is contained in:
malc 2009-10-10 01:13:41 +04:00
parent ad323081aa
commit d56316388d
7 changed files with 449 additions and 97 deletions

View File

@ -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))

View File

@ -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);

108
audio/audio_win_int.c Normal file
View File

@ -0,0 +1,108 @@
/* public domain */
#include "qemu-common.h"
#include "audio.h"
#define AUDIO_CAP "win-int"
#include <windows.h>
#include <mmsystem.h>
#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;
}

10
audio/audio_win_int.h Normal file
View File

@ -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 */

View File

@ -37,6 +37,8 @@
#include <objbase.h>
#include <dsound.h>
#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"

312
audio/winwaveaudio.c Normal file
View File

@ -0,0 +1,312 @@
/* public domain */
#include "qemu-common.h"
#include "audio.h"
#define AUDIO_CAP "winwave"
#include "audio_int.h"
#include <windows.h>
#include <mmsystem.h>
#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
};

16
configure vendored
View File

@ -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