* [Qemu-devel] [PATCH] Add WinWave-based audio backend to Windows QEMU builds
@ 2009-06-11 0:15 David Turner
2009-06-11 0:42 ` Anthony Liguori
0 siblings, 1 reply; 5+ messages in thread
From: David Turner @ 2009-06-11 0:15 UTC (permalink / raw)
To: qemu-devel, malc
[-- Attachment #1.1: Type: text/plain, Size: 115 bytes --]
Here's a patch that provides a Windows audio backend with both playback and
record capabilities, named "winaudio".
[-- Attachment #1.2: Type: text/html, Size: 144 bytes --]
[-- Attachment #2: 0001-Add-a-Windows-Wave-audio-backend-to-QEMU.patch --]
[-- Type: application/octet-stream, Size: 22684 bytes --]
From 4e061a312b5f6575462e3fa50c7df20788e5f95c Mon Sep 17 00:00:00 2001
From: digit <digit@google.com>
Date: Wed, 10 Jun 2009 14:11:28 +0200
Subject: [PATCH] Add a Windows Wave audio backend to QEMU.
This backend provides both playback and record. It doesn't depend
on any external library like SDL or FMOD, or any non-distributable
headers like the DirectSound ones.
For this reason, it is made the default for Cygwin and Mingw builds
Tested on both Cygwin and MSys builds.
Signed-off-by: David Turner <digit@google.com>
---
Makefile | 3 +
audio/audio_int.h | 1 +
audio/winaudio.c | 663 +++++++++++++++++++++++++++++++++++++++++++++++++++++
configure | 8 +-
4 files changed, 672 insertions(+), 3 deletions(-)
create mode 100644 audio/winaudio.c
diff --git a/Makefile b/Makefile
index 3177616..8f7894a 100644
--- a/Makefile
+++ b/Makefile
@@ -137,6 +137,9 @@ endif
ifdef CONFIG_DSOUND
AUDIO_OBJS += dsoundaudio.o
endif
+ifdef CONFIG_WINAUDIO
+AUDIO_OBJS += winaudio.o
+endif
ifdef CONFIG_FMOD
AUDIO_OBJS += fmodaudio.o
audio/audio.o audio/fmodaudio.o: CPPFLAGS := -I$(CONFIG_FMOD_INC) $(CPPFLAGS)
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 0abed38..f2c08ac 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -207,6 +207,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 winaudio_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/winaudio.c b/audio/winaudio.c
new file mode 100644
index 0000000..5000f7b
--- /dev/null
+++ b/audio/winaudio.c
@@ -0,0 +1,663 @@
+/*
+ * QEMU "simple" Windows audio driver
+ *
+ * Copyright (c) 2007 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <windows.h>
+#include <mmsystem.h>
+
+#include "qemu-common.h"
+#include "audio.h"
+
+#define AUDIO_CAP "winaudio"
+#include "audio_int.h"
+
+/* define DEBUG to 1 to dump audio debugging info at runtime to stderr */
+#define DEBUG 0
+
+/* define DEBUG_TIMING to 1 to dump timing debugging messages */
+#define DEBUG_TIMING 0
+
+#if DEBUG
+# define D(...) printf(__VA_ARGS__)
+#else
+# define D(...) ((void)0)
+#endif
+
+static struct {
+ int nb_samples;
+} conf = {
+ 1024
+};
+
+#if DEBUG_TIMING
+int64_t start_time;
+int64_t last_time;
+#endif
+
+/* XP works well with 4, but Vista struggles with anything less than 8 */
+#define NUM_OUT_BUFFERS 8 /* must be at least 2 */
+
+/** COMMON UTILITIES
+ **/
+
+#if DEBUG
+static void
+dump_mmerror( const char* func, MMRESULT error )
+{
+ const char* reason = NULL;
+
+ fprintf(stderr, "%s returned error: ", func);
+ switch (error) {
+ case MMSYSERR_ALLOCATED: reason="specified resource is already allocated"; break;
+ case MMSYSERR_BADDEVICEID: reason="bad device id"; break;
+ case MMSYSERR_NODRIVER: reason="no driver is present"; break;
+ case MMSYSERR_NOMEM: reason="unable to allocate or lock memory"; break;
+ case WAVERR_BADFORMAT: reason="unsupported waveform-audio format"; break;
+ case WAVERR_SYNC: reason="device is synchronous"; break;
+ default:
+ fprintf(stderr, "unknown(%d)\n", error);
+ }
+ if (reason)
+ fprintf(stderr, "%s\n", reason);
+}
+#else
+# define dump_mmerror(func,error) ((void)0)
+#endif
+
+
+/** AUDIO OUT
+ **/
+
+typedef struct WinAudioOut {
+ HWVoiceOut hw;
+ HWAVEOUT waveout;
+ int silence;
+ CRITICAL_SECTION lock;
+ unsigned char* buffer_bytes;
+ WAVEHDR buffers[ NUM_OUT_BUFFERS ];
+ int write_index; /* starting first writable buffer */
+ int write_count; /* available writable buffers count */
+ int write_pos; /* position in current writable buffer */
+ int write_size; /* size in bytes of each buffer */
+} WinAudioOut;
+
+/* The callback that is called when Windows has finished playing a buffer */
+static void CALLBACK
+winaudio_out_buffer_done (HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
+ DWORD dwParam1, DWORD dwParam2)
+{
+ WinAudioOut* s = (WinAudioOut*) dwInstance;
+
+ /* Only service "buffer done playing" messages */
+ if ( uMsg != WOM_DONE )
+ return;
+
+ /* Signal that we are done playing a buffer */
+ EnterCriticalSection( &s->lock );
+ if (s->write_count < NUM_OUT_BUFFERS)
+ s->write_count += 1;
+ LeaveCriticalSection( &s->lock );
+}
+
+static int
+winaudio_out_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static void
+winaudio_out_fini (HWVoiceOut *hw)
+{
+ WinAudioOut* s = (WinAudioOut*) hw;
+ int i;
+
+ if (s->waveout) {
+ waveOutReset(s->waveout);
+ s->waveout = 0;
+ }
+
+ for ( i=0; i<NUM_OUT_BUFFERS; ++i ) {
+ if ( s->buffers[i].dwUser != 0xFFFF ) {
+ waveOutUnprepareHeader(
+ s->waveout, &s->buffers[i], sizeof(s->buffers[i]) );
+ s->buffers[i].dwUser = 0xFFFF;
+ }
+ }
+
+ if (s->buffer_bytes != NULL) {
+ qemu_free(s->buffer_bytes);
+ s->buffer_bytes = NULL;
+ }
+
+ if (s->waveout) {
+ waveOutClose(s->waveout);
+ s->waveout = NULL;
+ }
+}
+
+
+static int
+winaudio_out_init (HWVoiceOut *hw, struct audsettings *as)
+{
+ WinAudioOut* s = (WinAudioOut*) hw;
+ MMRESULT result;
+ WAVEFORMATEX format;
+ int shift, i, samples_size;
+
+ s->waveout = NULL;
+ InitializeCriticalSection( &s->lock );
+ for (i = 0; i < NUM_OUT_BUFFERS; i++) {
+ s->buffers[i].dwUser = 0xFFFF;
+ }
+ s->buffer_bytes = NULL;
+
+ /* compute desired wave output format */
+ format.wFormatTag = WAVE_FORMAT_PCM;
+ format.nChannels = as->nchannels;
+ format.nSamplesPerSec = as->freq;
+ format.nAvgBytesPerSec = as->freq*as->nchannels;
+
+ s->silence = 0;
+
+ switch (as->fmt) {
+ case AUD_FMT_S8: shift = 0; break;
+ case AUD_FMT_U8: shift = 0; s->silence = 0x80; break;
+ case AUD_FMT_S16: shift = 1; break;
+ case AUD_FMT_U16: shift = 1; s->silence = 0x8000; break;
+ case AUD_FMT_S32: shift = 2; break;
+ case AUD_FMT_U32: shift = 2; s->silence = 0x80000000; break;
+ default:
+ fprintf(stderr, "qemu: winaudio: Bad output audio format: %d\n",
+ as->fmt);
+ return -1;
+ }
+
+ format.nAvgBytesPerSec = (format.nSamplesPerSec & format.nChannels) << shift;
+ format.nBlockAlign = format.nChannels << shift;
+ format.wBitsPerSample = 8 << shift;
+ format.cbSize = 0;
+
+ /* open the wave out device */
+ result = waveOutOpen( &s->waveout, WAVE_MAPPER, &format,
+ (DWORD_PTR) winaudio_out_buffer_done,
+ (DWORD_PTR) hw, CALLBACK_FUNCTION);
+ if ( result != MMSYSERR_NOERROR ) {
+ dump_mmerror( "qemu: winaudio: waveOutOpen()", result);
+ return -1;
+ }
+
+ samples_size = format.nBlockAlign * conf.nb_samples;
+ s->buffer_bytes = qemu_malloc( NUM_OUT_BUFFERS * samples_size );
+ if (s->buffer_bytes == NULL) {
+ waveOutClose( s->waveout );
+ s->waveout = NULL;
+ fprintf(stderr, "not enough memory for Windows audio buffers\n");
+ return -1;
+ }
+
+ for (i = 0; i < NUM_OUT_BUFFERS; i++) {
+ memset( &s->buffers[i], 0, sizeof(s->buffers[i]) );
+ s->buffers[i].lpData = (LPSTR)(s->buffer_bytes + i*samples_size);
+ s->buffers[i].dwBufferLength = samples_size;
+ s->buffers[i].dwFlags = WHDR_DONE;
+
+ result = waveOutPrepareHeader( s->waveout, &s->buffers[i],
+ sizeof(s->buffers[i]) );
+ if ( result != MMSYSERR_NOERROR ) {
+ dump_mmerror("waveOutPrepareHeader()", result);
+ return -1;
+ }
+ }
+
+#if DEBUG
+ /* Check the sound device we retrieved */
+ {
+ WAVEOUTCAPS caps;
+
+ result = waveOutGetDevCaps((UINT) s->waveout, &caps, sizeof(caps));
+ if ( result != MMSYSERR_NOERROR ) {
+ dump_mmerror("waveOutGetDevCaps()", result);
+ } else
+ printf("Audio out device: %s\n", caps.szPname);
+ }
+#endif
+
+ audio_pcm_init_info (&hw->info, as);
+ hw->samples = conf.nb_samples*2;
+
+ s->write_index = 0;
+ s->write_count = NUM_OUT_BUFFERS;
+ s->write_pos = 0;
+ s->write_size = samples_size;
+ return 0;
+}
+
+
+static int
+winaudio_out_run (HWVoiceOut *hw)
+{
+ WinAudioOut* s = (WinAudioOut*) hw;
+ int played = 0;
+ int has_buffer;
+ int live = audio_pcm_hw_get_live_out (hw);
+
+ if (!live) {
+ return 0;
+ }
+
+ EnterCriticalSection( &s->lock );
+ has_buffer = (s->write_count > 0);
+ LeaveCriticalSection( &s->lock );
+
+ if (!has_buffer)
+ return 0;
+
+ while (live > 0) {
+ WAVEHDR* wav_buffer = s->buffers + s->write_index;
+ int wav_bytes = (s->write_size - s->write_pos);
+ int wav_samples = audio_MIN(wav_bytes >> hw->info.shift, live);
+ int hw_samples = audio_MIN(hw->samples - hw->rpos, live);
+ struct st_sample* src = hw->mix_buf + hw->rpos;
+ uint8_t* dst = (uint8_t*)wav_buffer->lpData + s->write_pos;
+
+ if (wav_samples > hw_samples) {
+ wav_samples = hw_samples;
+ }
+
+ wav_bytes = wav_samples << hw->info.shift;
+
+ //D("run_out: buffer:%d pos:%d size:%d wsamples:%d wbytes:%d live:%d rpos:%d hwsamples:%d\n", s->write_index,
+ // s->write_pos, s->write_size, wav_samples, wav_bytes, live, hw->rpos, hw->samples);
+ hw->clip (dst, src, wav_samples);
+ hw->rpos += wav_samples;
+ if (hw->rpos >= hw->samples)
+ hw->rpos -= hw->samples;
+
+ live -= wav_samples;
+ played += wav_samples;
+ s->write_pos += wav_bytes;
+ if (s->write_pos == s->write_size) {
+#if DEBUG_TIMING
+ int64_t now = qemu_get_clock(vm_clock) - start_time;
+ int64_t diff = now - last_time;
+
+ D("run_out: (%7.3f:%7d):waveOutWrite buffer:%d\n",
+ now/1e9, (now-last_time)/1e9, s->write_index);
+ last_time = now;
+#endif
+ waveOutWrite( s->waveout, wav_buffer, sizeof(*wav_buffer) );
+ s->write_pos = 0;
+ s->write_index += 1;
+ if (s->write_index == NUM_OUT_BUFFERS)
+ s->write_index = 0;
+
+ EnterCriticalSection( &s->lock );
+ if (--s->write_count == 0) {
+ live = 0;
+ }
+ LeaveCriticalSection( &s->lock );
+ }
+ }
+ return played;
+}
+
+static int
+winaudio_out_ctl (HWVoiceOut *hw, int cmd, ...)
+{
+ WinAudioOut* s = (WinAudioOut*) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ waveOutRestart( s->waveout );
+ break;
+
+ case VOICE_DISABLE:
+ waveOutPause( s->waveout );
+ break;
+ }
+ return 0;
+}
+
+/** AUDIO IN
+ **/
+
+#define NUM_IN_BUFFERS 2
+
+typedef struct WinAudioIn {
+ HWVoiceIn hw;
+ HWAVEIN wavein;
+ CRITICAL_SECTION lock;
+ unsigned char* buffer_bytes;
+ WAVEHDR buffers[ NUM_IN_BUFFERS ];
+ int read_index;
+ int read_count;
+ int read_pos;
+ int read_size;
+} WinAudioIn;
+
+/* The callback that is called when Windows has finished filling a buffer */
+static void CALLBACK
+winaudio_in_buffer_done (HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance,
+ DWORD dwParam1, DWORD dwParam2)
+{
+ WinAudioIn* s = (WinAudioIn*) dwInstance;
+
+ /* Only service "buffer filled" messages */
+ if ( uMsg != WIM_DATA )
+ return;
+
+ /* Signal that we are done playing a buffer */
+ EnterCriticalSection( &s->lock );
+ if (s->read_count < NUM_IN_BUFFERS)
+ s->read_count += 1;
+ LeaveCriticalSection( &s->lock );
+}
+
+static void
+winaudio_in_fini (HWVoiceIn *hw)
+{
+ WinAudioIn* s = (WinAudioIn*) hw;
+ int i;
+
+ if (s->wavein) {
+ waveInReset(s->wavein);
+ s->wavein = 0;
+ }
+
+ for ( i=0; i<NUM_OUT_BUFFERS; ++i ) {
+ if ( s->buffers[i].dwUser != 0xFFFF ) {
+ waveInUnprepareHeader(
+ s->wavein, &s->buffers[i], sizeof(s->buffers[i]) );
+ s->buffers[i].dwUser = 0xFFFF;
+ }
+ }
+
+ if (s->buffer_bytes != NULL) {
+ qemu_free(s->buffer_bytes);
+ s->buffer_bytes = NULL;
+ }
+
+ if (s->wavein) {
+ waveInClose(s->wavein);
+ s->wavein = NULL;
+ }
+}
+
+
+static int
+winaudio_in_init (HWVoiceIn *hw, struct audsettings *as)
+{
+ WinAudioIn* s = (WinAudioIn*) hw;
+ MMRESULT result;
+ WAVEFORMATEX format;
+ int shift, i, samples_size;
+
+ s->wavein = NULL;
+ InitializeCriticalSection( &s->lock );
+ for (i = 0; i < NUM_OUT_BUFFERS; i++) {
+ s->buffers[i].dwUser = 0xFFFF;
+ }
+ s->buffer_bytes = NULL;
+
+ /* compute desired wave input format */
+ format.wFormatTag = WAVE_FORMAT_PCM;
+ format.nChannels = as->nchannels;
+ format.nSamplesPerSec = as->freq;
+ format.nAvgBytesPerSec = as->freq*as->nchannels;
+
+ switch (as->fmt) {
+ case AUD_FMT_S8: shift = 0; break;
+ case AUD_FMT_U8: shift = 0; break;
+ case AUD_FMT_S16: shift = 1; break;
+ case AUD_FMT_U16: shift = 1; break;
+ case AUD_FMT_S32: shift = 2; break;
+ case AUD_FMT_U32: shift = 2; break;
+ default:
+ fprintf(stderr, "qemu: winaudio: Bad input audio format: %d\n",
+ as->fmt);
+ return -1;
+ }
+
+ format.nAvgBytesPerSec = (format.nSamplesPerSec * format.nChannels) << shift;
+ format.nBlockAlign = format.nChannels << shift;
+ format.wBitsPerSample = 8 << shift;
+ format.cbSize = 0;
+
+ /* open the wave in device */
+ result = waveInOpen( &s->wavein, WAVE_MAPPER, &format,
+ (DWORD_PTR)winaudio_in_buffer_done, (DWORD_PTR) hw,
+ CALLBACK_FUNCTION);
+ if ( result != MMSYSERR_NOERROR ) {
+ dump_mmerror( "qemu: winaudio: waveInOpen()", result);
+ return -1;
+ }
+
+ samples_size = format.nBlockAlign * conf.nb_samples;
+ s->buffer_bytes = qemu_malloc( NUM_IN_BUFFERS * samples_size );
+ if (s->buffer_bytes == NULL) {
+ waveInClose( s->wavein );
+ s->wavein = NULL;
+ fprintf(stderr, "not enough memory for Windows audio buffers\n");
+ return -1;
+ }
+
+ for (i = 0; i < NUM_IN_BUFFERS; i++) {
+ memset( &s->buffers[i], 0, sizeof(s->buffers[i]) );
+ s->buffers[i].lpData = (LPSTR)(s->buffer_bytes + i*samples_size);
+ s->buffers[i].dwBufferLength = samples_size;
+ s->buffers[i].dwFlags = WHDR_DONE;
+
+ result = waveInPrepareHeader( s->wavein, &s->buffers[i],
+ sizeof(s->buffers[i]) );
+ if ( result != MMSYSERR_NOERROR ) {
+ dump_mmerror("waveInPrepareHeader()", result);
+ return -1;
+ }
+
+ result = waveInAddBuffer( s->wavein, &s->buffers[i],
+ sizeof(s->buffers[i]) );
+ if ( result != MMSYSERR_NOERROR ) {
+ dump_mmerror("waveInAddBuffer()", result);
+ return -1;
+ }
+ }
+
+#if DEBUG
+ /* Check the sound device we retrieved */
+ {
+ WAVEINCAPS caps;
+
+ result = waveInGetDevCaps((UINT) s->wavein, &caps, sizeof(caps));
+ if ( result != MMSYSERR_NOERROR ) {
+ dump_mmerror("waveInGetDevCaps()", result);
+ } else
+ printf("Audio in device: %s\n", caps.szPname);
+ }
+#endif
+
+ audio_pcm_init_info (&hw->info, as);
+ hw->samples = conf.nb_samples*2;
+
+ s->read_index = 0;
+ s->read_count = 0;
+ s->read_pos = 0;
+ s->read_size = samples_size;
+ return 0;
+}
+
+
+/* report the number of captured samples to the audio subsystem */
+static int
+winaudio_in_run (HWVoiceIn *hw)
+{
+ WinAudioIn* s = (WinAudioIn*) hw;
+ int captured = 0;
+ int has_buffer;
+ int live = hw->samples - hw->total_samples_captured;
+
+ if (!live) {
+ return 0;
+ }
+
+ EnterCriticalSection( &s->lock );
+ has_buffer = (s->read_count > 0);
+ LeaveCriticalSection( &s->lock );
+
+ if (!has_buffer)
+ return 0;
+
+ while (live > 0) {
+ WAVEHDR* wav_buffer = s->buffers + s->read_index;
+ int wav_bytes = (s->read_size - s->read_pos);
+ int wav_samples = audio_MIN(wav_bytes >> hw->info.shift, live);
+ int hw_samples = audio_MIN(hw->samples - hw->wpos, live);
+ struct st_sample* dst = hw->conv_buf + hw->wpos;
+ uint8_t* src = (uint8_t*)wav_buffer->lpData + s->read_pos;
+
+ if (wav_samples > hw_samples) {
+ wav_samples = hw_samples;
+ }
+
+ wav_bytes = wav_samples << hw->info.shift;
+
+ D("%s: buffer:%d pos:%d size:%d wsamples:%d wbytes:%d live:%d wpos:%d hwsamples:%d\n",
+ __FUNCTION__, s->read_index, s->read_pos, s->read_size, wav_samples, wav_bytes, live,
+ hw->wpos, hw->samples);
+
+ hw->conv(dst, src, wav_samples, &nominal_volume);
+
+ hw->wpos += wav_samples;
+ if (hw->wpos >= hw->samples)
+ hw->wpos -= hw->samples;
+
+ live -= wav_samples;
+ captured += wav_samples;
+ s->read_pos += wav_bytes;
+ if (s->read_pos == s->read_size) {
+ s->read_pos = 0;
+ s->read_index += 1;
+ if (s->read_index == NUM_IN_BUFFERS)
+ s->read_index = 0;
+
+ waveInAddBuffer( s->wavein, wav_buffer, sizeof(*wav_buffer) );
+
+ EnterCriticalSection( &s->lock );
+ if (--s->read_count == 0) {
+ live = 0;
+ }
+ LeaveCriticalSection( &s->lock );
+ }
+ }
+ return captured;
+}
+
+
+static int
+winaudio_in_read (SWVoiceIn *sw, void *buf, int len)
+{
+ int ret = audio_pcm_sw_read (sw, buf, len);
+ if (ret > 0)
+ D("%s: (%d) returned %d\n", __FUNCTION__, len, ret);
+ return ret;
+}
+
+
+static int
+winaudio_in_ctl (HWVoiceIn *hw, int cmd, ...)
+{
+ WinAudioIn* s = (WinAudioIn*) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ D("%s: enable audio in\n", __FUNCTION__);
+ waveInStart( s->wavein );
+ break;
+
+ case VOICE_DISABLE:
+ D("%s: disable audio in\n", __FUNCTION__);
+ waveInStop( s->wavein );
+ break;
+ }
+ return 0;
+}
+
+/** AUDIO STATE
+ **/
+
+typedef struct WinAudioState {
+ int dummy;
+} WinAudioState;
+
+static WinAudioState g_winaudio;
+
+static void*
+winaudio_init(void)
+{
+ WinAudioState* s = &g_winaudio;
+
+#if DEBUG_TIMING
+ start_time = qemu_get_clock(vm_clock);
+ last_time = 0;
+#endif
+
+ return s;
+}
+
+
+static void
+winaudio_fini (void *opaque)
+{
+}
+
+static struct audio_option winaudio_options[] = {
+ {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
+ "Size of Windows audio buffer in samples", NULL, 0},
+ {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static struct audio_pcm_ops winaudio_pcm_ops = {
+ winaudio_out_init,
+ winaudio_out_fini,
+ winaudio_out_run,
+ winaudio_out_write,
+ winaudio_out_ctl,
+
+ winaudio_in_init,
+ winaudio_in_fini,
+ winaudio_in_run,
+ winaudio_in_read,
+ winaudio_in_ctl
+};
+
+struct audio_driver winaudio_audio_driver = {
+ INIT_FIELD (name = ) "winaudio",
+ INIT_FIELD (descr = ) "Windows wave audio",
+ INIT_FIELD (options = ) winaudio_options,
+ INIT_FIELD (init = ) winaudio_init,
+ INIT_FIELD (fini = ) winaudio_fini,
+ INIT_FIELD (pcm_ops = ) &winaudio_pcm_ops,
+ INIT_FIELD (can_be_default = ) 1,
+ INIT_FIELD (max_voices_out = ) 1,
+ INIT_FIELD (max_voices_in = ) 1,
+ INIT_FIELD (voice_size_out = ) sizeof (WinAudioOut),
+ INIT_FIELD (voice_size_in = ) sizeof (WinAudioIn)
+};
diff --git a/configure b/configure
index 89e7f53..3ddb138 100755
--- a/configure
+++ b/configure
@@ -219,14 +219,16 @@ OS_CFLAGS="-mno-cygwin"
if [ "$cpu" = "i386" ] ; then
kqemu="yes"
fi
-audio_possible_drivers="sdl"
+audio_drv_list="winaudio"
+audio_possible_drivers="winaudio sdl"
;;
MINGW32*)
mingw32="yes"
if [ "$cpu" = "i386" ] ; then
kqemu="yes"
fi
-audio_possible_drivers="dsound sdl fmod"
+audio_drv_list=winaudio
+audio_possible_drivers="dsound winaudio sdl fmod"
;;
GNU/kFreeBSD)
audio_drv_list="oss"
@@ -1033,7 +1035,7 @@ for drv in $audio_drv_list; do
"pa_simple *s = NULL; pa_simple_free(s); return 0;"
;;
- oss|sdl|core|wav|dsound)
+ oss|sdl|core|wav|dsound|winaudio)
# XXX: Probes for CoreAudio, DirectSound, SDL(?)
;;
--
1.6.1.2
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [Qemu-devel] [PATCH] Add WinWave-based audio backend to Windows QEMU builds
2009-06-11 0:15 [Qemu-devel] [PATCH] Add WinWave-based audio backend to Windows QEMU builds David Turner
@ 2009-06-11 0:42 ` Anthony Liguori
2009-06-11 0:49 ` David Turner
0 siblings, 1 reply; 5+ messages in thread
From: Anthony Liguori @ 2009-06-11 0:42 UTC (permalink / raw)
To: David Turner; +Cc: qemu-devel
David Turner wrote:
> Here's a patch that provides a Windows audio backend with both
> playback and record capabilities, named "winaudio".
>
Could you please either inline the patch or use a mailer that doesn't
attach it as an application/octet-stream?
Regards,
Anthony Liguori
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [Qemu-devel] [PATCH] Add WinWave-based audio backend to Windows QEMU builds
2009-06-11 0:42 ` Anthony Liguori
@ 2009-06-11 0:49 ` David Turner
2009-06-11 1:35 ` malc
0 siblings, 1 reply; 5+ messages in thread
From: David Turner @ 2009-06-11 0:49 UTC (permalink / raw)
To: Anthony Liguori; +Cc: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 22700 bytes --]
Sorry, here's the patch inlined:
>From 4e061a312b5f6575462e3fa50c7df20788e5f95c Mon Sep 17 00:00:00 2001
From: digit <digit@google.com>
Date: Wed, 10 Jun 2009 14:11:28 +0200
Subject: [PATCH] Add a Windows Wave audio backend to QEMU.
This backend provides both playback and record. It doesn't depend
on any external library like SDL or FMOD, or any non-distributable
headers like the DirectSound ones.
For this reason, it is made the default for Cygwin and Mingw builds
Tested on both Cygwin and MSys builds.
Signed-off-by: David Turner <digit@google.com>
---
Makefile | 3 +
audio/audio_int.h | 1 +
audio/winaudio.c | 663
+++++++++++++++++++++++++++++++++++++++++++++++++++++
configure | 8 +-
4 files changed, 672 insertions(+), 3 deletions(-)
create mode 100644 audio/winaudio.c
diff --git a/Makefile b/Makefile
index 3177616..8f7894a 100644
--- a/Makefile
+++ b/Makefile
@@ -137,6 +137,9 @@ endif
ifdef CONFIG_DSOUND
AUDIO_OBJS += dsoundaudio.o
endif
+ifdef CONFIG_WINAUDIO
+AUDIO_OBJS += winaudio.o
+endif
ifdef CONFIG_FMOD
AUDIO_OBJS += fmodaudio.o
audio/audio.o audio/fmodaudio.o: CPPFLAGS := -I$(CONFIG_FMOD_INC)
$(CPPFLAGS)
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 0abed38..f2c08ac 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -207,6 +207,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 winaudio_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/winaudio.c b/audio/winaudio.c
new file mode 100644
index 0000000..5000f7b
--- /dev/null
+++ b/audio/winaudio.c
@@ -0,0 +1,663 @@
+/*
+ * QEMU "simple" Windows audio driver
+ *
+ * Copyright (c) 2007 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
copy
+ * of this software and associated documentation files (the "Software"), to
deal
+ * in the Software without restriction, including without limitation the
rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
IN
+ * THE SOFTWARE.
+ */
+#include <windows.h>
+#include <mmsystem.h>
+
+#include "qemu-common.h"
+#include "audio.h"
+
+#define AUDIO_CAP "winaudio"
+#include "audio_int.h"
+
+/* define DEBUG to 1 to dump audio debugging info at runtime to stderr */
+#define DEBUG 0
+
+/* define DEBUG_TIMING to 1 to dump timing debugging messages */
+#define DEBUG_TIMING 0
+
+#if DEBUG
+# define D(...) printf(__VA_ARGS__)
+#else
+# define D(...) ((void)0)
+#endif
+
+static struct {
+ int nb_samples;
+} conf = {
+ 1024
+};
+
+#if DEBUG_TIMING
+int64_t start_time;
+int64_t last_time;
+#endif
+
+/* XP works well with 4, but Vista struggles with anything less than 8 */
+#define NUM_OUT_BUFFERS 8 /* must be at least 2 */
+
+/** COMMON UTILITIES
+ **/
+
+#if DEBUG
+static void
+dump_mmerror( const char* func, MMRESULT error )
+{
+ const char* reason = NULL;
+
+ fprintf(stderr, "%s returned error: ", func);
+ switch (error) {
+ case MMSYSERR_ALLOCATED: reason="specified resource is already
allocated"; break;
+ case MMSYSERR_BADDEVICEID: reason="bad device id"; break;
+ case MMSYSERR_NODRIVER: reason="no driver is present"; break;
+ case MMSYSERR_NOMEM: reason="unable to allocate or lock
memory"; break;
+ case WAVERR_BADFORMAT: reason="unsupported waveform-audio
format"; break;
+ case WAVERR_SYNC: reason="device is synchronous"; break;
+ default:
+ fprintf(stderr, "unknown(%d)\n", error);
+ }
+ if (reason)
+ fprintf(stderr, "%s\n", reason);
+}
+#else
+# define dump_mmerror(func,error) ((void)0)
+#endif
+
+
+/** AUDIO OUT
+ **/
+
+typedef struct WinAudioOut {
+ HWVoiceOut hw;
+ HWAVEOUT waveout;
+ int silence;
+ CRITICAL_SECTION lock;
+ unsigned char* buffer_bytes;
+ WAVEHDR buffers[ NUM_OUT_BUFFERS ];
+ int write_index; /* starting first writable buffer
*/
+ int write_count; /* available writable buffers count
*/
+ int write_pos; /* position in current writable buffer
*/
+ int write_size; /* size in bytes of each buffer
*/
+} WinAudioOut;
+
+/* The callback that is called when Windows has finished playing a buffer
*/
+static void CALLBACK
+winaudio_out_buffer_done (HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
+ DWORD dwParam1, DWORD dwParam2)
+{
+ WinAudioOut* s = (WinAudioOut*) dwInstance;
+
+ /* Only service "buffer done playing" messages */
+ if ( uMsg != WOM_DONE )
+ return;
+
+ /* Signal that we are done playing a buffer */
+ EnterCriticalSection( &s->lock );
+ if (s->write_count < NUM_OUT_BUFFERS)
+ s->write_count += 1;
+ LeaveCriticalSection( &s->lock );
+}
+
+static int
+winaudio_out_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static void
+winaudio_out_fini (HWVoiceOut *hw)
+{
+ WinAudioOut* s = (WinAudioOut*) hw;
+ int i;
+
+ if (s->waveout) {
+ waveOutReset(s->waveout);
+ s->waveout = 0;
+ }
+
+ for ( i=0; i<NUM_OUT_BUFFERS; ++i ) {
+ if ( s->buffers[i].dwUser != 0xFFFF ) {
+ waveOutUnprepareHeader(
+ s->waveout, &s->buffers[i], sizeof(s->buffers[i]) );
+ s->buffers[i].dwUser = 0xFFFF;
+ }
+ }
+
+ if (s->buffer_bytes != NULL) {
+ qemu_free(s->buffer_bytes);
+ s->buffer_bytes = NULL;
+ }
+
+ if (s->waveout) {
+ waveOutClose(s->waveout);
+ s->waveout = NULL;
+ }
+}
+
+
+static int
+winaudio_out_init (HWVoiceOut *hw, struct audsettings *as)
+{
+ WinAudioOut* s = (WinAudioOut*) hw;
+ MMRESULT result;
+ WAVEFORMATEX format;
+ int shift, i, samples_size;
+
+ s->waveout = NULL;
+ InitializeCriticalSection( &s->lock );
+ for (i = 0; i < NUM_OUT_BUFFERS; i++) {
+ s->buffers[i].dwUser = 0xFFFF;
+ }
+ s->buffer_bytes = NULL;
+
+ /* compute desired wave output format */
+ format.wFormatTag = WAVE_FORMAT_PCM;
+ format.nChannels = as->nchannels;
+ format.nSamplesPerSec = as->freq;
+ format.nAvgBytesPerSec = as->freq*as->nchannels;
+
+ s->silence = 0;
+
+ switch (as->fmt) {
+ case AUD_FMT_S8: shift = 0; break;
+ case AUD_FMT_U8: shift = 0; s->silence = 0x80; break;
+ case AUD_FMT_S16: shift = 1; break;
+ case AUD_FMT_U16: shift = 1; s->silence = 0x8000; break;
+ case AUD_FMT_S32: shift = 2; break;
+ case AUD_FMT_U32: shift = 2; s->silence = 0x80000000; break;
+ default:
+ fprintf(stderr, "qemu: winaudio: Bad output audio format:
%d\n",
+ as->fmt);
+ return -1;
+ }
+
+ format.nAvgBytesPerSec = (format.nSamplesPerSec & format.nChannels) <<
shift;
+ format.nBlockAlign = format.nChannels << shift;
+ format.wBitsPerSample = 8 << shift;
+ format.cbSize = 0;
+
+ /* open the wave out device */
+ result = waveOutOpen( &s->waveout, WAVE_MAPPER, &format,
+ (DWORD_PTR) winaudio_out_buffer_done,
+ (DWORD_PTR) hw, CALLBACK_FUNCTION);
+ if ( result != MMSYSERR_NOERROR ) {
+ dump_mmerror( "qemu: winaudio: waveOutOpen()", result);
+ return -1;
+ }
+
+ samples_size = format.nBlockAlign * conf.nb_samples;
+ s->buffer_bytes = qemu_malloc( NUM_OUT_BUFFERS * samples_size );
+ if (s->buffer_bytes == NULL) {
+ waveOutClose( s->waveout );
+ s->waveout = NULL;
+ fprintf(stderr, "not enough memory for Windows audio
buffers\n");
+ return -1;
+ }
+
+ for (i = 0; i < NUM_OUT_BUFFERS; i++) {
+ memset( &s->buffers[i], 0, sizeof(s->buffers[i]) );
+ s->buffers[i].lpData = (LPSTR)(s->buffer_bytes +
i*samples_size);
+ s->buffers[i].dwBufferLength = samples_size;
+ s->buffers[i].dwFlags = WHDR_DONE;
+
+ result = waveOutPrepareHeader( s->waveout, &s->buffers[i],
+ sizeof(s->buffers[i]) );
+ if ( result != MMSYSERR_NOERROR ) {
+ dump_mmerror("waveOutPrepareHeader()", result);
+ return -1;
+ }
+ }
+
+#if DEBUG
+ /* Check the sound device we retrieved */
+ {
+ WAVEOUTCAPS caps;
+
+ result = waveOutGetDevCaps((UINT) s->waveout, &caps, sizeof(caps));
+ if ( result != MMSYSERR_NOERROR ) {
+ dump_mmerror("waveOutGetDevCaps()", result);
+ } else
+ printf("Audio out device: %s\n", caps.szPname);
+ }
+#endif
+
+ audio_pcm_init_info (&hw->info, as);
+ hw->samples = conf.nb_samples*2;
+
+ s->write_index = 0;
+ s->write_count = NUM_OUT_BUFFERS;
+ s->write_pos = 0;
+ s->write_size = samples_size;
+ return 0;
+}
+
+
+static int
+winaudio_out_run (HWVoiceOut *hw)
+{
+ WinAudioOut* s = (WinAudioOut*) hw;
+ int played = 0;
+ int has_buffer;
+ int live = audio_pcm_hw_get_live_out (hw);
+
+ if (!live) {
+ return 0;
+ }
+
+ EnterCriticalSection( &s->lock );
+ has_buffer = (s->write_count > 0);
+ LeaveCriticalSection( &s->lock );
+
+ if (!has_buffer)
+ return 0;
+
+ while (live > 0) {
+ WAVEHDR* wav_buffer = s->buffers + s->write_index;
+ int wav_bytes = (s->write_size - s->write_pos);
+ int wav_samples = audio_MIN(wav_bytes >> hw->info.shift,
live);
+ int hw_samples = audio_MIN(hw->samples - hw->rpos,
live);
+ struct st_sample* src = hw->mix_buf + hw->rpos;
+ uint8_t* dst = (uint8_t*)wav_buffer->lpData +
s->write_pos;
+
+ if (wav_samples > hw_samples) {
+ wav_samples = hw_samples;
+ }
+
+ wav_bytes = wav_samples << hw->info.shift;
+
+ //D("run_out: buffer:%d pos:%d size:%d wsamples:%d wbytes:%d
live:%d rpos:%d hwsamples:%d\n", s->write_index,
+ // s->write_pos, s->write_size, wav_samples, wav_bytes, live,
hw->rpos, hw->samples);
+ hw->clip (dst, src, wav_samples);
+ hw->rpos += wav_samples;
+ if (hw->rpos >= hw->samples)
+ hw->rpos -= hw->samples;
+
+ live -= wav_samples;
+ played += wav_samples;
+ s->write_pos += wav_bytes;
+ if (s->write_pos == s->write_size) {
+#if DEBUG_TIMING
+ int64_t now = qemu_get_clock(vm_clock) - start_time;
+ int64_t diff = now - last_time;
+
+ D("run_out: (%7.3f:%7d):waveOutWrite buffer:%d\n",
+ now/1e9, (now-last_time)/1e9, s->write_index);
+ last_time = now;
+#endif
+ waveOutWrite( s->waveout, wav_buffer, sizeof(*wav_buffer) );
+ s->write_pos = 0;
+ s->write_index += 1;
+ if (s->write_index == NUM_OUT_BUFFERS)
+ s->write_index = 0;
+
+ EnterCriticalSection( &s->lock );
+ if (--s->write_count == 0) {
+ live = 0;
+ }
+ LeaveCriticalSection( &s->lock );
+ }
+ }
+ return played;
+}
+
+static int
+winaudio_out_ctl (HWVoiceOut *hw, int cmd, ...)
+{
+ WinAudioOut* s = (WinAudioOut*) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ waveOutRestart( s->waveout );
+ break;
+
+ case VOICE_DISABLE:
+ waveOutPause( s->waveout );
+ break;
+ }
+ return 0;
+}
+
+/** AUDIO IN
+ **/
+
+#define NUM_IN_BUFFERS 2
+
+typedef struct WinAudioIn {
+ HWVoiceIn hw;
+ HWAVEIN wavein;
+ CRITICAL_SECTION lock;
+ unsigned char* buffer_bytes;
+ WAVEHDR buffers[ NUM_IN_BUFFERS ];
+ int read_index;
+ int read_count;
+ int read_pos;
+ int read_size;
+} WinAudioIn;
+
+/* The callback that is called when Windows has finished filling a buffer
*/
+static void CALLBACK
+winaudio_in_buffer_done (HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance,
+ DWORD dwParam1, DWORD dwParam2)
+{
+ WinAudioIn* s = (WinAudioIn*) dwInstance;
+
+ /* Only service "buffer filled" messages */
+ if ( uMsg != WIM_DATA )
+ return;
+
+ /* Signal that we are done playing a buffer */
+ EnterCriticalSection( &s->lock );
+ if (s->read_count < NUM_IN_BUFFERS)
+ s->read_count += 1;
+ LeaveCriticalSection( &s->lock );
+}
+
+static void
+winaudio_in_fini (HWVoiceIn *hw)
+{
+ WinAudioIn* s = (WinAudioIn*) hw;
+ int i;
+
+ if (s->wavein) {
+ waveInReset(s->wavein);
+ s->wavein = 0;
+ }
+
+ for ( i=0; i<NUM_OUT_BUFFERS; ++i ) {
+ if ( s->buffers[i].dwUser != 0xFFFF ) {
+ waveInUnprepareHeader(
+ s->wavein, &s->buffers[i], sizeof(s->buffers[i]) );
+ s->buffers[i].dwUser = 0xFFFF;
+ }
+ }
+
+ if (s->buffer_bytes != NULL) {
+ qemu_free(s->buffer_bytes);
+ s->buffer_bytes = NULL;
+ }
+
+ if (s->wavein) {
+ waveInClose(s->wavein);
+ s->wavein = NULL;
+ }
+}
+
+
+static int
+winaudio_in_init (HWVoiceIn *hw, struct audsettings *as)
+{
+ WinAudioIn* s = (WinAudioIn*) hw;
+ MMRESULT result;
+ WAVEFORMATEX format;
+ int shift, i, samples_size;
+
+ s->wavein = NULL;
+ InitializeCriticalSection( &s->lock );
+ for (i = 0; i < NUM_OUT_BUFFERS; i++) {
+ s->buffers[i].dwUser = 0xFFFF;
+ }
+ s->buffer_bytes = NULL;
+
+ /* compute desired wave input format */
+ format.wFormatTag = WAVE_FORMAT_PCM;
+ format.nChannels = as->nchannels;
+ format.nSamplesPerSec = as->freq;
+ format.nAvgBytesPerSec = as->freq*as->nchannels;
+
+ switch (as->fmt) {
+ case AUD_FMT_S8: shift = 0; break;
+ case AUD_FMT_U8: shift = 0; break;
+ case AUD_FMT_S16: shift = 1; break;
+ case AUD_FMT_U16: shift = 1; break;
+ case AUD_FMT_S32: shift = 2; break;
+ case AUD_FMT_U32: shift = 2; break;
+ default:
+ fprintf(stderr, "qemu: winaudio: Bad input audio format: %d\n",
+ as->fmt);
+ return -1;
+ }
+
+ format.nAvgBytesPerSec = (format.nSamplesPerSec * format.nChannels) <<
shift;
+ format.nBlockAlign = format.nChannels << shift;
+ format.wBitsPerSample = 8 << shift;
+ format.cbSize = 0;
+
+ /* open the wave in device */
+ result = waveInOpen( &s->wavein, WAVE_MAPPER, &format,
+ (DWORD_PTR)winaudio_in_buffer_done, (DWORD_PTR)
hw,
+ CALLBACK_FUNCTION);
+ if ( result != MMSYSERR_NOERROR ) {
+ dump_mmerror( "qemu: winaudio: waveInOpen()", result);
+ return -1;
+ }
+
+ samples_size = format.nBlockAlign * conf.nb_samples;
+ s->buffer_bytes = qemu_malloc( NUM_IN_BUFFERS * samples_size );
+ if (s->buffer_bytes == NULL) {
+ waveInClose( s->wavein );
+ s->wavein = NULL;
+ fprintf(stderr, "not enough memory for Windows audio buffers\n");
+ return -1;
+ }
+
+ for (i = 0; i < NUM_IN_BUFFERS; i++) {
+ memset( &s->buffers[i], 0, sizeof(s->buffers[i]) );
+ s->buffers[i].lpData = (LPSTR)(s->buffer_bytes +
i*samples_size);
+ s->buffers[i].dwBufferLength = samples_size;
+ s->buffers[i].dwFlags = WHDR_DONE;
+
+ result = waveInPrepareHeader( s->wavein, &s->buffers[i],
+ sizeof(s->buffers[i]) );
+ if ( result != MMSYSERR_NOERROR ) {
+ dump_mmerror("waveInPrepareHeader()", result);
+ return -1;
+ }
+
+ result = waveInAddBuffer( s->wavein, &s->buffers[i],
+ sizeof(s->buffers[i]) );
+ if ( result != MMSYSERR_NOERROR ) {
+ dump_mmerror("waveInAddBuffer()", result);
+ return -1;
+ }
+ }
+
+#if DEBUG
+ /* Check the sound device we retrieved */
+ {
+ WAVEINCAPS caps;
+
+ result = waveInGetDevCaps((UINT) s->wavein, &caps, sizeof(caps));
+ if ( result != MMSYSERR_NOERROR ) {
+ dump_mmerror("waveInGetDevCaps()", result);
+ } else
+ printf("Audio in device: %s\n", caps.szPname);
+ }
+#endif
+
+ audio_pcm_init_info (&hw->info, as);
+ hw->samples = conf.nb_samples*2;
+
+ s->read_index = 0;
+ s->read_count = 0;
+ s->read_pos = 0;
+ s->read_size = samples_size;
+ return 0;
+}
+
+
+/* report the number of captured samples to the audio subsystem */
+static int
+winaudio_in_run (HWVoiceIn *hw)
+{
+ WinAudioIn* s = (WinAudioIn*) hw;
+ int captured = 0;
+ int has_buffer;
+ int live = hw->samples - hw->total_samples_captured;
+
+ if (!live) {
+ return 0;
+ }
+
+ EnterCriticalSection( &s->lock );
+ has_buffer = (s->read_count > 0);
+ LeaveCriticalSection( &s->lock );
+
+ if (!has_buffer)
+ return 0;
+
+ while (live > 0) {
+ WAVEHDR* wav_buffer = s->buffers + s->read_index;
+ int wav_bytes = (s->read_size - s->read_pos);
+ int wav_samples = audio_MIN(wav_bytes >> hw->info.shift,
live);
+ int hw_samples = audio_MIN(hw->samples - hw->wpos,
live);
+ struct st_sample* dst = hw->conv_buf + hw->wpos;
+ uint8_t* src = (uint8_t*)wav_buffer->lpData +
s->read_pos;
+
+ if (wav_samples > hw_samples) {
+ wav_samples = hw_samples;
+ }
+
+ wav_bytes = wav_samples << hw->info.shift;
+
+ D("%s: buffer:%d pos:%d size:%d wsamples:%d wbytes:%d live:%d
wpos:%d hwsamples:%d\n",
+ __FUNCTION__, s->read_index, s->read_pos, s->read_size,
wav_samples, wav_bytes, live,
+ hw->wpos, hw->samples);
+
+ hw->conv(dst, src, wav_samples, &nominal_volume);
+
+ hw->wpos += wav_samples;
+ if (hw->wpos >= hw->samples)
+ hw->wpos -= hw->samples;
+
+ live -= wav_samples;
+ captured += wav_samples;
+ s->read_pos += wav_bytes;
+ if (s->read_pos == s->read_size) {
+ s->read_pos = 0;
+ s->read_index += 1;
+ if (s->read_index == NUM_IN_BUFFERS)
+ s->read_index = 0;
+
+ waveInAddBuffer( s->wavein, wav_buffer, sizeof(*wav_buffer) );
+
+ EnterCriticalSection( &s->lock );
+ if (--s->read_count == 0) {
+ live = 0;
+ }
+ LeaveCriticalSection( &s->lock );
+ }
+ }
+ return captured;
+}
+
+
+static int
+winaudio_in_read (SWVoiceIn *sw, void *buf, int len)
+{
+ int ret = audio_pcm_sw_read (sw, buf, len);
+ if (ret > 0)
+ D("%s: (%d) returned %d\n", __FUNCTION__, len, ret);
+ return ret;
+}
+
+
+static int
+winaudio_in_ctl (HWVoiceIn *hw, int cmd, ...)
+{
+ WinAudioIn* s = (WinAudioIn*) hw;
+
+ switch (cmd) {
+ case VOICE_ENABLE:
+ D("%s: enable audio in\n", __FUNCTION__);
+ waveInStart( s->wavein );
+ break;
+
+ case VOICE_DISABLE:
+ D("%s: disable audio in\n", __FUNCTION__);
+ waveInStop( s->wavein );
+ break;
+ }
+ return 0;
+}
+
+/** AUDIO STATE
+ **/
+
+typedef struct WinAudioState {
+ int dummy;
+} WinAudioState;
+
+static WinAudioState g_winaudio;
+
+static void*
+winaudio_init(void)
+{
+ WinAudioState* s = &g_winaudio;
+
+#if DEBUG_TIMING
+ start_time = qemu_get_clock(vm_clock);
+ last_time = 0;
+#endif
+
+ return s;
+}
+
+
+static void
+winaudio_fini (void *opaque)
+{
+}
+
+static struct audio_option winaudio_options[] = {
+ {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
+ "Size of Windows audio buffer in samples", NULL, 0},
+ {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static struct audio_pcm_ops winaudio_pcm_ops = {
+ winaudio_out_init,
+ winaudio_out_fini,
+ winaudio_out_run,
+ winaudio_out_write,
+ winaudio_out_ctl,
+
+ winaudio_in_init,
+ winaudio_in_fini,
+ winaudio_in_run,
+ winaudio_in_read,
+ winaudio_in_ctl
+};
+
+struct audio_driver winaudio_audio_driver = {
+ INIT_FIELD (name = ) "winaudio",
+ INIT_FIELD (descr = ) "Windows wave audio",
+ INIT_FIELD (options = ) winaudio_options,
+ INIT_FIELD (init = ) winaudio_init,
+ INIT_FIELD (fini = ) winaudio_fini,
+ INIT_FIELD (pcm_ops = ) &winaudio_pcm_ops,
+ INIT_FIELD (can_be_default = ) 1,
+ INIT_FIELD (max_voices_out = ) 1,
+ INIT_FIELD (max_voices_in = ) 1,
+ INIT_FIELD (voice_size_out = ) sizeof (WinAudioOut),
+ INIT_FIELD (voice_size_in = ) sizeof (WinAudioIn)
+};
diff --git a/configure b/configure
index 89e7f53..3ddb138 100755
--- a/configure
+++ b/configure
@@ -219,14 +219,16 @@ OS_CFLAGS="-mno-cygwin"
if [ "$cpu" = "i386" ] ; then
kqemu="yes"
fi
-audio_possible_drivers="sdl"
+audio_drv_list="winaudio"
+audio_possible_drivers="winaudio sdl"
;;
MINGW32*)
mingw32="yes"
if [ "$cpu" = "i386" ] ; then
kqemu="yes"
fi
-audio_possible_drivers="dsound sdl fmod"
+audio_drv_list=winaudio
+audio_possible_drivers="dsound winaudio sdl fmod"
;;
GNU/kFreeBSD)
audio_drv_list="oss"
@@ -1033,7 +1035,7 @@ for drv in $audio_drv_list; do
"pa_simple *s = NULL; pa_simple_free(s); return 0;"
;;
- oss|sdl|core|wav|dsound)
+ oss|sdl|core|wav|dsound|winaudio)
# XXX: Probes for CoreAudio, DirectSound, SDL(?)
;;
--
1.5.4.3
[-- Attachment #2: Type: text/html, Size: 26673 bytes --]
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [Qemu-devel] [PATCH] Add WinWave-based audio backend to Windows QEMU builds
2009-06-11 0:49 ` David Turner
@ 2009-06-11 1:35 ` malc
2009-06-12 9:03 ` David Turner
0 siblings, 1 reply; 5+ messages in thread
From: malc @ 2009-06-11 1:35 UTC (permalink / raw)
To: David Turner; +Cc: qemu-devel
On Thu, 11 Jun 2009, David Turner wrote:
Thanks for submitting this.
> Sorry, here's the patch inlined:
And it was mangled beyond recognition by your mailer, but nevermind that
for now, since the attached version applies cleanly.
Stylistic nit, the thing is terribly inconsistent, it's neither in QEMU
nor in audio/* style. Personally I would have preferred it to look like
the rest of audio, i.e.:
func/controlflow<space>(<no space>...<no space>)
Or if you find it more acceptable, per QEMU coding style:
func<no space>(<no space>...<no space>)
controlflow<space>(<no space>...<no space>)
>
> >>From 4e061a312b5f6575462e3fa50c7df20788e5f95c Mon Sep 17 00:00:00 2001
> From: digit <digit@google.com>
> Date: Wed, 10 Jun 2009 14:11:28 +0200
> Subject: [PATCH] Add a Windows Wave audio backend to QEMU.
>
> This backend provides both playback and record. It doesn't depend
> on any external library like SDL or FMOD, or any non-distributable
> headers like the DirectSound ones.
>
> For this reason, it is made the default for Cygwin and Mingw builds
>
> Tested on both Cygwin and MSys builds.
[..snip..]
> +/* XP works well with 4, but Vista struggles with anything less than 8 */
> +#define NUM_OUT_BUFFERS 8 /* must be at least 2 */
I wonder how Vista works with dsound, okayish, struggles?
> +
> +/** COMMON UTILITIES
> + **/
> +
> +#if DEBUG
> +static void
> +dump_mmerror( const char* func, MMRESULT error )
> +{
> + const char* reason = NULL;
> +
> + fprintf(stderr, "%s returned error: ", func);
> + switch (error) {
> + case MMSYSERR_ALLOCATED: reason="specified resource is already
> allocated"; break;
> + case MMSYSERR_BADDEVICEID: reason="bad device id"; break;
> + case MMSYSERR_NODRIVER: reason="no driver is present"; break;
> + case MMSYSERR_NOMEM: reason="unable to allocate or lock
> memory"; break;
> + case WAVERR_BADFORMAT: reason="unsupported waveform-audio
> format"; break;
> + case WAVERR_SYNC: reason="device is synchronous"; break;
> + default:
> + fprintf(stderr, "unknown(%d)\n", error);
> + }
> + if (reason)
> + fprintf(stderr, "%s\n", reason);
> +}
> +#else
> +# define dump_mmerror(func,error) ((void)0)
> +#endif
1. It should be unconditional
2. switch (error) {
case ...:
default: return;
}
fprintf (...)
3. The lines are extra long
> +
> +
> +/** AUDIO OUT
> + **/
> +
> +typedef struct WinAudioOut {
> + HWVoiceOut hw;
> + HWAVEOUT waveout;
> + int silence;
> + CRITICAL_SECTION lock;
> + unsigned char* buffer_bytes;
> + WAVEHDR buffers[ NUM_OUT_BUFFERS ];
> + int write_index; /* starting first writable buffer
> */
> + int write_count; /* available writable buffers count
> */
> + int write_pos; /* position in current writable buffer
> */
> + int write_size; /* size in bytes of each buffer
> */
> +} WinAudioOut;
> +
> +/* The callback that is called when Windows has finished playing a buffer
> */
> +static void CALLBACK
> +winaudio_out_buffer_done (HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
> + DWORD dwParam1, DWORD dwParam2)
> +{
> + WinAudioOut* s = (WinAudioOut*) dwInstance;
> +
> + /* Only service "buffer done playing" messages */
> + if ( uMsg != WOM_DONE )
> + return;
> +
> + /* Signal that we are done playing a buffer */
> + EnterCriticalSection( &s->lock );
> + if (s->write_count < NUM_OUT_BUFFERS)
> + s->write_count += 1;
> + LeaveCriticalSection( &s->lock );
> +}
> +
> +static int
> +winaudio_out_write (SWVoiceOut *sw, void *buf, int len)
> +{
> + return audio_pcm_sw_write (sw, buf, len);
> +}
> +
> +static void
> +winaudio_out_fini (HWVoiceOut *hw)
> +{
> + WinAudioOut* s = (WinAudioOut*) hw;
> + int i;
> +
> + if (s->waveout) {
> + waveOutReset(s->waveout);
> + s->waveout = 0;
> + }
> +
> + for ( i=0; i<NUM_OUT_BUFFERS; ++i ) {
> + if ( s->buffers[i].dwUser != 0xFFFF ) {
> + waveOutUnprepareHeader(
> + s->waveout, &s->buffers[i], sizeof(s->buffers[i]) );
> + s->buffers[i].dwUser = 0xFFFF;
> + }
> + }
> +
> + if (s->buffer_bytes != NULL) {
> + qemu_free(s->buffer_bytes);
> + s->buffer_bytes = NULL;
> + }
> +
> + if (s->waveout) {
> + waveOutClose(s->waveout);
> + s->waveout = NULL;
> + }
> +}
> +
> +
> +static int
> +winaudio_out_init (HWVoiceOut *hw, struct audsettings *as)
> +{
> + WinAudioOut* s = (WinAudioOut*) hw;
> + MMRESULT result;
> + WAVEFORMATEX format;
> + int shift, i, samples_size;
> +
> + s->waveout = NULL;
> + InitializeCriticalSection( &s->lock );
> + for (i = 0; i < NUM_OUT_BUFFERS; i++) {
> + s->buffers[i].dwUser = 0xFFFF;
> + }
> + s->buffer_bytes = NULL;
> +
> + /* compute desired wave output format */
> + format.wFormatTag = WAVE_FORMAT_PCM;
> + format.nChannels = as->nchannels;
> + format.nSamplesPerSec = as->freq;
> + format.nAvgBytesPerSec = as->freq*as->nchannels;
> +
> + s->silence = 0;
> +
> + switch (as->fmt) {
> + case AUD_FMT_S8: shift = 0; break;
> + case AUD_FMT_U8: shift = 0; s->silence = 0x80; break;
> + case AUD_FMT_S16: shift = 1; break;
> + case AUD_FMT_U16: shift = 1; s->silence = 0x8000; break;
> + case AUD_FMT_S32: shift = 2; break;
> + case AUD_FMT_U32: shift = 2; s->silence = 0x80000000; break;
> + default:
> + fprintf(stderr, "qemu: winaudio: Bad output audio format:
> %d\n",
> + as->fmt);
> + return -1;
> + }
> +
It appears that s->silence is never used.
> + format.nAvgBytesPerSec = (format.nSamplesPerSec & format.nChannels) <<
> shift;
~format.nChannels was meant perhaps?
> + format.nBlockAlign = format.nChannels << shift;
> + format.wBitsPerSample = 8 << shift;
[Just checking - is wBitsPerSample is really sample or frame? Only ALSA, to the
best of my knowledge makes clear distinction between the two]
> + format.cbSize = 0;
> +
> + /* open the wave out device */
> + result = waveOutOpen( &s->waveout, WAVE_MAPPER, &format,
> + (DWORD_PTR) winaudio_out_buffer_done,
> + (DWORD_PTR) hw, CALLBACK_FUNCTION);
> + if ( result != MMSYSERR_NOERROR ) {
> + dump_mmerror( "qemu: winaudio: waveOutOpen()", result);
> + return -1;
> + }
Indentation is off here.
> +
> + samples_size = format.nBlockAlign * conf.nb_samples;
> + s->buffer_bytes = qemu_malloc( NUM_OUT_BUFFERS * samples_size );
audio_calloc ought to be used here...
> + if (s->buffer_bytes == NULL) {
> + waveOutClose( s->waveout );
> + s->waveout = NULL;
> + fprintf(stderr, "not enough memory for Windows audio
> buffers\n");
> + return -1;
> + }
Whose return value is indeed ought to be checked for NULL, but no
message is necessary. Checks in audio_calloc once have proven to be
useful when some FreeBSD returned bogus number of fragments(0) and
consequently attempt was made to allocate 0 bytes for oss's pcm_buf.
> +
> + for (i = 0; i < NUM_OUT_BUFFERS; i++) {
> + memset( &s->buffers[i], 0, sizeof(s->buffers[i]) );
> + s->buffers[i].lpData = (LPSTR)(s->buffer_bytes +
> i*samples_size);
> + s->buffers[i].dwBufferLength = samples_size;
> + s->buffers[i].dwFlags = WHDR_DONE;
> +
> + result = waveOutPrepareHeader( s->waveout, &s->buffers[i],
> + sizeof(s->buffers[i]) );
> + if ( result != MMSYSERR_NOERROR ) {
> + dump_mmerror("waveOutPrepareHeader()", result);
> + return -1;
> + }
> + }
> +
> +#if DEBUG
> + /* Check the sound device we retrieved */
> + {
> + WAVEOUTCAPS caps;
> +
> + result = waveOutGetDevCaps((UINT) s->waveout, &caps, sizeof(caps));
> + if ( result != MMSYSERR_NOERROR ) {
> + dump_mmerror("waveOutGetDevCaps()", result);
> + } else
> + printf("Audio out device: %s\n", caps.szPname);
> + }
> +#endif
> +
> + audio_pcm_init_info (&hw->info, as);
> + hw->samples = conf.nb_samples*2;
This one I don't get, why is it unconditionally multiplied by 2?
> +
> + s->write_index = 0;
> + s->write_count = NUM_OUT_BUFFERS;
> + s->write_pos = 0;
> + s->write_size = samples_size;
> + return 0;
> +}
> +
> +
> +static int
> +winaudio_out_run (HWVoiceOut *hw)
> +{
> + WinAudioOut* s = (WinAudioOut*) hw;
> + int played = 0;
> + int has_buffer;
> + int live = audio_pcm_hw_get_live_out (hw);
> +
> + if (!live) {
> + return 0;
> + }
> +
> + EnterCriticalSection( &s->lock );
> + has_buffer = (s->write_count > 0);
> + LeaveCriticalSection( &s->lock );
> +
> + if (!has_buffer)
> + return 0;
> +
> + while (live > 0) {
> + WAVEHDR* wav_buffer = s->buffers + s->write_index;
> + int wav_bytes = (s->write_size - s->write_pos);
> + int wav_samples = audio_MIN(wav_bytes >> hw->info.shift,
> live);
> + int hw_samples = audio_MIN(hw->samples - hw->rpos,
> live);
> + struct st_sample* src = hw->mix_buf + hw->rpos;
> + uint8_t* dst = (uint8_t*)wav_buffer->lpData +
> s->write_pos;
Asterisks are c++isplaced.
> +
> + if (wav_samples > hw_samples) {
> + wav_samples = hw_samples;
> + }
> +
> + wav_bytes = wav_samples << hw->info.shift;
> +
> + //D("run_out: buffer:%d pos:%d size:%d wsamples:%d wbytes:%d
> live:%d rpos:%d hwsamples:%d\n", s->write_index,
> + // s->write_pos, s->write_size, wav_samples, wav_bytes, live,
> hw->rpos, hw->samples);
> + hw->clip (dst, src, wav_samples);
> + hw->rpos += wav_samples;
> + if (hw->rpos >= hw->samples)
> + hw->rpos -= hw->samples;
> +
> + live -= wav_samples;
> + played += wav_samples;
> + s->write_pos += wav_bytes;
> + if (s->write_pos == s->write_size) {
> +#if DEBUG_TIMING
> + int64_t now = qemu_get_clock(vm_clock) - start_time;
> + int64_t diff = now - last_time;
> +
> + D("run_out: (%7.3f:%7d):waveOutWrite buffer:%d\n",
> + now/1e9, (now-last_time)/1e9, s->write_index);
> + last_time = now;
> +#endif
> + waveOutWrite( s->waveout, wav_buffer, sizeof(*wav_buffer) );
> + s->write_pos = 0;
> + s->write_index += 1;
> + if (s->write_index == NUM_OUT_BUFFERS)
> + s->write_index = 0;
> +
> + EnterCriticalSection( &s->lock );
> + if (--s->write_count == 0) {
> + live = 0;
> + }
> + LeaveCriticalSection( &s->lock );
> + }
> + }
> + return played;
> +}
> +
> +static int
> +winaudio_out_ctl (HWVoiceOut *hw, int cmd, ...)
> +{
> + WinAudioOut* s = (WinAudioOut*) hw;
> +
> + switch (cmd) {
> + case VOICE_ENABLE:
> + waveOutRestart( s->waveout );
> + break;
> +
> + case VOICE_DISABLE:
> + waveOutPause( s->waveout );
> + break;
> + }
> + return 0;
> +}
> +
> +/** AUDIO IN
> + **/
> +
> +#define NUM_IN_BUFFERS 2
> +
> +typedef struct WinAudioIn {
> + HWVoiceIn hw;
> + HWAVEIN wavein;
> + CRITICAL_SECTION lock;
> + unsigned char* buffer_bytes;
> + WAVEHDR buffers[ NUM_IN_BUFFERS ];
> + int read_index;
> + int read_count;
> + int read_pos;
> + int read_size;
> +} WinAudioIn;
> +
> +/* The callback that is called when Windows has finished filling a buffer
> */
> +static void CALLBACK
> +winaudio_in_buffer_done (HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance,
> + DWORD dwParam1, DWORD dwParam2)
> +{
> + WinAudioIn* s = (WinAudioIn*) dwInstance;
> +
> + /* Only service "buffer filled" messages */
> + if ( uMsg != WIM_DATA )
> + return;
> +
> + /* Signal that we are done playing a buffer */
> + EnterCriticalSection( &s->lock );
> + if (s->read_count < NUM_IN_BUFFERS)
> + s->read_count += 1;
> + LeaveCriticalSection( &s->lock );
> +}
> +
> +static void
> +winaudio_in_fini (HWVoiceIn *hw)
> +{
> + WinAudioIn* s = (WinAudioIn*) hw;
> + int i;
> +
> + if (s->wavein) {
> + waveInReset(s->wavein);
> + s->wavein = 0;
> + }
> +
> + for ( i=0; i<NUM_OUT_BUFFERS; ++i ) {
> + if ( s->buffers[i].dwUser != 0xFFFF ) {
> + waveInUnprepareHeader(
> + s->wavein, &s->buffers[i], sizeof(s->buffers[i]) );
> + s->buffers[i].dwUser = 0xFFFF;
Indentation is off.
> + }
> + }
> +
> + if (s->buffer_bytes != NULL) {
> + qemu_free(s->buffer_bytes);
> + s->buffer_bytes = NULL;
> + }
> +
> + if (s->wavein) {
> + waveInClose(s->wavein);
> + s->wavein = NULL;
> + }
> +}
> +
> +
> +static int
> +winaudio_in_init (HWVoiceIn *hw, struct audsettings *as)
> +{
> + WinAudioIn* s = (WinAudioIn*) hw;
> + MMRESULT result;
> + WAVEFORMATEX format;
> + int shift, i, samples_size;
> +
> + s->wavein = NULL;
> + InitializeCriticalSection( &s->lock );
> + for (i = 0; i < NUM_OUT_BUFFERS; i++) {
> + s->buffers[i].dwUser = 0xFFFF;
> + }
> + s->buffer_bytes = NULL;
> +
> + /* compute desired wave input format */
> + format.wFormatTag = WAVE_FORMAT_PCM;
> + format.nChannels = as->nchannels;
> + format.nSamplesPerSec = as->freq;
> + format.nAvgBytesPerSec = as->freq*as->nchannels;
> +
> + switch (as->fmt) {
> + case AUD_FMT_S8: shift = 0; break;
> + case AUD_FMT_U8: shift = 0; break;
> + case AUD_FMT_S16: shift = 1; break;
> + case AUD_FMT_U16: shift = 1; break;
> + case AUD_FMT_S32: shift = 2; break;
> + case AUD_FMT_U32: shift = 2; break;
> + default:
> + fprintf(stderr, "qemu: winaudio: Bad input audio format: %d\n",
> + as->fmt);
> + return -1;
> + }
> +
> + format.nAvgBytesPerSec = (format.nSamplesPerSec * format.nChannels) <<
> shift;
> + format.nBlockAlign = format.nChannels << shift;
> + format.wBitsPerSample = 8 << shift;
> + format.cbSize = 0;
> +
> + /* open the wave in device */
> + result = waveInOpen( &s->wavein, WAVE_MAPPER, &format,
> + (DWORD_PTR)winaudio_in_buffer_done, (DWORD_PTR)
> hw,
> + CALLBACK_FUNCTION);
> + if ( result != MMSYSERR_NOERROR ) {
> + dump_mmerror( "qemu: winaudio: waveInOpen()", result);
> + return -1;
Here too.
> + }
> +
> + samples_size = format.nBlockAlign * conf.nb_samples;
> + s->buffer_bytes = qemu_malloc( NUM_IN_BUFFERS * samples_size );
audio_calloc
> + if (s->buffer_bytes == NULL) {
> + waveInClose( s->wavein );
> + s->wavein = NULL;
> + fprintf(stderr, "not enough memory for Windows audio buffers\n");
> + return -1;
> + }
> +
> + for (i = 0; i < NUM_IN_BUFFERS; i++) {
> + memset( &s->buffers[i], 0, sizeof(s->buffers[i]) );
> + s->buffers[i].lpData = (LPSTR)(s->buffer_bytes +
> i*samples_size);
> + s->buffers[i].dwBufferLength = samples_size;
> + s->buffers[i].dwFlags = WHDR_DONE;
> +
> + result = waveInPrepareHeader( s->wavein, &s->buffers[i],
> + sizeof(s->buffers[i]) );
> + if ( result != MMSYSERR_NOERROR ) {
> + dump_mmerror("waveInPrepareHeader()", result);
> + return -1;
> + }
> +
> + result = waveInAddBuffer( s->wavein, &s->buffers[i],
> + sizeof(s->buffers[i]) );
> + if ( result != MMSYSERR_NOERROR ) {
> + dump_mmerror("waveInAddBuffer()", result);
> + return -1;
> + }
> + }
> +
> +#if DEBUG
> + /* Check the sound device we retrieved */
> + {
> + WAVEINCAPS caps;
> +
> + result = waveInGetDevCaps((UINT) s->wavein, &caps, sizeof(caps));
> + if ( result != MMSYSERR_NOERROR ) {
> + dump_mmerror("waveInGetDevCaps()", result);
> + } else
> + printf("Audio in device: %s\n", caps.szPname);
> + }
> +#endif
> +
> + audio_pcm_init_info (&hw->info, as);
> + hw->samples = conf.nb_samples*2;
Again.. why?
> +
> + s->read_index = 0;
> + s->read_count = 0;
> + s->read_pos = 0;
> + s->read_size = samples_size;
> + return 0;
> +}
> +
> +
> +/* report the number of captured samples to the audio subsystem */
> +static int
> +winaudio_in_run (HWVoiceIn *hw)
> +{
> + WinAudioIn* s = (WinAudioIn*) hw;
> + int captured = 0;
> + int has_buffer;
> + int live = hw->samples - hw->total_samples_captured;
> +
> + if (!live) {
> + return 0;
> + }
> +
> + EnterCriticalSection( &s->lock );
> + has_buffer = (s->read_count > 0);
> + LeaveCriticalSection( &s->lock );
> +
> + if (!has_buffer)
> + return 0;
> +
> + while (live > 0) {
> + WAVEHDR* wav_buffer = s->buffers + s->read_index;
> + int wav_bytes = (s->read_size - s->read_pos);
> + int wav_samples = audio_MIN(wav_bytes >> hw->info.shift,
> live);
> + int hw_samples = audio_MIN(hw->samples - hw->wpos,
> live);
> + struct st_sample* dst = hw->conv_buf + hw->wpos;
> + uint8_t* src = (uint8_t*)wav_buffer->lpData +
> s->read_pos;
> +
> + if (wav_samples > hw_samples) {
> + wav_samples = hw_samples;
> + }
> +
> + wav_bytes = wav_samples << hw->info.shift;
> +
> + D("%s: buffer:%d pos:%d size:%d wsamples:%d wbytes:%d live:%d
> wpos:%d hwsamples:%d\n",
> + __FUNCTION__, s->read_index, s->read_pos, s->read_size,
> wav_samples, wav_bytes, live,
> + hw->wpos, hw->samples);
> +
> + hw->conv(dst, src, wav_samples, &nominal_volume);
> +
> + hw->wpos += wav_samples;
> + if (hw->wpos >= hw->samples)
> + hw->wpos -= hw->samples;
> +
> + live -= wav_samples;
> + captured += wav_samples;
> + s->read_pos += wav_bytes;
> + if (s->read_pos == s->read_size) {
> + s->read_pos = 0;
> + s->read_index += 1;
> + if (s->read_index == NUM_IN_BUFFERS)
> + s->read_index = 0;
> +
> + waveInAddBuffer( s->wavein, wav_buffer, sizeof(*wav_buffer) );
> +
> + EnterCriticalSection( &s->lock );
> + if (--s->read_count == 0) {
> + live = 0;
> + }
> + LeaveCriticalSection( &s->lock );
> + }
> + }
> + return captured;
> +}
> +
> +
> +static int
> +winaudio_in_read (SWVoiceIn *sw, void *buf, int len)
> +{
> + int ret = audio_pcm_sw_read (sw, buf, len);
> + if (ret > 0)
> + D("%s: (%d) returned %d\n", __FUNCTION__, len, ret);
> + return ret;
> +}
> +
> +
> +static int
> +winaudio_in_ctl (HWVoiceIn *hw, int cmd, ...)
> +{
> + WinAudioIn* s = (WinAudioIn*) hw;
> +
> + switch (cmd) {
> + case VOICE_ENABLE:
> + D("%s: enable audio in\n", __FUNCTION__);
> + waveInStart( s->wavein );
> + break;
> +
> + case VOICE_DISABLE:
> + D("%s: disable audio in\n", __FUNCTION__);
> + waveInStop( s->wavein );
> + break;
> + }
> + return 0;
> +}
> +
> +/** AUDIO STATE
> + **/
> +
> +typedef struct WinAudioState {
> + int dummy;
> +} WinAudioState;
> +
> +static WinAudioState g_winaudio;
> +
> +static void*
> +winaudio_init(void)
> +{
> + WinAudioState* s = &g_winaudio;
> +
> +#if DEBUG_TIMING
> + start_time = qemu_get_clock(vm_clock);
> + last_time = 0;
> +#endif
> +
> + return s;
> +}
> +
> +
> +static void
> +winaudio_fini (void *opaque)
> +{
> +}
> +
> +static struct audio_option winaudio_options[] = {
> + {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
> + "Size of Windows audio buffer in samples", NULL, 0},
> + {NULL, 0, NULL, NULL, NULL, 0}
> +};
> +
> +static struct audio_pcm_ops winaudio_pcm_ops = {
> + winaudio_out_init,
> + winaudio_out_fini,
> + winaudio_out_run,
> + winaudio_out_write,
> + winaudio_out_ctl,
> +
> + winaudio_in_init,
> + winaudio_in_fini,
> + winaudio_in_run,
> + winaudio_in_read,
> + winaudio_in_ctl
> +};
> +
> +struct audio_driver winaudio_audio_driver = {
> + INIT_FIELD (name = ) "winaudio",
> + INIT_FIELD (descr = ) "Windows wave audio",
> + INIT_FIELD (options = ) winaudio_options,
> + INIT_FIELD (init = ) winaudio_init,
> + INIT_FIELD (fini = ) winaudio_fini,
> + INIT_FIELD (pcm_ops = ) &winaudio_pcm_ops,
> + INIT_FIELD (can_be_default = ) 1,
> + INIT_FIELD (max_voices_out = ) 1,
> + INIT_FIELD (max_voices_in = ) 1,
> + INIT_FIELD (voice_size_out = ) sizeof (WinAudioOut),
> + INIT_FIELD (voice_size_in = ) sizeof (WinAudioIn)
> +};
> diff --git a/configure b/configure
> index 89e7f53..3ddb138 100755
> --- a/configure
> +++ b/configure
> @@ -219,14 +219,16 @@ OS_CFLAGS="-mno-cygwin"
> if [ "$cpu" = "i386" ] ; then
> kqemu="yes"
> fi
> -audio_possible_drivers="sdl"
> +audio_drv_list="winaudio"
> +audio_possible_drivers="winaudio sdl"
> ;;
> MINGW32*)
> mingw32="yes"
> if [ "$cpu" = "i386" ] ; then
> kqemu="yes"
> fi
> -audio_possible_drivers="dsound sdl fmod"
> +audio_drv_list=winaudio
> +audio_possible_drivers="dsound winaudio sdl fmod"
> ;;
> GNU/kFreeBSD)
> audio_drv_list="oss"
> @@ -1033,7 +1035,7 @@ for drv in $audio_drv_list; do
> "pa_simple *s = NULL; pa_simple_free(s); return 0;"
> ;;
>
> - oss|sdl|core|wav|dsound)
> + oss|sdl|core|wav|dsound|winaudio)
> # XXX: Probes for CoreAudio, DirectSound, SDL(?)
> ;;
>
>
--
mailto:av1474@comtv.ru
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [Qemu-devel] [PATCH] Add WinWave-based audio backend to Windows QEMU builds
2009-06-11 1:35 ` malc
@ 2009-06-12 9:03 ` David Turner
0 siblings, 0 replies; 5+ messages in thread
From: David Turner @ 2009-06-12 9:03 UTC (permalink / raw)
To: malc; +Cc: qemu-devel
[-- Attachment #1: Type: text/plain, Size: 5307 bytes --]
On Thu, Jun 11, 2009 at 3:35 AM, malc <av1474@comtv.ru> wrote:
> On Thu, 11 Jun 2009, David Turner wrote:
>
> Thanks for submitting this.
>
> > Sorry, here's the patch inlined:
>
> And it was mangled beyond recognition by your mailer, but nevermind that
> for now, since the attached version applies cleanly.
>
That's unfortunate. I really need to understand why git-send-email crashes
on my cygwin installation.
It should make all these things disappear. I hope it's just a missing Perl
dependency or something like that.
>
> Stylistic nit, the thing is terribly inconsistent, it's neither in QEMU
> nor in audio/* style. Personally I would have preferred it to look like
> the rest of audio, i.e.:
>
> func/controlflow<space>(<no space>...<no space>)
>
> Or if you find it more acceptable, per QEMU coding style:
>
> func<no space>(<no space>...<no space>)
> controlflow<space>(<no space>...<no space>)
>
ok, I'll try to reformat it to use the audio style. It's only logical that
it looks like the rest of the audio
backends.
>
>
> > +/* XP works well with 4, but Vista struggles with anything less than 8
> */
> > +#define NUM_OUT_BUFFERS 8 /* must be at least 2 */
>
> I wonder how Vista works with dsound, okayish, struggles?
>
I have no idea really, and I don't have a Vista machine near me to test that
with a custom build
at the moment.
>
> > +
> > +/** COMMON UTILITIES
> > + **/
> > +
> > +#if DEBUG
> > +static void
> > +dump_mmerror( const char* func, MMRESULT error )
> > +{
> > + const char* reason = NULL;
> > +
> > + fprintf(stderr, "%s returned error: ", func);
> > + switch (error) {
> > + case MMSYSERR_ALLOCATED: reason="specified resource is already
> > allocated"; break;
> > + case MMSYSERR_BADDEVICEID: reason="bad device id"; break;
> > + case MMSYSERR_NODRIVER: reason="no driver is present"; break;
> > + case MMSYSERR_NOMEM: reason="unable to allocate or lock
> > memory"; break;
> > + case WAVERR_BADFORMAT: reason="unsupported waveform-audio
> > format"; break;
> > + case WAVERR_SYNC: reason="device is synchronous";
> break;
> > + default:
> > + fprintf(stderr, "unknown(%d)\n", error);
> > + }
> > + if (reason)
> > + fprintf(stderr, "%s\n", reason);
> > +}
> > +#else
> > +# define dump_mmerror(func,error) ((void)0)
> > +#endif
>
> 1. It should be unconditional
why is that, the function is only used when debugging is turned on, and will
result in a compiler
warning if I leave it. Or do you mean that debug mode should always be on ?
>
> 2. switch (error) {
> case ...:
> default: return;
> }
> fprintf (...)
> 3. The lines are extra long
>
ok, will fix this
>
> It appears that s->silence is never used.
>
True, it's probably obsolete right now, so I'll remove it.
>
> > + format.nAvgBytesPerSec = (format.nSamplesPerSec & format.nChannels)
> <<
> > shift;
>
> ~format.nChannels was meant perhaps?
>
wow, interesting, thanks a lot. I'm surprised the thing still works though,
or if this is related to the Vista
issues?
>
> > + format.nBlockAlign = format.nChannels << shift;
> > + format.wBitsPerSample = 8 << shift;
>
> [Just checking - is wBitsPerSample is really sample or frame? Only ALSA, to
> the
> best of my knowledge makes clear distinction between the two]
>
I believe it is per channel sample only.
Documentation states it should be 8 or 16, except for floats which should
use 32 (and 0 for compressed formats)
http://msdn.microsoft.com/en-us/library/ms791274.aspx
Given that stereo float is supported, and that the documentation says it
"must" be 32 for float, I assume
it's channel per sample.
> > + }
>
> Indentation is off here.
>
ok, will fix these
>
> > +
> > + samples_size = format.nBlockAlign * conf.nb_samples;
> > + s->buffer_bytes = qemu_malloc( NUM_OUT_BUFFERS * samples_size );
>
> audio_calloc ought to be used here...
>
ok
>
> > + if (s->buffer_bytes == NULL) {
> > + waveOutClose( s->waveout );
> > + s->waveout = NULL;
> > + fprintf(stderr, "not enough memory for Windows audio
> > buffers\n");
> > + return -1;
> > + }
>
> Whose return value is indeed ought to be checked for NULL, but no
> message is necessary. Checks in audio_calloc once have proven to be
> useful when some FreeBSD returned bogus number of fragments(0) and
> consequently attempt was made to allocate 0 bytes for oss's pcm_buf.
>
I'm not sure we need to check anything. Given that waveOutReset() was called
before in the same function, the "buffers still playing error" shouldn't be
returned
by waveOutClose(), and any other error condition is hardly recoverable
anyway.
>
> > +
> > + audio_pcm_init_info (&hw->info, as);
> > + hw->samples = conf.nb_samples*2;
>
> This one I don't get, why is it unconditionally multiplied by 2?
>
Probably because the code has only been tested with 16-bit stereo :-)
I'll look into that. By the way I realize that 32-bit audio samples are not
supported by WinWave, the code should probably return an error for
S32 and U32 then.
>
>
> Asterisks are c++isplaced.
>
yes, will fix that while reformatting.
I'll send a new version of the patch in a few days. Thanks a lot for the
comments
[-- Attachment #2: Type: text/html, Size: 8774 bytes --]
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2009-06-12 9:03 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-06-11 0:15 [Qemu-devel] [PATCH] Add WinWave-based audio backend to Windows QEMU builds David Turner
2009-06-11 0:42 ` Anthony Liguori
2009-06-11 0:49 ` David Turner
2009-06-11 1:35 ` malc
2009-06-12 9:03 ` David Turner
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).