aboutsummaryrefslogtreecommitdiff
path: root/audio
diff options
context:
space:
mode:
Diffstat (limited to 'audio')
-rw-r--r--audio/audio_int.h1
-rw-r--r--audio/audio_win_int.c108
-rw-r--r--audio/audio_win_int.h10
-rw-r--r--audio/dsoundaudio.c97
-rw-r--r--audio/winwaveaudio.c312
5 files changed, 433 insertions, 95 deletions
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 <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;
+}
+
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 <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"
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 <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
+};