aboutsummaryrefslogtreecommitdiffstats
path: root/audio
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commit55f4e4a5ec657a017e3bf75299ad71fd1c968dd3 (patch)
tree550ce922ea0e125ac6a9738210ce2939bf2fe901 /audio
parent413f05aaf54fa08c0ae7e997327a4f4a473c0a8d (diff)
downloadexternal_qemu-55f4e4a5ec657a017e3bf75299ad71fd1c968dd3.zip
external_qemu-55f4e4a5ec657a017e3bf75299ad71fd1c968dd3.tar.gz
external_qemu-55f4e4a5ec657a017e3bf75299ad71fd1c968dd3.tar.bz2
Initial Contribution
Diffstat (limited to 'audio')
-rw-r--r--audio/alsaaudio.c215
-rw-r--r--audio/audio.c349
-rw-r--r--audio/audio.h11
-rw-r--r--audio/audio_int.h8
-rw-r--r--audio/audio_pt_int.c149
-rw-r--r--audio/audio_pt_int.h22
-rw-r--r--audio/audio_template.h15
-rw-r--r--audio/coreaudio.c696
-rw-r--r--audio/dsoundaudio.c2
-rw-r--r--audio/esdaudio.c692
-rw-r--r--audio/noaudio.c2
-rw-r--r--audio/ossaudio.c2
-rw-r--r--audio/sdlaudio.c241
-rw-r--r--audio/wavaudio.c265
-rw-r--r--audio/wavcapture.c6
-rw-r--r--audio/winaudio.c668
16 files changed, 2958 insertions, 385 deletions
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index 71e5235..d18705b 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -1,6 +1,7 @@
/*
* QEMU ALSA audio driver
*
+ * Copyright (c) 2008 The Android Open Source Project
* Copyright (c) 2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -26,6 +27,77 @@
#define AUDIO_CAP "alsa"
#include "audio_int.h"
+#include <dlfcn.h>
+#include <pthread.h>
+#include "android_debug.h"
+
+#define DEBUG 1
+
+#if DEBUG
+# include <stdio.h>
+# define D(...) VERBOSE_PRINT(audio,__VA_ARGS__)
+# define D_ACTIVE VERBOSE_CHECK(audio)
+# define O(...) VERBOSE_PRINT(audioout,__VA_ARGS__)
+# define I(...) VERBOSE_PRINT(audioin,__VA_ARGS__)
+#else
+# define D(...) ((void)0)
+# define D_ACTIVE 0
+# define O(...) ((void)0)
+# define I(...) ((void)0)
+#endif
+
+
+#define STRINGIFY_(x) #x
+#define STRINGIFY(x) STRINGIFY_(x)
+
+#define DYN_SYMBOLS \
+ DYN_FUNCTION(size_t,snd_pcm_sw_params_sizeof,(void)) \
+ DYN_FUNCTION(int,snd_pcm_hw_params_current,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)) \
+ DYN_FUNCTION(int,snd_pcm_sw_params_set_start_threshold,(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val)) \
+ DYN_FUNCTION(int,snd_pcm_sw_params,(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)) \
+ DYN_FUNCTION(int,snd_pcm_sw_params_current,(snd_pcm_t *pcm, snd_pcm_sw_params_t *params)) \
+ DYN_FUNCTION(size_t,snd_pcm_hw_params_sizeof,(void)) \
+ DYN_FUNCTION(int,snd_pcm_hw_params_any,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)) \
+ DYN_FUNCTION(int,snd_pcm_hw_params_set_access,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t _access)) \
+ DYN_FUNCTION(int,snd_pcm_hw_params_set_format,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val)) \
+ DYN_FUNCTION(int,snd_pcm_hw_params_set_rate_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir)) \
+ DYN_FUNCTION(int,snd_pcm_hw_params_set_channels_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val)) \
+ DYN_FUNCTION(int,snd_pcm_hw_params_set_buffer_time_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir)) \
+ DYN_FUNCTION(int,snd_pcm_hw_params,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)) \
+ DYN_FUNCTION(int,snd_pcm_hw_params_get_buffer_size,(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)) \
+ DYN_FUNCTION(int,snd_pcm_prepare,(snd_pcm_t *pcm)) \
+ DYN_FUNCTION(int,snd_pcm_hw_params_get_period_size,(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir)) \
+ DYN_FUNCTION(int,snd_pcm_hw_params_get_period_size_min,(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir)) \
+ DYN_FUNCTION(int,snd_pcm_hw_params_set_period_size,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val, int dir)) \
+ DYN_FUNCTION(int,snd_pcm_hw_params_get_buffer_size_min,(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val)) \
+ DYN_FUNCTION(int,snd_pcm_hw_params_set_buffer_size,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t val)) \
+ DYN_FUNCTION(int,snd_pcm_hw_params_set_period_time_near,(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir)) \
+ DYN_FUNCTION(snd_pcm_sframes_t,snd_pcm_avail_update,(snd_pcm_t *pcm)) \
+ DYN_FUNCTION(int,snd_pcm_drop,(snd_pcm_t *pcm)) \
+ DYN_FUNCTION(snd_pcm_sframes_t,snd_pcm_writei,(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)) \
+ DYN_FUNCTION(snd_pcm_sframes_t,snd_pcm_readi,(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)) \
+ DYN_FUNCTION(snd_pcm_state_t,snd_pcm_state,(snd_pcm_t *pcm)) \
+ DYN_FUNCTION(const char*,snd_strerror,(int errnum)) \
+ DYN_FUNCTION(int,snd_pcm_open,(snd_pcm_t **pcm, const char *name,snd_pcm_stream_t stream, int mode)) \
+ DYN_FUNCTION(int,snd_pcm_close,(snd_pcm_t *pcm)) \
+
+
+
+/* define pointers to library functions we're going to use */
+#define DYN_FUNCTION(ret,name,sig) \
+ static ret (*func_ ## name)sig;
+
+DYN_SYMBOLS
+
+#undef DYN_FUNCTION
+
+#define func_snd_pcm_hw_params_alloca(ptr) \
+ do { assert(ptr); *ptr = (snd_pcm_hw_params_t *) alloca(func_snd_pcm_hw_params_sizeof()); memset(*ptr, 0, func_snd_pcm_hw_params_sizeof()); } while (0)
+
+#define func_snd_pcm_sw_params_alloca(ptr) \
+ do { assert(ptr); *ptr = (snd_pcm_sw_params_t *) alloca(func_snd_pcm_sw_params_sizeof()); memset(*ptr, 0, func_snd_pcm_sw_params_sizeof()); } while (0)
+
+static void* alsa_lib;
typedef struct ALSAVoiceOut {
HWVoiceOut hw;
@@ -107,7 +179,7 @@ static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...)
AUD_vlog (AUDIO_CAP, fmt, ap);
va_end (ap);
- AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
+ AUD_log (AUDIO_CAP, "Reason: %s\n", func_snd_strerror (err));
}
static void GCC_FMT_ATTR (3, 4) alsa_logerr2 (
@@ -125,12 +197,12 @@ static void GCC_FMT_ATTR (3, 4) alsa_logerr2 (
AUD_vlog (AUDIO_CAP, fmt, ap);
va_end (ap);
- AUD_log (AUDIO_CAP, "Reason: %s\n", snd_strerror (err));
+ AUD_log (AUDIO_CAP, "Reason: %s\n", func_snd_strerror (err));
}
static void alsa_anal_close (snd_pcm_t **handlep)
{
- int err = snd_pcm_close (*handlep);
+ int err = func_snd_pcm_close (*handlep);
if (err) {
alsa_logerr (err, "Failed to close PCM handle %p\n", *handlep);
}
@@ -228,16 +300,16 @@ static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
int err;
snd_pcm_sw_params_t *sw_params;
- snd_pcm_sw_params_alloca (&sw_params);
+ func_snd_pcm_sw_params_alloca (&sw_params);
- err = snd_pcm_sw_params_current (handle, sw_params);
+ err = func_snd_pcm_sw_params_current (handle, sw_params);
if (err < 0) {
dolog ("Could not fully initialize DAC\n");
alsa_logerr (err, "Failed to get current software parameters\n");
return;
}
- err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold);
+ err = func_snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold);
if (err < 0) {
dolog ("Could not fully initialize DAC\n");
alsa_logerr (err, "Failed to set software threshold to %ld\n",
@@ -245,7 +317,7 @@ static void alsa_set_threshold (snd_pcm_t *handle, snd_pcm_uframes_t threshold)
return;
}
- err = snd_pcm_sw_params (handle, sw_params);
+ err = func_snd_pcm_sw_params (handle, sw_params);
if (err < 0) {
dolog ("Could not fully initialize DAC\n");
alsa_logerr (err, "Failed to set software parameters\n");
@@ -269,9 +341,9 @@ static int alsa_open (int in, struct alsa_params_req *req,
buffer_size = req->buffer_size;
nchannels = req->nchannels;
- snd_pcm_hw_params_alloca (&hw_params);
+ func_snd_pcm_hw_params_alloca (&hw_params);
- err = snd_pcm_open (
+ err = func_snd_pcm_open (
&handle,
pcm_name,
in ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
@@ -282,13 +354,13 @@ static int alsa_open (int in, struct alsa_params_req *req,
return -1;
}
- err = snd_pcm_hw_params_any (handle, hw_params);
+ err = func_snd_pcm_hw_params_any (handle, hw_params);
if (err < 0) {
alsa_logerr2 (err, typ, "Failed to initialize hardware parameters\n");
goto err;
}
- err = snd_pcm_hw_params_set_access (
+ err = func_snd_pcm_hw_params_set_access (
handle,
hw_params,
SND_PCM_ACCESS_RW_INTERLEAVED
@@ -298,22 +370,22 @@ static int alsa_open (int in, struct alsa_params_req *req,
goto err;
}
- err = snd_pcm_hw_params_set_format (handle, hw_params, req->fmt);
+ err = func_snd_pcm_hw_params_set_format (handle, hw_params, req->fmt);
if (err < 0) {
alsa_logerr2 (err, typ, "Failed to set format %d\n", req->fmt);
goto err;
}
- err = snd_pcm_hw_params_set_rate_near (handle, hw_params, &freq, 0);
+ err = func_snd_pcm_hw_params_set_rate_near (handle, hw_params, (unsigned*)&freq, 0);
if (err < 0) {
alsa_logerr2 (err, typ, "Failed to set frequency %d\n", req->freq);
goto err;
}
- err = snd_pcm_hw_params_set_channels_near (
+ err = func_snd_pcm_hw_params_set_channels_near (
handle,
hw_params,
- &nchannels
+ (unsigned*)&nchannels
);
if (err < 0) {
alsa_logerr2 (err, typ, "Failed to set number of channels %d\n",
@@ -338,7 +410,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
if (buffer_size) {
if ((in && conf.size_in_usec_in) || (!in && conf.size_in_usec_out)) {
if (period_size) {
- err = snd_pcm_hw_params_set_period_time_near (
+ err = func_snd_pcm_hw_params_set_period_time_near (
handle,
hw_params,
&period_size,
@@ -352,7 +424,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
}
}
- err = snd_pcm_hw_params_set_buffer_time_near (
+ err = func_snd_pcm_hw_params_set_buffer_time_near (
handle,
hw_params,
&buffer_size,
@@ -374,7 +446,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
minval = period_size;
dir = 0;
- err = snd_pcm_hw_params_get_period_size_min (
+ err = func_snd_pcm_hw_params_get_period_size_min (
hw_params,
&minval,
&dir
@@ -400,7 +472,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
}
}
- err = snd_pcm_hw_params_set_period_size (
+ err = func_snd_pcm_hw_params_set_period_size (
handle,
hw_params,
period_size,
@@ -414,7 +486,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
}
minval = buffer_size;
- err = snd_pcm_hw_params_get_buffer_size_min (
+ err = func_snd_pcm_hw_params_get_buffer_size_min (
hw_params,
&minval
);
@@ -438,7 +510,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
}
}
- err = snd_pcm_hw_params_set_buffer_size (
+ err = func_snd_pcm_hw_params_set_buffer_size (
handle,
hw_params,
buffer_size
@@ -454,19 +526,19 @@ static int alsa_open (int in, struct alsa_params_req *req,
dolog ("warning: Buffer size is not set\n");
}
- err = snd_pcm_hw_params (handle, hw_params);
+ err = func_snd_pcm_hw_params (handle, hw_params);
if (err < 0) {
alsa_logerr2 (err, typ, "Failed to apply audio parameters\n");
goto err;
}
- err = snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size);
+ err = func_snd_pcm_hw_params_get_buffer_size (hw_params, &obt_buffer_size);
if (err < 0) {
alsa_logerr2 (err, typ, "Failed to get buffer size\n");
goto err;
}
- err = snd_pcm_prepare (handle);
+ err = func_snd_pcm_prepare (handle);
if (err < 0) {
alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle);
goto err;
@@ -511,7 +583,7 @@ static int alsa_open (int in, struct alsa_params_req *req,
static int alsa_recover (snd_pcm_t *handle)
{
- int err = snd_pcm_prepare (handle);
+ int err = func_snd_pcm_prepare (handle);
if (err < 0) {
alsa_logerr (err, "Failed to prepare handle %p\n", handle);
return -1;
@@ -523,11 +595,11 @@ static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
{
snd_pcm_sframes_t avail;
- avail = snd_pcm_avail_update (handle);
+ avail = func_snd_pcm_avail_update (handle);
if (avail < 0) {
if (avail == -EPIPE) {
if (!alsa_recover (handle)) {
- avail = snd_pcm_avail_update (handle);
+ avail = func_snd_pcm_avail_update (handle);
}
}
@@ -575,7 +647,7 @@ static int alsa_run_out (HWVoiceOut *hw)
hw->clip (dst, src, len);
while (len) {
- written = snd_pcm_writei (alsa->handle, dst, len);
+ written = func_snd_pcm_writei (alsa->handle, dst, len);
if (written <= 0) {
switch (written) {
@@ -639,10 +711,14 @@ static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as)
struct alsa_params_obt obt;
audfmt_e effective_fmt;
int endianness;
- int err;
+ int err, result = -1;
snd_pcm_t *handle;
audsettings_t obt_as;
+ /* shut alsa debug spew */
+ if (!D_ACTIVE)
+ stdio_disable();
+
req.fmt = aud_to_alsafmt (as->fmt);
req.freq = as->freq;
req.nchannels = as->nchannels;
@@ -650,13 +726,13 @@ static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as)
req.buffer_size = conf.buffer_size_out;
if (alsa_open (0, &req, &obt, &handle)) {
- return -1;
+ goto Exit;
}
err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
if (err) {
alsa_anal_close (&handle);
- return -1;
+ goto Exit;
}
obt_as.freq = obt.freq;
@@ -672,11 +748,17 @@ static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as)
dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift);
alsa_anal_close (&handle);
- return -1;
+ goto Exit;
}
alsa->handle = handle;
- return 0;
+ result = 0; /* success */
+
+Exit:
+ if (!D_ACTIVE)
+ stdio_enable();
+
+ return result;
}
static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause)
@@ -684,14 +766,14 @@ static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause)
int err;
if (pause) {
- err = snd_pcm_drop (handle);
+ err = func_snd_pcm_drop (handle);
if (err < 0) {
alsa_logerr (err, "Could not stop %s\n", typ);
return -1;
}
}
else {
- err = snd_pcm_prepare (handle);
+ err = func_snd_pcm_prepare (handle);
if (err < 0) {
alsa_logerr (err, "Could not prepare handle for %s\n", typ);
return -1;
@@ -724,11 +806,15 @@ static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as)
struct alsa_params_req req;
struct alsa_params_obt obt;
int endianness;
- int err;
+ int err, result = -1;
audfmt_e effective_fmt;
snd_pcm_t *handle;
audsettings_t obt_as;
+ /* shut alsa debug spew */
+ if (!D_ACTIVE)
+ stdio_disable();
+
req.fmt = aud_to_alsafmt (as->fmt);
req.freq = as->freq;
req.nchannels = as->nchannels;
@@ -736,13 +822,13 @@ static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as)
req.buffer_size = conf.buffer_size_in;
if (alsa_open (1, &req, &obt, &handle)) {
- return -1;
+ goto Exit;
}
err = alsa_to_audfmt (obt.fmt, &effective_fmt, &endianness);
if (err) {
alsa_anal_close (&handle);
- return -1;
+ goto Exit;
}
obt_as.freq = obt.freq;
@@ -758,11 +844,17 @@ static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as)
dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
hw->samples, 1 << hw->info.shift);
alsa_anal_close (&handle);
- return -1;
+ goto Exit;
}
alsa->handle = handle;
- return 0;
+ result = 0; /* success */
+
+Exit:
+ if (!D_ACTIVE)
+ stdio_enable();
+
+ return result;
}
static void alsa_fini_in (HWVoiceIn *hw)
@@ -805,7 +897,7 @@ static int alsa_run_in (HWVoiceIn *hw)
return 0;
}
- if (!avail && (snd_pcm_state (alsa->handle) == SND_PCM_STATE_PREPARED)) {
+ if (!avail && (func_snd_pcm_state (alsa->handle) == SND_PCM_STATE_PREPARED)) {
avail = hw->samples;
}
@@ -834,7 +926,7 @@ static int alsa_run_in (HWVoiceIn *hw)
dst = hw->conv_buf + bufs[i].add;
while (len) {
- nread = snd_pcm_readi (alsa->handle, src, len);
+ nread = func_snd_pcm_readi (alsa->handle, src, len);
if (nread <= 0) {
switch (nread) {
@@ -907,11 +999,46 @@ static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
static void *alsa_audio_init (void)
{
- return &conf;
+ void* result = NULL;
+
+ alsa_lib = dlopen( "libasound.so", RTLD_NOW );
+ if (alsa_lib == NULL)
+ alsa_lib = dlopen( "libasound.so.2", RTLD_NOW );
+
+ if (alsa_lib == NULL) {
+ ldebug("could not find libasound on this system\n");
+ goto Exit;
+ }
+
+#undef DYN_FUNCTION
+#define DYN_FUNCTION(ret,name,sig) \
+ do { \
+ (func_ ##name) = dlsym( alsa_lib, STRINGIFY(name) ); \
+ if ((func_##name) == NULL) { \
+ ldebug("could not find %s in libasound\n", STRINGIFY(name)); \
+ goto Fail; \
+ } \
+ } while (0);
+
+ DYN_SYMBOLS
+
+ result = &conf;
+ goto Exit;
+
+Fail:
+ ldebug("%s: failed to open library\n", __FUNCTION__);
+ dlclose(alsa_lib);
+
+Exit:
+ return result;
}
static void alsa_audio_fini (void *opaque)
{
+ if (alsa_lib != NULL) {
+ dlclose(alsa_lib);
+ alsa_lib = NULL;
+ }
(void) opaque;
}
@@ -961,7 +1088,7 @@ static struct audio_pcm_ops alsa_pcm_ops = {
struct audio_driver alsa_audio_driver = {
INIT_FIELD (name = ) "alsa",
- INIT_FIELD (descr = ) "ALSA http://www.alsa-project.org",
+ INIT_FIELD (descr = ) "ALSA audio (www.alsa-project.org)",
INIT_FIELD (options = ) alsa_options,
INIT_FIELD (init = ) alsa_audio_init,
INIT_FIELD (fini = ) alsa_audio_fini,
diff --git a/audio/audio.c b/audio/audio.c
index 8e7af1a..4ba1f7d 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -1,6 +1,7 @@
/*
* QEMU Audio subsystem
*
+ * Copyright (c) 2007-2008 The Android Open Source Project
* Copyright (c) 2003-2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -25,6 +26,8 @@
#define AUDIO_CAP "audio"
#include "audio_int.h"
+#include "android_utils.h"
+#include "android.h"
/* #define DEBUG_PLIVE */
/* #define DEBUG_LIVE */
@@ -34,8 +37,8 @@
#define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown"
static struct audio_driver *drvtab[] = {
-#ifdef CONFIG_OSS
- &oss_audio_driver,
+#ifdef CONFIG_ESD
+ &esd_audio_driver,
#endif
#ifdef CONFIG_ALSA
&alsa_audio_driver,
@@ -49,13 +52,91 @@ static struct audio_driver *drvtab[] = {
#ifdef CONFIG_FMOD
&fmod_audio_driver,
#endif
+#ifdef CONFIG_WINAUDIO
+ &win_audio_driver,
+#endif
#ifdef CONFIG_SDL
&sdl_audio_driver,
#endif
+#ifdef CONFIG_OSS
+ &oss_audio_driver,
+#endif
&no_audio_driver,
+#if 0 /* disabled WAV audio for now - until we find a user-friendly way to use it */
&wav_audio_driver
+#endif
};
+
+int
+audio_get_backend_count( int is_input )
+{
+ int nn, count = 0;
+
+ for (nn = 0; nn < sizeof(drvtab)/sizeof(drvtab[0]); nn++)
+ {
+ if (is_input) {
+ if ( drvtab[nn]->max_voices_in > 0 )
+ count += 1;
+ } else {
+ if ( drvtab[nn]->max_voices_out > 0 )
+ count += 1;
+ }
+ }
+ return count;
+}
+
+const char*
+audio_get_backend_name( int is_input, int index, const char* *pinfo )
+{
+ int nn;
+
+ index += 1;
+ for (nn = 0; nn < sizeof(drvtab)/sizeof(drvtab[0]); nn++)
+ {
+ if (is_input) {
+ if ( drvtab[nn]->max_voices_in > 0 ) {
+ if ( --index == 0 ) {
+ *pinfo = drvtab[nn]->descr;
+ return drvtab[nn]->name;
+ }
+ }
+ } else {
+ if ( drvtab[nn]->max_voices_out > 0 ) {
+ if ( --index == 0 ) {
+ *pinfo = drvtab[nn]->descr;
+ return drvtab[nn]->name;
+ }
+ }
+ }
+ }
+ *pinfo = NULL;
+ return NULL;
+}
+
+
+int
+audio_check_backend_name( int is_input, const char* name )
+{
+ int nn;
+
+ for (nn = 0; nn < sizeof(drvtab)/sizeof(drvtab[0]); nn++)
+ {
+ if ( !strcmp(drvtab[nn]->name, name) ) {
+ if (is_input) {
+ if (drvtab[nn]->max_voices_in > 0)
+ return 1;
+ } else {
+ if (drvtab[nn]->max_voices_out > 0)
+ return 1;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+
struct fixed_settings {
int enabled;
int nb_voices;
@@ -100,7 +181,7 @@ static struct {
0 /* log_to_monitor */
};
-static AudioState glob_audio_state;
+AudioState glob_audio_state;
volume_t nominal_volume = {
0,
@@ -303,6 +384,10 @@ static const char *audio_get_conf_str (const char *key,
}
}
+/* defined in android_sdl.c */
+extern void dprintn(const char* fmt, ...);
+extern void dprintnv(const char* fmt, va_list args);
+
void AUD_vlog (const char *cap, const char *fmt, va_list ap)
{
if (conf.log_to_monitor) {
@@ -313,11 +398,14 @@ void AUD_vlog (const char *cap, const char *fmt, va_list ap)
term_vprintf (fmt, ap);
}
else {
+ if (!VERBOSE_CHECK(audio))
+ return;
+
if (cap) {
- fprintf (stderr, "%s: ", cap);
+ dprintn("%s: ", cap);
}
- vfprintf (stderr, fmt, ap);
+ dprintnv(fmt, ap);
}
}
@@ -605,11 +693,11 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
}
if (info->sign) {
- memset (buf, len << info->shift, 0x00);
+ memset (buf, 0x80, len << info->shift);
}
else {
if (info->bits == 8) {
- memset (buf, len << info->shift, 0x80);
+ memset (buf, 0x80, len << info->shift);
}
else {
int i;
@@ -1024,7 +1112,9 @@ int AUD_write (SWVoiceOut *sw, void *buf, int size)
return 0;
}
- bytes = sw->hw->pcm_ops->write (sw, buf, size);
+ BEGIN_NOSIGALRM
+ bytes = sw->hw->pcm_ops->write (sw, buf, size);
+ END_NOSIGALRM
return bytes;
}
@@ -1042,7 +1132,9 @@ int AUD_read (SWVoiceIn *sw, void *buf, int size)
return 0;
}
- bytes = sw->hw->pcm_ops->read (sw, buf, size);
+ BEGIN_NOSIGALRM
+ bytes = sw->hw->pcm_ops->read (sw, buf, size);
+ END_NOSIGALRM
return bytes;
}
@@ -1068,7 +1160,9 @@ void AUD_set_active_out (SWVoiceOut *sw, int on)
hw->pending_disable = 0;
if (!hw->enabled) {
hw->enabled = 1;
- hw->pcm_ops->ctl_out (hw, VOICE_ENABLE);
+ BEGIN_NOSIGALRM
+ hw->pcm_ops->ctl_out (hw, VOICE_ENABLE);
+ END_NOSIGALRM
}
}
else {
@@ -1109,7 +1203,9 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
if (on) {
if (!hw->enabled) {
hw->enabled = 1;
- hw->pcm_ops->ctl_in (hw, VOICE_ENABLE);
+ BEGIN_NOSIGALRM
+ hw->pcm_ops->ctl_in (hw, VOICE_ENABLE);
+ END_NOSIGALRM
}
sw->total_hw_samples_acquired = hw->total_samples_captured;
}
@@ -1124,7 +1220,9 @@ void AUD_set_active_in (SWVoiceIn *sw, int on)
if (nb_active == 1) {
hw->enabled = 0;
- hw->pcm_ops->ctl_in (hw, VOICE_DISABLE);
+ BEGIN_NOSIGALRM
+ hw->pcm_ops->ctl_in (hw, VOICE_DISABLE);
+ END_NOSIGALRM
}
}
}
@@ -1244,7 +1342,9 @@ static void audio_run_out (AudioState *s)
#endif
hw->enabled = 0;
hw->pending_disable = 0;
- hw->pcm_ops->ctl_out (hw, VOICE_DISABLE);
+ BEGIN_NOSIGALRM
+ hw->pcm_ops->ctl_out (hw, VOICE_DISABLE);
+ END_NOSIGALRM
for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
sc->sw.active = 0;
audio_recalc_and_notify_capture (sc->cap);
@@ -1265,7 +1365,9 @@ static void audio_run_out (AudioState *s)
}
prev_rpos = hw->rpos;
- played = hw->pcm_ops->run_out (hw);
+ BEGIN_NOSIGALRM
+ played = hw->pcm_ops->run_out (hw);
+ END_NOSIGALRM
if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) {
dolog ("hw->rpos=%d hw->samples=%d played=%d\n",
hw->rpos, hw->samples, played);
@@ -1334,7 +1436,9 @@ static void audio_run_in (AudioState *s)
SWVoiceIn *sw;
int captured, min;
- captured = hw->pcm_ops->run_in (hw);
+ BEGIN_NOSIGALRM
+ captured = hw->pcm_ops->run_in (hw);
+ END_NOSIGALRM
min = audio_pcm_hw_find_min_in (hw);
hw->total_samples_captured += captured - min;
@@ -1404,8 +1508,36 @@ static void audio_run_capture (AudioState *s)
static void audio_timer (void *opaque)
{
- AudioState *s = opaque;
-
+ AudioState* s = opaque;
+#if 0
+#define MAX_DIFFS 1000
+ int64_t now = qemu_get_clock(vm_clock);
+ static int64_t last = 0;
+ static float diffs[MAX_DIFFS];
+ static int num_diffs;
+
+ if (last == 0)
+ last = now;
+ else {
+ diffs[num_diffs] = (float)((now-last)/1e6); /* last diff in ms */
+ if (++num_diffs == MAX_DIFFS) {
+ double min_diff = 1e6, max_diff = -1e6;
+ double all_diff = 0.;
+ int nn;
+
+ for (nn = 0; nn < num_diffs; nn++) {
+ if (diffs[nn] < min_diff) min_diff = diffs[nn];
+ if (diffs[nn] > max_diff) max_diff = diffs[nn];
+ all_diff += diffs[nn];
+ }
+ all_diff *= 1.0/num_diffs;
+ printf("audio timer: min_diff=%6.2g max_diff=%6.2g avg_diff=%6.2g samples=%d\n",
+ min_diff, max_diff, all_diff, num_diffs);
+ num_diffs = 0;
+ }
+ }
+ last = now;
+#endif
audio_run_out (s);
audio_run_in (s);
audio_run_capture (s);
@@ -1530,17 +1662,43 @@ void AUD_help (void)
);
}
-static int audio_driver_init (AudioState *s, struct audio_driver *drv)
+static int audio_driver_init (AudioState *s, struct audio_driver *drv, int out)
{
+ void* opaque;
+
if (drv->options) {
audio_process_options (drv->name, drv->options);
}
- s->drv_opaque = drv->init ();
- if (s->drv_opaque) {
+ /* is the driver already initialized ? */
+ if (out) {
+ if (drv == s->drv_in) {
+ s->drv_out = drv;
+ s->drv_out_opaque = s->drv_in_opaque;
+ return 0;
+ }
+ } else {
+ if (drv == s->drv_out) {
+ s->drv_in = drv;
+ s->drv_in_opaque = s->drv_out_opaque;
+ return 0;
+ }
+ }
+
+ BEGIN_NOSIGALRM
+ opaque = drv->init();
+ END_NOSIGALRM
+
+ if (opaque != NULL) {
audio_init_nb_voices_out (s, drv);
audio_init_nb_voices_in (s, drv);
- s->drv = drv;
+ if (out) {
+ s->drv_out = drv;
+ s->drv_out_opaque = opaque;
+ } else {
+ s->drv_in = drv;
+ s->drv_in_opaque = opaque;
+ }
return 0;
}
else {
@@ -1556,21 +1714,30 @@ static void audio_vm_change_state_handler (void *opaque, int running)
HWVoiceIn *hwi = NULL;
int op = running ? VOICE_ENABLE : VOICE_DISABLE;
- while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) {
- hwo->pcm_ops->ctl_out (hwo, op);
- }
+ BEGIN_NOSIGALRM
+ while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) {
+ hwo->pcm_ops->ctl_out (hwo, op);
+ }
- while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) {
- hwi->pcm_ops->ctl_in (hwi, op);
- }
+ while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) {
+ hwi->pcm_ops->ctl_in (hwi, op);
+ }
+ END_NOSIGALRM
}
+// to make sure audio_atexit() is only called once
+static int initialized = 0;
+
static void audio_atexit (void)
{
AudioState *s = &glob_audio_state;
HWVoiceOut *hwo = NULL;
HWVoiceIn *hwi = NULL;
+ if (!initialized) return;
+ initialized = 0;
+
+ BEGIN_NOSIGALRM
while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) {
SWVoiceCap *sc;
@@ -1592,9 +1759,13 @@ static void audio_atexit (void)
hwi->pcm_ops->fini_in (hwi);
}
- if (s->drv) {
- s->drv->fini (s->drv_opaque);
+ if (s->drv_in) {
+ s->drv_in->fini (s->drv_in_opaque);
}
+ if (s->drv_out) {
+ s->drv_out->fini (s->drv_out_opaque);
+ }
+ END_NOSIGALRM
}
static void audio_save (QEMUFile *f, void *opaque)
@@ -1630,11 +1801,70 @@ void AUD_remove_card (QEMUSoundCard *card)
qemu_free (card->name);
}
+static int
+find_audio_driver( AudioState* s, int out )
+{
+ int i, done = 0, def;
+ const char* envname;
+ const char* drvname;
+ struct audio_driver* drv = NULL;
+ const char* drvtype = out ? "output" : "input";
+
+ envname = out ? "QEMU_AUDIO_OUT_DRV" : "QEMU_AUDIO_IN_DRV";
+ drvname = audio_get_conf_str(envname, NULL, &def);
+ if (drvname == NULL) {
+ drvname = audio_get_conf_str("QEMU_AUDIO_DRV", NULL, &def);
+ }
+
+ if (drvname != NULL) { /* look for a specific driver */
+ for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
+ if (!strcmp (drvname, drvtab[i]->name)) {
+ drv = drvtab[i];
+ break;
+ }
+ }
+ }
+
+ if (drv != NULL) {
+ done = !audio_driver_init (s, drv, out);
+ if (!done) {
+ dolog ("Could not initialize '%s' %s audio backend, trying default one.\n",
+ drvname, drvtype);
+ dolog ("Run with -qemu -audio-help to list available backends\n");
+ drv = NULL;
+ }
+ }
+
+ if (!drv) {
+ for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
+ if (drvtab[i]->can_be_default) {
+ drv = drvtab[i];
+ done = !audio_driver_init (s, drv, out);
+ if (done)
+ break;
+ }
+ }
+ }
+
+ if (!done) {
+ drv = &no_audio_driver;
+ done = !audio_driver_init (s, drv, out);
+ if (!done) {
+ /* this should never happen */
+ dolog ("Could not initialize audio subsystem\n");
+ return -1;
+ }
+ dolog ("warning: Could not find suitable audio %s backend\n", drvtype);
+ }
+
+ if (VERBOSE_CHECK(init))
+ dprint("using '%s' audio %s backend", drv->name, drvtype );
+ return 0;
+}
+
+
AudioState *AUD_init (void)
{
- size_t i;
- int done = 0;
- const char *drvname;
AudioState *s = &glob_audio_state;
LIST_INIT (&s->hw_head_out);
@@ -1665,47 +1895,9 @@ AudioState *AUD_init (void)
s->nb_hw_voices_in = 0;
}
+ if ( find_audio_driver (s, 0) == 0 &&
+ find_audio_driver (s, 1) == 0 )
{
- int def;
- drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def);
- }
-
- if (drvname) {
- int found = 0;
-
- for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
- if (!strcmp (drvname, drvtab[i]->name)) {
- done = !audio_driver_init (s, drvtab[i]);
- found = 1;
- break;
- }
- }
-
- if (!found) {
- dolog ("Unknown audio driver `%s'\n", drvname);
- dolog ("Run with -audio-help to list available drivers\n");
- }
- }
-
- if (!done) {
- for (i = 0; !done && i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
- if (drvtab[i]->can_be_default) {
- done = !audio_driver_init (s, drvtab[i]);
- }
- }
- }
-
- if (!done) {
- done = !audio_driver_init (s, &no_audio_driver);
- if (!done) {
- dolog ("Could not initialize audio subsystem\n");
- }
- else {
- dolog ("warning: Using timer based audio emulation\n");
- }
- }
-
- if (done) {
VMChangeStateEntry *e;
if (conf.period.hz <= 0) {
@@ -1731,12 +1923,20 @@ AudioState *AUD_init (void)
return NULL;
}
+ initialized = 1;
+
LIST_INIT (&s->card_head);
register_savevm ("audio", 0, 1, audio_save, audio_load, s);
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
return s;
}
+// this was added to work around a deadlock in SDL when quitting
+void AUD_cleanup()
+{
+ audio_atexit();
+}
+
CaptureVoiceOut *AUD_add_capture (
AudioState *s,
audsettings_t *as,
@@ -1845,21 +2045,16 @@ void AUD_del_capture (CaptureVoiceOut *cap, void *cb_opaque)
if (!cap->cb_head.lh_first) {
SWVoiceOut *sw = cap->hw.sw_head.lh_first, *sw1;
-
while (sw) {
- SWVoiceCap *sc = (SWVoiceCap *) sw;
#ifdef DEBUG_CAPTURE
dolog ("freeing %s\n", sw->name);
#endif
-
sw1 = sw->entries.le_next;
if (sw->rate) {
st_rate_stop (sw->rate);
sw->rate = NULL;
}
LIST_REMOVE (sw, entries);
- LIST_REMOVE (sc, entries);
- qemu_free (sc);
sw = sw1;
}
LIST_REMOVE (cap, entries);
diff --git a/audio/audio.h b/audio/audio.h
index c097f39..cc7aa04 100644
--- a/audio/audio.h
+++ b/audio/audio.h
@@ -93,6 +93,8 @@ void AUD_log (const char *cap, const char *fmt, ...)
#endif
;
+extern AudioState glob_audio_state;
+
AudioState *AUD_init (void);
void AUD_help (void);
void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card);
@@ -166,4 +168,13 @@ uint32_t lsbindex (uint32_t u);
#define audio_MAX(a, b) ((a)<(b)?(b):(a))
#endif
+extern int
+audio_get_backend_count( int is_input );
+
+extern const char*
+audio_get_backend_name( int is_input, int index, const char* *pinfo );
+
+extern int
+audio_check_backend_name( int is_input, const char* name );
+
#endif /* audio.h */
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 1a15d4c..099bcbd 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -182,8 +182,10 @@ struct SWVoiceCap {
};
struct AudioState {
- struct audio_driver *drv;
- void *drv_opaque;
+ struct audio_driver* drv_in;
+ void* drv_in_opaque;
+ struct audio_driver* drv_out;
+ void* drv_out_opaque;
QEMUTimer *ts;
LIST_HEAD (card_listhead, QEMUSoundCard) card_head;
@@ -197,8 +199,10 @@ struct AudioState {
extern struct audio_driver no_audio_driver;
extern struct audio_driver oss_audio_driver;
extern struct audio_driver sdl_audio_driver;
+extern struct audio_driver win_audio_driver;
extern struct audio_driver wav_audio_driver;
extern struct audio_driver fmod_audio_driver;
+extern struct audio_driver esd_audio_driver;
extern struct audio_driver alsa_audio_driver;
extern struct audio_driver coreaudio_audio_driver;
extern struct audio_driver dsound_audio_driver;
diff --git a/audio/audio_pt_int.c b/audio/audio_pt_int.c
new file mode 100644
index 0000000..3f01762
--- /dev/null
+++ b/audio/audio_pt_int.c
@@ -0,0 +1,149 @@
+#include "vl.h"
+#include "audio.h"
+
+#define AUDIO_CAP "audio-pt"
+
+#include "audio_int.h"
+#include "audio_pt_int.h"
+
+static void logerr (struct audio_pt *pt, int err, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (pt->drv, fmt, ap);
+ va_end (ap);
+
+ AUD_log (NULL, "\n");
+ AUD_log (pt->drv, "Reason: %s\n", strerror (err));
+}
+
+int audio_pt_init (struct audio_pt *p, void *(*func) (void *),
+ void *opaque, const char *drv, const char *cap)
+{
+ int err, err2;
+ const char *efunc;
+
+ p->drv = drv;
+
+ err = pthread_mutex_init (&p->mutex, NULL);
+ if (err) {
+ efunc = "pthread_mutex_init";
+ goto err0;
+ }
+
+ err = pthread_cond_init (&p->cond, NULL);
+ if (err) {
+ efunc = "pthread_cond_init";
+ goto err1;
+ }
+
+ err = pthread_create (&p->thread, NULL, func, opaque);
+ if (err) {
+ efunc = "pthread_create";
+ goto err2;
+ }
+
+ return 0;
+
+ err2:
+ err2 = pthread_cond_destroy (&p->cond);
+ if (err2) {
+ logerr (p, err2, "%s(%s): pthread_cond_destroy failed", cap, AUDIO_FUNC);
+ }
+
+ err1:
+ err2 = pthread_mutex_destroy (&p->mutex);
+ if (err2) {
+ logerr (p, err2, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_FUNC);
+ }
+
+ err0:
+ logerr (p, err, "%s(%s): %s failed", cap, AUDIO_FUNC, efunc);
+ return -1;
+}
+
+int audio_pt_fini (struct audio_pt *p, const char *cap)
+{
+ int err, ret = 0;
+
+ err = pthread_cond_destroy (&p->cond);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_cond_destroy failed", cap, AUDIO_FUNC);
+ ret = -1;
+ }
+
+ err = pthread_mutex_destroy (&p->mutex);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_mutex_destroy failed", cap, AUDIO_FUNC);
+ ret = -1;
+ }
+ return ret;
+}
+
+int audio_pt_lock (struct audio_pt *p, const char *cap)
+{
+ int err;
+
+ err = pthread_mutex_lock (&p->mutex);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_mutex_lock failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ return 0;
+}
+
+int audio_pt_unlock (struct audio_pt *p, const char *cap)
+{
+ int err;
+
+ err = pthread_mutex_unlock (&p->mutex);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_mutex_unlock failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ return 0;
+}
+
+int audio_pt_wait (struct audio_pt *p, const char *cap)
+{
+ int err;
+
+ err = pthread_cond_wait (&p->cond, &p->mutex);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_cond_wait failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ return 0;
+}
+
+int audio_pt_unlock_and_signal (struct audio_pt *p, const char *cap)
+{
+ int err;
+
+ err = pthread_mutex_unlock (&p->mutex);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_mutex_unlock failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ err = pthread_cond_signal (&p->cond);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_cond_signal failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ return 0;
+}
+
+int audio_pt_join (struct audio_pt *p, void **arg, const char *cap)
+{
+ int err;
+ void *ret;
+
+ err = pthread_join (p->thread, &ret);
+ if (err) {
+ logerr (p, err, "%s(%s): pthread_join failed", cap, AUDIO_FUNC);
+ return -1;
+ }
+ *arg = ret;
+ return 0;
+}
diff --git a/audio/audio_pt_int.h b/audio/audio_pt_int.h
new file mode 100644
index 0000000..0dfff76
--- /dev/null
+++ b/audio/audio_pt_int.h
@@ -0,0 +1,22 @@
+#ifndef QEMU_AUDIO_PT_INT_H
+#define QEMU_AUDIO_PT_INT_H
+
+#include <pthread.h>
+
+struct audio_pt {
+ const char *drv;
+ pthread_t thread;
+ pthread_cond_t cond;
+ pthread_mutex_t mutex;
+};
+
+int audio_pt_init (struct audio_pt *, void *(*) (void *), void *,
+ const char *, const char *);
+int audio_pt_fini (struct audio_pt *, const char *);
+int audio_pt_lock (struct audio_pt *, const char *);
+int audio_pt_unlock (struct audio_pt *, const char *);
+int audio_pt_wait (struct audio_pt *, const char *);
+int audio_pt_unlock_and_signal (struct audio_pt *, const char *);
+int audio_pt_join (struct audio_pt *, void **, const char *);
+
+#endif /* audio_pt_int.h */
diff --git a/audio/audio_template.h b/audio/audio_template.h
index 13e1c3e..4575a56 100644
--- a/audio/audio_template.h
+++ b/audio/audio_template.h
@@ -205,7 +205,9 @@ static void glue (audio_pcm_hw_gc_, TYPE) (AudioState *s, HW **hwp)
LIST_REMOVE (hw, entries);
glue (s->nb_hw_voices_, TYPE) += 1;
glue (audio_pcm_hw_free_resources_ ,TYPE) (hw);
+ BEGIN_NOSIGALRM
glue (hw->pcm_ops->fini_, TYPE) (hw);
+ END_NOSIGALRM
qemu_free (hw);
*hwp = NULL;
}
@@ -243,7 +245,8 @@ static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as)
{
HW *hw;
- struct audio_driver *drv = s->drv;
+ struct audio_driver *drv = glue(s->drv_, TYPE);
+ int err;
if (!glue (s->nb_hw_voices_, TYPE)) {
return NULL;
@@ -271,9 +274,11 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as)
#ifdef DAC
LIST_INIT (&hw->cap_head);
#endif
- if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
+ BEGIN_NOSIGALRM
+ err = glue (hw->pcm_ops->init_, TYPE) (hw, as);
+ END_NOSIGALRM
+ if (err)
goto err0;
- }
if (audio_bug (AUDIO_FUNC, hw->samples <= 0)) {
dolog ("hw->samples=%d\n", hw->samples);
@@ -302,7 +307,9 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as)
return hw;
err1:
+ BEGIN_NOSIGALRM
glue (hw->pcm_ops->fini_, TYPE) (hw);
+ END_NOSIGALRM
err0:
qemu_free (hw);
return NULL;
@@ -431,7 +438,7 @@ SW *glue (AUD_open_, TYPE) (
goto fail;
}
- if (audio_bug (AUDIO_FUNC, !s->drv)) {
+ if (audio_bug (AUDIO_FUNC, !glue (s->drv_, TYPE))) {
dolog ("Can not open `%s' (no host audio driver)\n", name);
goto fail;
}
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
index 8512f12..cf3eaf9 100644
--- a/audio/coreaudio.c
+++ b/audio/coreaudio.c
@@ -1,6 +1,7 @@
/*
* QEMU OS X CoreAudio audio driver
*
+ * Copyright (c) 2008 The Android Open Source Project
* Copyright (c) 2005 Mike Kronenberg
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -31,27 +32,35 @@
#define AUDIO_CAP "coreaudio"
#include "audio_int.h"
+#define ENABLE_IN 1
+
+#if 0
+# define D(...) fprintf(stderr, __VA_ARGS__)
+#else
+# define D(...) ((void)0)
+#endif
+
struct {
- int buffer_frames;
- int nbuffers;
+ int out_buffer_frames;
+ int out_nbuffers;
+ int in_buffer_frames;
+ int in_nbuffers;
int isAtexit;
} conf = {
- .buffer_frames = 512,
- .nbuffers = 4,
+ .out_buffer_frames = 512,
+ .out_nbuffers = 4,
+ .in_buffer_frames = 512,
+ .in_nbuffers = 4,
.isAtexit = 0
};
-typedef struct coreaudioVoiceOut {
- HWVoiceOut hw;
- pthread_mutex_t mutex;
- int isAtexit;
- AudioDeviceID outputDeviceID;
- UInt32 audioDevicePropertyBufferFrameSize;
- AudioStreamBasicDescription outputStreamBasicDescription;
- int live;
- int decr;
- int rpos;
-} coreaudioVoiceOut;
+/***************************************************************************************/
+/***************************************************************************************/
+/*** ***/
+/*** U T I L I T Y R O U T I N E S ***/
+/*** ***/
+/***************************************************************************************/
+/***************************************************************************************/
static void coreaudio_logstatus (OSStatus status)
{
@@ -143,13 +152,40 @@ static void GCC_FMT_ATTR (3, 4) coreaudio_logerr2 (
coreaudio_logstatus (status);
}
-static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
+static void coreaudio_atexit (void)
+{
+ conf.isAtexit = 1;
+}
+
+/***************************************************************************************/
+/***************************************************************************************/
+/*** ***/
+/*** S H A R E D I N / O U T V O I C E ***/
+/*** ***/
+/***************************************************************************************/
+/***************************************************************************************/
+
+typedef struct coreAudioVoice {
+ pthread_mutex_t mutex;
+ AudioDeviceID deviceID;
+ Boolean isInput;
+ UInt32 bufferFrameSize;
+ AudioStreamBasicDescription streamBasicDescription;
+ AudioDeviceIOProc ioproc;
+ int live;
+ int decr;
+ int pos;
+} coreaudioVoice;
+
+
+static inline UInt32
+coreaudio_voice_isPlaying (coreaudioVoice* core)
{
OSStatus status;
UInt32 result = 0;
- UInt32 propertySize = sizeof(outputDeviceID);
+ UInt32 propertySize = sizeof(core->deviceID);
status = AudioDeviceGetProperty(
- outputDeviceID, 0, 0,
+ core->deviceID, 0, core->isInput,
kAudioDevicePropertyDeviceIsRunning, &propertySize, &result);
if (status != kAudioHardwareNoError) {
coreaudio_logerr(status,
@@ -158,12 +194,8 @@ static inline UInt32 isPlaying (AudioDeviceID outputDeviceID)
return result;
}
-static void coreaudio_atexit (void)
-{
- conf.isAtexit = 1;
-}
-
-static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
+static int
+coreaudio_voice_lock (coreaudioVoice* core, const char *fn_name)
{
int err;
@@ -176,7 +208,8 @@ static int coreaudio_lock (coreaudioVoiceOut *core, const char *fn_name)
return 0;
}
-static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
+static int
+coreaudio_voice_unlock (coreaudioVoice* core, const char *fn_name)
{
int err;
@@ -189,114 +222,81 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
return 0;
}
-static int coreaudio_run_out (HWVoiceOut *hw)
+static int
+coreaudio_voice_ctl (coreaudioVoice* core, int cmd)
{
- int live, decr;
- coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
-
- if (coreaudio_lock (core, "coreaudio_run_out")) {
- return 0;
- }
+ OSStatus status;
- live = audio_pcm_hw_get_live_out (hw);
+ switch (cmd) {
+ case VOICE_ENABLE:
+ /* start playback */
+ D("%s: %s started\n", __FUNCTION__, core->isInput ? "input" : "output");
+ if (!coreaudio_voice_isPlaying(core)) {
+ status = AudioDeviceStart(core->deviceID, core->ioproc);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not resume playback\n");
+ }
+ }
+ break;
- if (core->decr > live) {
- ldebug ("core->decr %d live %d core->live %d\n",
- core->decr,
- live,
- core->live);
+ case VOICE_DISABLE:
+ /* stop playback */
+ D("%s: %s stopped\n", __FUNCTION__, core->isInput ? "input" : "output");
+ if (!conf.isAtexit) {
+ if (coreaudio_voice_isPlaying(core)) {
+ status = AudioDeviceStop(core->deviceID, core->ioproc);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not pause playback\n");
+ }
+ }
+ }
+ break;
}
-
- decr = audio_MIN (core->decr, live);
- core->decr -= decr;
-
- core->live = live - decr;
- hw->rpos = core->rpos;
-
- coreaudio_unlock (core, "coreaudio_run_out");
- return decr;
+ return 0;
}
-/* callback to feed audiooutput buffer */
-static OSStatus audioDeviceIOProc(
- AudioDeviceID inDevice,
- const AudioTimeStamp* inNow,
- const AudioBufferList* inInputData,
- const AudioTimeStamp* inInputTime,
- AudioBufferList* outOutputData,
- const AudioTimeStamp* inOutputTime,
- void* hwptr)
+static void
+coreaudio_voice_fini (coreaudioVoice* core)
{
- UInt32 frame, frameCount;
- float *out = outOutputData->mBuffers[0].mData;
- HWVoiceOut *hw = hwptr;
- coreaudioVoiceOut *core = (coreaudioVoiceOut *) hwptr;
- int rpos, live;
- st_sample_t *src;
-#ifndef FLOAT_MIXENG
-#ifdef RECIPROCAL
- const float scale = 1.f / UINT_MAX;
-#else
- const float scale = UINT_MAX;
-#endif
-#endif
-
- if (coreaudio_lock (core, "audioDeviceIOProc")) {
- inInputTime = 0;
- return 0;
- }
+ OSStatus status;
+ int err;
- frameCount = core->audioDevicePropertyBufferFrameSize;
- live = core->live;
+ if (!conf.isAtexit) {
+ /* stop playback */
+ coreaudio_voice_ctl(core, VOICE_DISABLE);
- /* if there are not enough samples, set signal and return */
- if (live < frameCount) {
- inInputTime = 0;
- coreaudio_unlock (core, "audioDeviceIOProc(empty)");
- return 0;
+ /* remove callback */
+ status = AudioDeviceRemoveIOProc(core->deviceID, core->ioproc);
+ if (status != kAudioHardwareNoError) {
+ coreaudio_logerr (status, "Could not remove IOProc\n");
+ }
}
+ core->deviceID = kAudioDeviceUnknown;
- rpos = core->rpos;
- src = hw->mix_buf + rpos;
-
- /* fill buffer */
- for (frame = 0; frame < frameCount; frame++) {
-#ifdef FLOAT_MIXENG
- *out++ = src[frame].l; /* left channel */
- *out++ = src[frame].r; /* right channel */
-#else
-#ifdef RECIPROCAL
- *out++ = src[frame].l * scale; /* left channel */
- *out++ = src[frame].r * scale; /* right channel */
-#else
- *out++ = src[frame].l / scale; /* left channel */
- *out++ = src[frame].r / scale; /* right channel */
-#endif
-#endif
+ /* destroy mutex */
+ err = pthread_mutex_destroy(&core->mutex);
+ if (err) {
+ dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
}
-
- rpos = (rpos + frameCount) % hw->samples;
- core->decr += frameCount;
- core->rpos = rpos;
-
- coreaudio_unlock (core, "audioDeviceIOProc");
- return 0;
}
-static int coreaudio_write (SWVoiceOut *sw, void *buf, int len)
-{
- return audio_pcm_sw_write (sw, buf, len);
-}
-static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
+static int
+coreaudio_voice_init (coreaudioVoice* core,
+ audsettings_t* as,
+ int frameSize,
+ AudioDeviceIOProc ioproc,
+ void* hw,
+ int input)
{
- OSStatus status;
- coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
- UInt32 propertySize;
- int err;
- int bits = 8;
- const char *typ = "playback";
+ OSStatus status;
+ UInt32 propertySize;
+ int err;
+ int bits = 8;
AudioValueRange frameRange;
+ const char* typ = input ? "input" : "playback";
+
+ core->isInput = input ? true : false;
/* create mutex */
err = pthread_mutex_init(&core->mutex, NULL);
@@ -309,20 +309,24 @@ static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
bits = 16;
}
- audio_pcm_init_info (&hw->info, as);
-
+ // TODO: audio_pcm_init_info (&hw->info, as);
/* open default output device */
- propertySize = sizeof(core->outputDeviceID);
+ /* note: we use DefaultSystemOutputDevice because DefaultOutputDevice seems to
+ * always link to the internal speakers, and not the ones selected through system properties
+ * go figure...
+ */
+ propertySize = sizeof(core->deviceID);
status = AudioHardwareGetProperty(
- kAudioHardwarePropertyDefaultOutputDevice,
+ input ? kAudioHardwarePropertyDefaultInputDevice :
+ kAudioHardwarePropertyDefaultSystemOutputDevice,
&propertySize,
- &core->outputDeviceID);
+ &core->deviceID);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ,
- "Could not get default output Device\n");
+ "Could not get default %s device\n", typ);
return -1;
}
- if (core->outputDeviceID == kAudioDeviceUnknown) {
+ if (core->deviceID == kAudioDeviceUnknown) {
dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
return -1;
}
@@ -330,9 +334,9 @@ static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
/* get minimum and maximum buffer frame sizes */
propertySize = sizeof(frameRange);
status = AudioDeviceGetProperty(
- core->outputDeviceID,
- 0,
+ core->deviceID,
0,
+ core->isInput,
kAudioDevicePropertyBufferFrameSizeRange,
&propertySize,
&frameRange);
@@ -342,100 +346,101 @@ static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
return -1;
}
- if (frameRange.mMinimum > conf.buffer_frames) {
- core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMinimum;
- dolog ("warning: Upsizing Buffer Frames to %f\n", frameRange.mMinimum);
+ if (frameRange.mMinimum > frameSize) {
+ core->bufferFrameSize = (UInt32) frameRange.mMinimum;
+ dolog ("warning: Upsizing Output Buffer Frames to %f\n", frameRange.mMinimum);
}
- else if (frameRange.mMaximum < conf.buffer_frames) {
- core->audioDevicePropertyBufferFrameSize = (UInt32) frameRange.mMaximum;
- dolog ("warning: Downsizing Buffer Frames to %f\n", frameRange.mMaximum);
+ else if (frameRange.mMaximum < frameSize) {
+ core->bufferFrameSize = (UInt32) frameRange.mMaximum;
+ dolog ("warning: Downsizing Output Buffer Frames to %f\n", frameRange.mMaximum);
}
else {
- core->audioDevicePropertyBufferFrameSize = conf.buffer_frames;
+ core->bufferFrameSize = frameSize;
}
/* set Buffer Frame Size */
- propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
+ propertySize = sizeof(core->bufferFrameSize);
status = AudioDeviceSetProperty(
- core->outputDeviceID,
+ core->deviceID,
NULL,
0,
- false,
+ core->isInput,
kAudioDevicePropertyBufferFrameSize,
propertySize,
- &core->audioDevicePropertyBufferFrameSize);
+ &core->bufferFrameSize);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ,
"Could not set device buffer frame size %ld\n",
- core->audioDevicePropertyBufferFrameSize);
+ core->bufferFrameSize);
return -1;
}
/* get Buffer Frame Size */
- propertySize = sizeof(core->audioDevicePropertyBufferFrameSize);
+ propertySize = sizeof(core->bufferFrameSize);
status = AudioDeviceGetProperty(
- core->outputDeviceID,
+ core->deviceID,
0,
- false,
+ core->isInput,
kAudioDevicePropertyBufferFrameSize,
&propertySize,
- &core->audioDevicePropertyBufferFrameSize);
+ &core->bufferFrameSize);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ,
"Could not get device buffer frame size\n");
return -1;
}
- hw->samples = conf.nbuffers * core->audioDevicePropertyBufferFrameSize;
+ // TODO: hw->samples = *pNBuffers * core->bufferFrameSize;
/* get StreamFormat */
- propertySize = sizeof(core->outputStreamBasicDescription);
+ propertySize = sizeof(core->streamBasicDescription);
status = AudioDeviceGetProperty(
- core->outputDeviceID,
+ core->deviceID,
0,
- false,
+ core->isInput,
kAudioDevicePropertyStreamFormat,
&propertySize,
- &core->outputStreamBasicDescription);
+ &core->streamBasicDescription);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ,
"Could not get Device Stream properties\n");
- core->outputDeviceID = kAudioDeviceUnknown;
+ core->deviceID = kAudioDeviceUnknown;
return -1;
}
/* set Samplerate */
- core->outputStreamBasicDescription.mSampleRate = (Float64) as->freq;
- propertySize = sizeof(core->outputStreamBasicDescription);
+ core->streamBasicDescription.mSampleRate = (Float64) as->freq;
+ propertySize = sizeof(core->streamBasicDescription);
status = AudioDeviceSetProperty(
- core->outputDeviceID,
- 0,
+ core->deviceID,
0,
0,
+ core->isInput,
kAudioDevicePropertyStreamFormat,
propertySize,
- &core->outputStreamBasicDescription);
+ &core->streamBasicDescription);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n",
as->freq);
- core->outputDeviceID = kAudioDeviceUnknown;
+ core->deviceID = kAudioDeviceUnknown;
return -1;
}
/* set Callback */
- status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
+ core->ioproc = ioproc;
+ status = AudioDeviceAddIOProc(core->deviceID, ioproc, hw);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
- core->outputDeviceID = kAudioDeviceUnknown;
+ core->deviceID = kAudioDeviceUnknown;
return -1;
}
/* start Playback */
- if (!isPlaying(core->outputDeviceID)) {
- status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
+ if (!input && !coreaudio_voice_isPlaying(core)) {
+ status = AudioDeviceStart(core->deviceID, core->ioproc);
if (status != kAudioHardwareNoError) {
coreaudio_logerr2 (status, typ, "Could not start playback\n");
- AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
- core->outputDeviceID = kAudioDeviceUnknown;
+ AudioDeviceRemoveIOProc(core->deviceID, core->ioproc);
+ core->deviceID = kAudioDeviceUnknown;
return -1;
}
}
@@ -443,84 +448,332 @@ static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
return 0;
}
-static void coreaudio_fini_out (HWVoiceOut *hw)
+
+/***************************************************************************************/
+/***************************************************************************************/
+/*** ***/
+/*** O U T P U T V O I C E ***/
+/*** ***/
+/***************************************************************************************/
+/***************************************************************************************/
+
+typedef struct coreaudioVoiceOut {
+ HWVoiceOut hw;
+ coreaudioVoice core[1];
+} coreaudioVoiceOut;
+
+#define CORE_OUT(hw) ((coreaudioVoiceOut*)(hw))->core
+
+
+static int
+coreaudio_run_out (HWVoiceOut *hw)
{
- OSStatus status;
- int err;
- coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+ int live, decr;
+ coreaudioVoice *core = CORE_OUT(hw);
- if (!conf.isAtexit) {
- /* stop playback */
- if (isPlaying(core->outputDeviceID)) {
- status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
- if (status != kAudioHardwareNoError) {
- coreaudio_logerr (status, "Could not stop playback\n");
- }
- }
+ if (coreaudio_voice_lock (core, "coreaudio_run_out")) {
+ return 0;
+ }
- /* remove callback */
- status = AudioDeviceRemoveIOProc(core->outputDeviceID,
- audioDeviceIOProc);
- if (status != kAudioHardwareNoError) {
- coreaudio_logerr (status, "Could not remove IOProc\n");
- }
+ live = audio_pcm_hw_get_live_out (hw);
+
+ if (core->decr > live) {
+ ldebug ("core->decr %d live %d core->live %d\n",
+ core->decr,
+ live,
+ core->live);
}
- core->outputDeviceID = kAudioDeviceUnknown;
- /* destroy mutex */
- err = pthread_mutex_destroy(&core->mutex);
- if (err) {
- dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
+ decr = audio_MIN (core->decr, live);
+ core->decr -= decr;
+ core->live = live - decr;
+ hw->rpos = core->pos;
+
+ coreaudio_voice_unlock (core, "coreaudio_run_out");
+ return decr;
+}
+
+
+/* callback to feed audiooutput buffer */
+static OSStatus
+audioOutDeviceIOProc(
+ AudioDeviceID inDevice,
+ const AudioTimeStamp* inNow,
+ const AudioBufferList* inInputData,
+ const AudioTimeStamp* inInputTime,
+ AudioBufferList* outOutputData,
+ const AudioTimeStamp* inOutputTime,
+ void* hwptr)
+{
+ UInt32 frame, frameCount;
+ float *out = outOutputData->mBuffers[0].mData;
+ HWVoiceOut *hw = hwptr;
+ coreaudioVoice *core = CORE_OUT(hw);
+ int rpos, live;
+ st_sample_t *src;
+#ifndef FLOAT_MIXENG
+#ifdef RECIPROCAL
+ const float scale = 1.f / UINT_MAX;
+#else
+ const float scale = UINT_MAX;
+#endif
+#endif
+
+ if (coreaudio_voice_lock (core, "audioDeviceIOProc")) {
+ inInputTime = 0;
+ return 0;
}
+
+ frameCount = core->bufferFrameSize;
+ live = core->live;
+
+ /* if there are not enough samples, set signal and return */
+ if (live < frameCount) {
+ inInputTime = 0;
+ coreaudio_voice_unlock (core, "audioDeviceIOProc(empty)");
+ return 0;
+ }
+
+ rpos = core->pos;
+ src = hw->mix_buf + rpos;
+
+ /* fill buffer */
+ for (frame = 0; frame < frameCount; frame++) {
+#ifdef FLOAT_MIXENG
+ *out++ = src[frame].l; /* left channel */
+ *out++ = src[frame].r; /* right channel */
+#else
+#ifdef RECIPROCAL
+ *out++ = src[frame].l * scale; /* left channel */
+ *out++ = src[frame].r * scale; /* right channel */
+#else
+ *out++ = src[frame].l / scale; /* left channel */
+ *out++ = src[frame].r / scale; /* right channel */
+#endif
+#endif
+ }
+
+ rpos = (rpos + frameCount) % hw->samples;
+ core->decr += frameCount;
+ core->pos = rpos;
+
+ coreaudio_voice_unlock (core, "audioDeviceIOProc");
+ return 0;
}
-static int coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
+static int
+coreaudio_write (SWVoiceOut *sw, void *buf, int len)
{
- OSStatus status;
- coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
+ return audio_pcm_sw_write (sw, buf, len);
+}
- switch (cmd) {
- case VOICE_ENABLE:
- /* start playback */
- if (!isPlaying(core->outputDeviceID)) {
- status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
- if (status != kAudioHardwareNoError) {
- coreaudio_logerr (status, "Could not resume playback\n");
- }
- }
- break;
+static int
+coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+ coreaudioVoice* core = CORE_OUT(hw);
+ int err;
- case VOICE_DISABLE:
- /* stop playback */
- if (!conf.isAtexit) {
- if (isPlaying(core->outputDeviceID)) {
- status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
- if (status != kAudioHardwareNoError) {
- coreaudio_logerr (status, "Could not pause playback\n");
- }
- }
- }
- break;
+ audio_pcm_init_info (&hw->info, as);
+
+ err = coreaudio_voice_init( core, as, conf.out_buffer_frames, audioOutDeviceIOProc, hw, 0 );
+ if (err < 0)
+ return err;
+
+ hw->samples = core->bufferFrameSize * conf.out_nbuffers;
+ return 0;
+}
+
+static void
+coreaudio_fini_out (HWVoiceOut *hw)
+{
+
+ coreaudioVoice* core = CORE_OUT(hw);
+
+ coreaudio_voice_fini(core);
+}
+
+static int
+coreaudio_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ coreaudioVoice* core = CORE_OUT(hw);
+
+ return coreaudio_voice_ctl(core, cmd);
+}
+
+/***************************************************************************************/
+/***************************************************************************************/
+/*** ***/
+/*** I N P U T V O I C E ***/
+/*** ***/
+/***************************************************************************************/
+/***************************************************************************************/
+
+
+
+typedef struct coreaudioVoiceIn {
+ HWVoiceIn hw;
+ coreaudioVoice core[1];
+} coreaudioVoiceIn;
+
+#define CORE_IN(hw) ((coreaudioVoiceIn*)(hw))->core
+
+
+static int
+coreaudio_run_in (HWVoiceIn *hw)
+{
+ int decr;
+
+ coreaudioVoice *core = CORE_IN(hw);
+
+ if (coreaudio_voice_lock (core, "coreaudio_run_in")) {
+ return 0;
+ }
+
+ D("%s: core.decr=%d core.pos=%d\n", __FUNCTION__, core->decr, core->pos);
+ decr = core->decr;
+ core->decr -= decr;
+ hw->wpos = core->pos;
+
+ coreaudio_voice_unlock (core, "coreaudio_run_in");
+ return decr;
+}
+
+
+/* callback to feed audiooutput buffer */
+static OSStatus
+audioInDeviceIOProc(
+ AudioDeviceID inDevice,
+ const AudioTimeStamp* inNow,
+ const AudioBufferList* inInputData,
+ const AudioTimeStamp* inInputTime,
+ AudioBufferList* outOutputData,
+ const AudioTimeStamp* inOutputTime,
+ void* hwptr)
+{
+ UInt32 frame, frameCount;
+ float *in = inInputData->mBuffers[0].mData;
+ HWVoiceIn *hw = hwptr;
+ coreaudioVoice *core = CORE_IN(hw);
+ int wpos, avail;
+ st_sample_t *dst;
+#ifndef FLOAT_MIXENG
+#ifdef RECIPROCAL
+ const float scale = 1.f / UINT_MAX;
+#else
+ const float scale = UINT_MAX;
+#endif
+#endif
+
+ if (coreaudio_voice_lock (core, "audioDeviceIOProc")) {
+ inInputTime = 0;
+ return 0;
+ }
+
+ frameCount = core->bufferFrameSize;
+ avail = hw->samples - hw->total_samples_captured - core->decr;
+
+ D("%s: enter avail=%d core.decr=%d core.pos=%d hw.samples=%d hw.total_samples_captured=%d frameCount=%d\n",
+ __FUNCTION__, avail, core->decr, core->pos, hw->samples, hw->total_samples_captured, (int)frameCount);
+
+ /* if there are not enough samples, set signal and return */
+ if (avail < frameCount) {
+ inInputTime = 0;
+ coreaudio_voice_unlock (core, "audioDeviceIOProc(empty)");
+ return 0;
}
+
+ wpos = core->pos;
+ dst = hw->conv_buf + wpos;
+
+ /* fill buffer */
+ for (frame = 0; frame < frameCount; frame++) {
+#ifdef FLOAT_MIXENG
+ dst[frame].l = *in++; /* left channel */
+ dst[frame].r = *in++; /* right channel */
+#else
+#ifdef RECIPROCAL
+ dst[frame].l = *in++ * scale; /* left channel */
+ dst[frame].r = *in++ * scale; /* right channel */
+#else
+ dst[frame].l = *in++ / scale; /* left channel */
+ dst[frame].r = *in++ / scale; /* right channel */
+#endif
+#endif
+ }
+
+ wpos = (wpos + frameCount) % hw->samples;
+ core->decr += frameCount;
+ core->pos = wpos;
+
+ D("exit: core.decr=%d core.pos=%d\n", core->decr, core->pos);
+ coreaudio_voice_unlock (core, "audioDeviceIOProc");
+ return 0;
+}
+
+static int
+coreaudio_read (SWVoiceIn *sw, void *buf, int len)
+{
+ int result = audio_pcm_sw_read(sw, buf, len);
+ D("%s: audio_pcm_sw_read(%d) returned %d\n", __FUNCTION__, len, result);
+ return result;
+}
+
+static int
+coreaudio_init_in (HWVoiceIn *hw, audsettings_t *as)
+{
+ coreaudioVoice* core = CORE_IN(hw);
+ int err;
+
+ audio_pcm_init_info (&hw->info, as);
+
+ err = coreaudio_voice_init( core, as, conf.in_buffer_frames, audioInDeviceIOProc, hw, 1 );
+ if (err < 0) {
+ return err;
+ }
+
+ hw->samples = core->bufferFrameSize * conf.in_nbuffers;
return 0;
}
-static void *coreaudio_audio_init (void)
+static void
+coreaudio_fini_in (HWVoiceIn *hw)
+{
+
+ coreaudioVoice* core = CORE_IN(hw);
+
+ coreaudio_voice_fini(core);
+}
+
+static int
+coreaudio_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+ coreaudioVoice* core = CORE_IN(hw);
+
+ return coreaudio_voice_ctl(core, cmd);
+}
+
+static void*
+coreaudio_audio_init (void)
{
atexit(coreaudio_atexit);
return &coreaudio_audio_init;
}
-static void coreaudio_audio_fini (void *opaque)
+static void
+coreaudio_audio_fini (void *opaque)
{
(void) opaque;
}
static struct audio_option coreaudio_options[] = {
- {"BUFFER_SIZE", AUD_OPT_INT, &conf.buffer_frames,
- "Size of the buffer in frames", NULL, 0},
- {"BUFFER_COUNT", AUD_OPT_INT, &conf.nbuffers,
- "Number of buffers", NULL, 0},
+ {"OUT_BUFFER_SIZE", AUD_OPT_INT, &conf.out_buffer_frames,
+ "Size of the output buffer in frames", NULL, 0},
+ {"OUT_BUFFER_COUNT", AUD_OPT_INT, &conf.out_nbuffers,
+ "Number of output buffers", NULL, 0},
+ {"IN_BUFFER_SIZE", AUD_OPT_INT, &conf.in_buffer_frames,
+ "Size of the input buffer in frames", NULL, 0},
+ {"IN_BUFFER_COUNT", AUD_OPT_INT, &conf.in_nbuffers,
+ "Number of input buffers", NULL, 0},
{NULL, 0, NULL, NULL, NULL, 0}
};
@@ -531,24 +784,39 @@ static struct audio_pcm_ops coreaudio_pcm_ops = {
coreaudio_write,
coreaudio_ctl_out,
+#if ENABLE_IN
+ coreaudio_init_in,
+ coreaudio_fini_in,
+ coreaudio_run_in,
+ coreaudio_read,
+ coreaudio_ctl_in
+#else
NULL,
NULL,
NULL,
NULL,
NULL
+#endif
};
struct audio_driver coreaudio_audio_driver = {
INIT_FIELD (name = ) "coreaudio",
INIT_FIELD (descr = )
- "CoreAudio http://developer.apple.com/audio/coreaudio.html",
+ "CoreAudio (developer.apple.com/audio/coreaudio.html)",
INIT_FIELD (options = ) coreaudio_options,
INIT_FIELD (init = ) coreaudio_audio_init,
INIT_FIELD (fini = ) coreaudio_audio_fini,
INIT_FIELD (pcm_ops = ) &coreaudio_pcm_ops,
INIT_FIELD (can_be_default = ) 1,
+#if ENABLE_IN
+ INIT_FIELD (max_voices_out = ) 1,
+ INIT_FIELD (max_voices_in = ) 1,
+ INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut),
+ INIT_FIELD (voice_size_in = ) sizeof (coreaudioVoiceIn),
+#else
INIT_FIELD (max_voices_out = ) 1,
INIT_FIELD (max_voices_in = ) 0,
INIT_FIELD (voice_size_out = ) sizeof (coreaudioVoiceOut),
- INIT_FIELD (voice_size_in = ) 0
+ INIT_FIELD (voice_size_in = ) 0,
+#endif
};
diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
index 90a0333..6104acb 100644
--- a/audio/dsoundaudio.c
+++ b/audio/dsoundaudio.c
@@ -1067,7 +1067,7 @@ static struct audio_pcm_ops dsound_pcm_ops = {
struct audio_driver dsound_audio_driver = {
INIT_FIELD (name = ) "dsound",
INIT_FIELD (descr = )
- "DirectSound http://wikipedia.org/wiki/DirectSound",
+ "DirectSound audio (www.wikipedia.org/wiki/DirectSound)",
INIT_FIELD (options = ) dsound_options,
INIT_FIELD (init = ) dsound_audio_init,
INIT_FIELD (fini = ) dsound_audio_fini,
diff --git a/audio/esdaudio.c b/audio/esdaudio.c
new file mode 100644
index 0000000..9aeb8a5
--- /dev/null
+++ b/audio/esdaudio.c
@@ -0,0 +1,692 @@
+/*
+ * QEMU ESD audio driver
+ *
+ * Copyright (c) 2008 The Android Open Source Project
+ * Copyright (c) 2006 Frederick Reeve (brushed up by malc)
+ *
+ * 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 <esd.h>
+#include "vl.h"
+#include "audio.h"
+#include <signal.h>
+
+#define AUDIO_CAP "esd"
+#include "audio_int.h"
+#include "audio_pt_int.h"
+#include <dlfcn.h>
+
+#include "android_debug.h"
+
+#define DEBUG 1
+
+#if DEBUG
+# include <stdio.h>
+# define D(...) VERBOSE_PRINT(audio,__VA_ARGS__)
+# define D_ACTIVE VERBOSE_CHECK(audio)
+# define O(...) VERBOSE_PRINT(audioout,__VA_ARGS__)
+# define I(...) VERBOSE_PRINT(audioin,__VA_ARGS__)
+#else
+# define D(...) ((void)0)
+# define D_ACTIVE 0
+# define O(...) ((void)0)
+# define I(...) ((void)0)
+#endif
+
+#define STRINGIFY_(x) #x
+#define STRINGIFY(x) STRINGIFY_(x)
+
+typedef struct {
+ HWVoiceOut hw;
+ int done;
+ int live;
+ int decr;
+ int rpos;
+ void *pcm_buf;
+ int fd;
+ struct audio_pt pt;
+} ESDVoiceOut;
+
+typedef struct {
+ HWVoiceIn hw;
+ int done;
+ int dead;
+ int incr;
+ int wpos;
+ void *pcm_buf;
+ int fd;
+ struct audio_pt pt;
+} ESDVoiceIn;
+
+static struct {
+ int samples;
+ int divisor;
+ char *dac_host;
+ char *adc_host;
+} conf = {
+ 1024,
+ 2,
+ NULL,
+ NULL
+};
+
+/* link dynamically to the libesd.so */
+
+#define ESD_SYMBOLS \
+ ESD_FUNCTION(int,esd_play_stream,(esd_format_t,int,const char*,const char*)) \
+ ESD_FUNCTION(int,esd_record_stream,(esd_format_t,int,const char*,const char*)) \
+ ESD_FUNCTION(int,esd_open_sound,( const char *host )) \
+ ESD_FUNCTION(int,esd_close,(int)) \
+
+/* define pointers to library functions we're going to use */
+#define ESD_FUNCTION(ret,name,sig) \
+ static ret (*func_ ## name)sig;
+
+ESD_SYMBOLS
+
+#undef ESD_FUNCTION
+
+static void* esd_lib;
+
+static void GCC_FMT_ATTR (2, 3) qesd_logerr (int err, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start (ap, fmt);
+ AUD_vlog (AUDIO_CAP, fmt, ap);
+ va_end (ap);
+
+ AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
+}
+
+/* playback */
+static void *qesd_thread_out (void *arg)
+{
+ ESDVoiceOut* esd = arg;
+ HWVoiceOut* hw = &esd->hw;
+ int threshold;
+ sigset_t set;
+
+ threshold = conf.divisor ? hw->samples / conf.divisor : 0;
+
+ /* ignore SIGALRM in this thread */
+ sigemptyset(&set);
+ sigaddset(&set, SIGALRM);
+
+ pthread_sigmask( SIG_BLOCK, &set, NULL);
+
+ O("EsounD output thread starting, threshold is %d samples", threshold);
+ for (;;) {
+ int decr, to_mix, rpos;
+
+ for (;;) {
+ if (esd->done) {
+ goto exit;
+ }
+
+ if (esd->live > threshold) {
+ break;
+ }
+
+ if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
+ O("EsounD output thread aborting on wait error");
+ goto exit;
+ }
+ }
+
+ decr = to_mix = esd->live;
+ rpos = hw->rpos;
+
+ if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
+ O("EsounD output thread aborting on unlock error");
+ return NULL;
+ }
+
+ while (to_mix) {
+ ssize_t written;
+ int chunk = audio_MIN (to_mix, hw->samples - rpos);
+ st_sample_t *src = hw->mix_buf + rpos;
+
+ hw->clip (esd->pcm_buf, src, chunk);
+
+ again:
+ written = write (esd->fd, esd->pcm_buf, chunk << hw->info.shift);
+ if (written == -1) {
+ if (errno == EINTR || errno == EAGAIN) {
+ goto again;
+ }
+ qesd_logerr (errno, "write failed\n");
+ O("EsounD output thread aborting on write error: %s", strerror(errno));
+ return NULL;
+ }
+
+ if (written != chunk << hw->info.shift) {
+ int wsamples = written >> hw->info.shift;
+ int wbytes = wsamples << hw->info.shift;
+ if (wbytes != written) {
+ dolog ("warning: Misaligned write %d (requested %d), "
+ "alignment %d\n",
+ wbytes, written, hw->info.align + 1);
+ }
+ to_mix -= wsamples;
+ rpos = (rpos + wsamples) % hw->samples;
+ break;
+ }
+
+ rpos = (rpos + chunk) % hw->samples;
+ to_mix -= chunk;
+ }
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ O("EsounD output thread aborting on lock error\n");
+ return NULL;
+ }
+
+ esd->rpos = rpos;
+ esd->live -= decr;
+ esd->decr += decr;
+ }
+ O("EsounD output thread exiting");
+
+ exit:
+ audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+ return NULL;
+}
+
+static int qesd_run_out (HWVoiceOut *hw)
+{
+ int live, decr;
+ ESDVoiceOut *esd = (ESDVoiceOut *) hw;
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ O("%s: exiting on lock error", __FUNCTION__);
+ return 0;
+ }
+
+ live = audio_pcm_hw_get_live_out (hw);
+ decr = audio_MIN (live, esd->decr);
+ esd->decr -= decr;
+ esd->live = live - decr;
+ hw->rpos = esd->rpos;
+ if (esd->live > 0) {
+ O("%s: signaling %d samples\n", esd->live);
+ audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+ }
+ else {
+ O(".");
+ audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+ }
+ return decr;
+}
+
+static int qesd_write (SWVoiceOut *sw, void *buf, int len)
+{
+ return audio_pcm_sw_write (sw, buf, len);
+}
+
+static int qesd_init_out (HWVoiceOut *hw, audsettings_t *as)
+{
+ ESDVoiceOut *esd = (ESDVoiceOut *) hw;
+ audsettings_t obt_as = *as;
+ int esdfmt = ESD_STREAM | ESD_PLAY;
+ int result = -1;
+
+ /* shut down verbose debug spew */
+ if (!D_ACTIVE)
+ stdio_disable();
+
+ O("initializing EsoundD audio output");
+ esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
+ switch (as->fmt) {
+ case AUD_FMT_S8:
+ case AUD_FMT_U8:
+ esdfmt |= ESD_BITS8;
+ obt_as.fmt = AUD_FMT_U8;
+ break;
+#if 0
+ case AUD_FMT_S32:
+ case AUD_FMT_U32:
+ dolog ("Will use 16 instead of 32 bit samples\n");
+#endif
+ case AUD_FMT_S16:
+ case AUD_FMT_U16:
+ deffmt:
+ esdfmt |= ESD_BITS16;
+ obt_as.fmt = AUD_FMT_S16;
+ break;
+
+ default:
+ dolog ("Internal logic error: Bad audio format %d\n", as->fmt);
+#ifdef DEBUG_FMOD
+ abort ();
+#endif
+ goto deffmt;
+
+ }
+ obt_as.endianness = 0;
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+
+ hw->samples = conf.samples;
+ esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ if (!esd->pcm_buf) {
+ dolog ("Could not allocate buffer (%d bytes)\n",
+ hw->samples << hw->info.shift);
+ goto exit;
+ }
+
+ esd->fd = func_esd_play_stream (esdfmt, as->freq, conf.dac_host, NULL);
+ if (esd->fd < 0) {
+ if (conf.dac_host == NULL) {
+ esd->fd = func_esd_play_stream (esdfmt, as->freq, "localhost", NULL);
+ }
+ if (esd->fd < 0) {
+ qesd_logerr (errno, "esd_play_stream failed\n");
+ goto fail2;
+ }
+ }
+
+ if (audio_pt_init (&esd->pt, qesd_thread_out, esd, AUDIO_CAP, AUDIO_FUNC)) {
+ goto fail3;
+ }
+
+ result = 0; /* success */
+ goto exit;
+
+ fail3:
+ if (close (esd->fd)) {
+ qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
+ AUDIO_FUNC, esd->fd);
+ }
+ esd->fd = -1;
+
+ fail2:
+ qemu_free (esd->pcm_buf);
+ esd->pcm_buf = NULL;
+
+ exit:
+ if (!D_ACTIVE)
+ stdio_enable();
+
+ return result;
+}
+
+static void qesd_fini_out (HWVoiceOut *hw)
+{
+ void *ret;
+ ESDVoiceOut *esd = (ESDVoiceOut *) hw;
+
+ audio_pt_lock (&esd->pt, AUDIO_FUNC);
+ esd->done = 1;
+ audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+ audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
+
+ if (esd->fd >= 0) {
+ if (close (esd->fd)) {
+ qesd_logerr (errno, "failed to close esd socket\n");
+ }
+ esd->fd = -1;
+ }
+
+ audio_pt_fini (&esd->pt, AUDIO_FUNC);
+
+ qemu_free (esd->pcm_buf);
+ esd->pcm_buf = NULL;
+}
+
+static int qesd_ctl_out (HWVoiceOut *hw, int cmd, ...)
+{
+ (void) hw;
+ (void) cmd;
+ return 0;
+}
+
+/* capture */
+static void *qesd_thread_in (void *arg)
+{
+ ESDVoiceIn *esd = arg;
+ HWVoiceIn *hw = &esd->hw;
+ int threshold;
+
+ threshold = conf.divisor ? hw->samples / conf.divisor : 0;
+
+ for (;;) {
+ int incr, to_grab, wpos;
+
+ for (;;) {
+ if (esd->done) {
+ goto exit;
+ }
+
+ if (esd->dead > threshold) {
+ break;
+ }
+
+ if (audio_pt_wait (&esd->pt, AUDIO_FUNC)) {
+ goto exit;
+ }
+ }
+
+ incr = to_grab = esd->dead;
+ wpos = hw->wpos;
+
+ if (audio_pt_unlock (&esd->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ while (to_grab) {
+ ssize_t nread;
+ int chunk = audio_MIN (to_grab, hw->samples - wpos);
+ void *buf = advance (esd->pcm_buf, wpos);
+
+ again:
+ nread = read (esd->fd, buf, chunk << hw->info.shift);
+ if (nread == -1) {
+ if (errno == EINTR || errno == EAGAIN) {
+ goto again;
+ }
+ qesd_logerr (errno, "read failed\n");
+ return NULL;
+ }
+
+ if (nread != chunk << hw->info.shift) {
+ int rsamples = nread >> hw->info.shift;
+ int rbytes = rsamples << hw->info.shift;
+ if (rbytes != nread) {
+ dolog ("warning: Misaligned write %d (requested %d), "
+ "alignment %d\n",
+ rbytes, nread, hw->info.align + 1);
+ }
+ to_grab -= rsamples;
+ wpos = (wpos + rsamples) % hw->samples;
+ break;
+ }
+
+ hw->conv (hw->conv_buf + wpos, buf, nread >> hw->info.shift,
+ &nominal_volume);
+ wpos = (wpos + chunk) % hw->samples;
+ to_grab -= chunk;
+ }
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ return NULL;
+ }
+
+ esd->wpos = wpos;
+ esd->dead -= incr;
+ esd->incr += incr;
+ }
+
+ exit:
+ audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+ return NULL;
+}
+
+static int qesd_run_in (HWVoiceIn *hw)
+{
+ int live, incr, dead;
+ ESDVoiceIn *esd = (ESDVoiceIn *) hw;
+
+ if (audio_pt_lock (&esd->pt, AUDIO_FUNC)) {
+ return 0;
+ }
+
+ live = audio_pcm_hw_get_live_in (hw);
+ dead = hw->samples - live;
+ incr = audio_MIN (dead, esd->incr);
+ esd->incr -= incr;
+ esd->dead = dead - incr;
+ hw->wpos = esd->wpos;
+ if (esd->dead > 0) {
+ audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+ }
+ else {
+ audio_pt_unlock (&esd->pt, AUDIO_FUNC);
+ }
+ return incr;
+}
+
+static int qesd_read (SWVoiceIn *sw, void *buf, int len)
+{
+ return audio_pcm_sw_read (sw, buf, len);
+}
+
+static int qesd_init_in (HWVoiceIn *hw, audsettings_t *as)
+{
+ ESDVoiceIn *esd = (ESDVoiceIn *) hw;
+ audsettings_t obt_as = *as;
+ int esdfmt = ESD_STREAM | ESD_RECORD;
+ int result = -1;
+
+ /* shut down verbose debug spew */
+ if (!D_ACTIVE)
+ stdio_disable();
+
+ esdfmt |= (as->nchannels == 2) ? ESD_STEREO : ESD_MONO;
+ switch (as->fmt) {
+ case AUD_FMT_S8:
+ case AUD_FMT_U8:
+ esdfmt |= ESD_BITS8;
+ obt_as.fmt = AUD_FMT_U8;
+ break;
+
+ case AUD_FMT_S16:
+ case AUD_FMT_U16:
+ esdfmt |= ESD_BITS16;
+ obt_as.fmt = AUD_FMT_S16;
+ break;
+#if 0
+ case AUD_FMT_S32:
+ case AUD_FMT_U32:
+ dolog ("Will use 16 instead of 32 bit samples\n");
+ esdfmt |= ESD_BITS16;
+ obt_as.fmt = AUD_FMT_S16;
+ break;
+#endif
+ }
+ obt_as.endianness = 0;
+
+ audio_pcm_init_info (&hw->info, &obt_as);
+
+ hw->samples = conf.samples;
+ esd->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ if (!esd->pcm_buf) {
+ dolog ("Could not allocate buffer (%d bytes)\n",
+ hw->samples << hw->info.shift);
+ goto exit;
+ }
+
+ esd->fd = func_esd_record_stream (esdfmt, as->freq, conf.adc_host, NULL);
+ if (esd->fd < 0) {
+ if (conf.adc_host == NULL) {
+ esd->fd = func_esd_record_stream (esdfmt, as->freq, "localhost", NULL);
+ }
+ if (esd->fd < 0) {
+ qesd_logerr (errno, "esd_record_stream failed\n");
+ goto fail2;
+ }
+ }
+
+ if (audio_pt_init (&esd->pt, qesd_thread_in, esd, AUDIO_CAP, AUDIO_FUNC)) {
+ goto fail3;
+ }
+
+ result = 0; /* success */
+ goto exit;
+
+ fail3:
+ if (close (esd->fd)) {
+ qesd_logerr (errno, "%s: close on esd socket(%d) failed\n",
+ AUDIO_FUNC, esd->fd);
+ }
+ esd->fd = -1;
+
+ fail2:
+ qemu_free (esd->pcm_buf);
+ esd->pcm_buf = NULL;
+
+ exit:
+ if (!D_ACTIVE)
+ stdio_enable();
+
+ return result;
+}
+
+static void qesd_fini_in (HWVoiceIn *hw)
+{
+ void *ret;
+ ESDVoiceIn *esd = (ESDVoiceIn *) hw;
+
+ audio_pt_lock (&esd->pt, AUDIO_FUNC);
+ esd->done = 1;
+ audio_pt_unlock_and_signal (&esd->pt, AUDIO_FUNC);
+ audio_pt_join (&esd->pt, &ret, AUDIO_FUNC);
+
+ if (esd->fd >= 0) {
+ if (close (esd->fd)) {
+ qesd_logerr (errno, "failed to close esd socket\n");
+ }
+ esd->fd = -1;
+ }
+
+ audio_pt_fini (&esd->pt, AUDIO_FUNC);
+
+ qemu_free (esd->pcm_buf);
+ esd->pcm_buf = NULL;
+}
+
+static int qesd_ctl_in (HWVoiceIn *hw, int cmd, ...)
+{
+ (void) hw;
+ (void) cmd;
+ return 0;
+}
+
+/* common */
+static void *qesd_audio_init (void)
+{
+ void* result = NULL;
+
+ D("%s: entering", __FUNCTION__);
+
+ if (esd_lib == NULL) {
+ int fd;
+
+ esd_lib = dlopen( "libesd.so", RTLD_NOW );
+ if (esd_lib == NULL)
+ esd_lib = dlopen( "libesd.so.0", RTLD_NOW );
+
+ if (esd_lib == NULL) {
+ D("could not find libesd on this system");
+ goto Exit;
+ }
+
+ #undef ESD_FUNCTION
+ #define ESD_FUNCTION(ret,name,sig) \
+ do { \
+ (func_ ##name) = dlsym( esd_lib, STRINGIFY(name) ); \
+ if ((func_##name) == NULL) { \
+ D("could not find %s in libesd\n", STRINGIFY(name)); \
+ goto Fail; \
+ } \
+ } while (0);
+
+ ESD_SYMBOLS
+
+ fd = func_esd_open_sound(conf.dac_host);
+ if (fd < 0) {
+ D("%s: could not open direct sound server connection, trying localhost",
+ __FUNCTION__);
+ fd = func_esd_open_sound("localhost");
+ if (fd < 0) {
+ D("%s: could not open localhost sound server connection", __FUNCTION__);
+ goto Fail;
+ }
+ }
+
+ D("%s: EsounD server connection succeeded", __FUNCTION__);
+ /* func_esd_close(fd); */
+ }
+ result = &conf;
+ goto Exit;
+
+Fail:
+ D("%s: failed to open library", __FUNCTION__);
+ dlclose(esd_lib);
+ esd_lib = NULL;
+
+Exit:
+ return result;
+}
+
+static void qesd_audio_fini (void *opaque)
+{
+ (void) opaque;
+ if (esd_lib != NULL) {
+ dlclose(esd_lib);
+ esd_lib = NULL;
+ }
+ ldebug ("esd_fini");
+}
+
+struct audio_option qesd_options[] = {
+ {"SAMPLES", AUD_OPT_INT, &conf.samples,
+ "buffer size in samples", NULL, 0},
+
+ {"DIVISOR", AUD_OPT_INT, &conf.divisor,
+ "threshold divisor", NULL, 0},
+
+ {"DAC_HOST", AUD_OPT_STR, &conf.dac_host,
+ "playback host", NULL, 0},
+
+ {"ADC_HOST", AUD_OPT_STR, &conf.adc_host,
+ "capture host", NULL, 0},
+
+ {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+struct audio_pcm_ops qesd_pcm_ops = {
+ qesd_init_out,
+ qesd_fini_out,
+ qesd_run_out,
+ qesd_write,
+ qesd_ctl_out,
+
+ qesd_init_in,
+ qesd_fini_in,
+ qesd_run_in,
+ qesd_read,
+ qesd_ctl_in,
+};
+
+struct audio_driver esd_audio_driver = {
+ INIT_FIELD (name = ) "esd",
+ INIT_FIELD (descr = )
+ "EsounD audio (en.wikipedia.org/wiki/Esound)",
+ INIT_FIELD (options = ) qesd_options,
+ INIT_FIELD (init = ) qesd_audio_init,
+ INIT_FIELD (fini = ) qesd_audio_fini,
+ INIT_FIELD (pcm_ops = ) &qesd_pcm_ops,
+ INIT_FIELD (can_be_default = ) 1,
+ INIT_FIELD (max_voices_out = ) INT_MAX,
+ INIT_FIELD (max_voices_in = ) 1,
+ INIT_FIELD (voice_size_out = ) sizeof (ESDVoiceOut),
+ INIT_FIELD (voice_size_in = ) sizeof (ESDVoiceIn)
+};
diff --git a/audio/noaudio.c b/audio/noaudio.c
index a3423e5..3567e9c 100644
--- a/audio/noaudio.c
+++ b/audio/noaudio.c
@@ -159,7 +159,7 @@ static struct audio_pcm_ops no_pcm_ops = {
struct audio_driver no_audio_driver = {
INIT_FIELD (name = ) "none",
- INIT_FIELD (descr = ) "Timer based audio emulation",
+ INIT_FIELD (descr = ) "disabled audio",
INIT_FIELD (options = ) NULL,
INIT_FIELD (init = ) no_audio_init,
INIT_FIELD (fini = ) no_audio_fini,
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index 125e4c8..4d36e45 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -755,7 +755,7 @@ static struct audio_pcm_ops oss_pcm_ops = {
struct audio_driver oss_audio_driver = {
INIT_FIELD (name = ) "oss",
- INIT_FIELD (descr = ) "OSS http://www.opensound.com",
+ INIT_FIELD (descr = ) "OSS audio (www.opensound.com)",
INIT_FIELD (options = ) oss_options,
INIT_FIELD (init = ) oss_audio_init,
INIT_FIELD (fini = ) oss_audio_fini,
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
index f2a6896..83469ac 100644
--- a/audio/sdlaudio.c
+++ b/audio/sdlaudio.c
@@ -28,12 +28,17 @@
#define AUDIO_CAP "sdl"
#include "audio_int.h"
-typedef struct SDLVoiceOut {
- HWVoiceOut hw;
- int live;
- int rpos;
- int decr;
-} SDLVoiceOut;
+/* define DEBUG to 1 to dump audio debugging info at runtime to stderr */
+#define DEBUG 0
+
+/* define NEW_AUDIO to 1 to activate the new audio thread callback */
+#define NEW_AUDIO 1
+
+#if DEBUG
+# define D(...) fprintf(stderr, __VA_ARGS__)
+#else
+# define D(...) ((void)0)
+#endif
static struct {
int nb_samples;
@@ -41,12 +46,41 @@ static struct {
1024
};
+#if DEBUG
+int64_t start_time;
+#endif
+
+#if NEW_AUDIO
+
+#define AUDIO_BUFFER_SIZE (8192)
+
+typedef HWVoiceOut SDLVoiceOut;
+
+struct SDLAudioState {
+ int exit;
+ SDL_mutex* mutex;
+ int initialized;
+ uint8_t data[ AUDIO_BUFFER_SIZE ];
+ int pos, count;
+} glob_sdl;
+#else /* !NEW_AUDIO */
+
+typedef struct SDLVoiceOut {
+ HWVoiceOut hw;
+ int live;
+ int rpos;
+ int decr;
+} SDLVoiceOut;
+
struct SDLAudioState {
int exit;
SDL_mutex *mutex;
SDL_sem *sem;
int initialized;
} glob_sdl;
+
+#endif /* !NEW_AUDIO */
+
typedef struct SDLAudioState SDLAudioState;
static void GCC_FMT_ATTR (1, 2) sdl_logerr (const char *fmt, ...)
@@ -78,6 +112,7 @@ static int sdl_unlock (SDLAudioState *s, const char *forfn)
return 0;
}
+#if !NEW_AUDIO
static int sdl_post (SDLAudioState *s, const char *forfn)
{
if (SDL_SemPost (s->sem)) {
@@ -104,6 +139,7 @@ static int sdl_unlock_and_post (SDLAudioState *s, const char *forfn)
return sdl_post (s, forfn);
}
+#endif
static int aud_to_sdlfmt (audfmt_e fmt, int *shift)
{
@@ -190,13 +226,62 @@ static void sdl_close (SDLAudioState *s)
if (s->initialized) {
sdl_lock (s, "sdl_close");
s->exit = 1;
+#if NEW_AUDIO
+ sdl_unlock (s, "sdl_close");
+#else
sdl_unlock_and_post (s, "sdl_close");
+#endif
SDL_PauseAudio (1);
SDL_CloseAudio ();
s->initialized = 0;
}
}
+#if NEW_AUDIO
+
+static void sdl_callback (void *opaque, Uint8 *buf, int len)
+{
+#if DEBUG
+ int64_t now;
+#endif
+ SDLAudioState *s = &glob_sdl;
+
+ if (s->exit) {
+ return;
+ }
+
+ sdl_lock (s, "sdl_callback");
+#if DEBUG
+ if (s->count > 0) {
+ now = qemu_get_clock(vm_clock);
+ if (start_time == 0)
+ start_time = now;
+ now = now - start_time;
+ D( "R %6.3f: pos:%5d count:%5d len:%5d\n", now/1e9, s->pos, s->count, len );
+ }
+#endif
+ while (len > 0) {
+ int avail = audio_MIN( AUDIO_BUFFER_SIZE - s->pos, s->count );
+
+ if (avail == 0)
+ break;
+
+ if (avail > len)
+ avail = len;
+
+ memcpy( buf, s->data + s->pos, avail );
+ buf += avail;
+ len -= avail;
+
+ s->count -= avail;
+ s->pos += avail;
+ if (s->pos == AUDIO_BUFFER_SIZE)
+ s->pos = 0;
+ }
+ sdl_unlock (s, "sdl_callback");
+}
+
+#else /* !NEW_AUDIO */
static void sdl_callback (void *opaque, Uint8 *buf, int len)
{
SDLVoiceOut *sdl = opaque;
@@ -255,12 +340,69 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
}
/* dolog ("done len=%d\n", len); */
}
+#endif /* !NEW_AUDIO */
static int sdl_write_out (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
+#if NEW_AUDIO
+
+static int sdl_run_out (HWVoiceOut *hw)
+{
+ SDLAudioState *s = &glob_sdl;
+ int live, avail, end, total;
+
+ if (sdl_lock (s, "sdl_run_out")) {
+ return 0;
+ }
+ avail = AUDIO_BUFFER_SIZE - s->count;
+ end = s->pos + s->count;
+ if (end >= AUDIO_BUFFER_SIZE)
+ end -= AUDIO_BUFFER_SIZE;
+ sdl_unlock (s, "sdl_run_out");
+
+ live = audio_pcm_hw_get_live_out (hw);
+
+ total = 0;
+ while (live > 0) {
+ int bytes = audio_MIN(AUDIO_BUFFER_SIZE - end, avail);
+ int samples = bytes >> hw->info.shift;
+ int hwsamples = audio_MIN(hw->samples - hw->rpos, live);
+ uint8_t* dst = s->data + end;
+ st_sample_t* src = hw->mix_buf + hw->rpos;
+
+ if (samples == 0)
+ break;
+
+ if (samples > hwsamples) {
+ samples = hwsamples;
+ bytes = hwsamples << hw->info.shift;
+ }
+
+ hw->clip (dst, src, samples);
+ hw->rpos += samples;
+ if (hw->rpos == hw->samples)
+ hw->rpos = 0;
+
+ live -= samples;
+ avail -= bytes;
+ end += bytes;
+ if (end == AUDIO_BUFFER_SIZE)
+ end = 0;
+
+ total += bytes;
+ }
+
+ sdl_lock (s, "sdl_run_out");
+ s->count += total;
+ sdl_unlock (s, "sdl_run_out");
+
+ return total >> hw->info.shift;
+}
+
+#else /* !NEW_AUDIO */
static int sdl_run_out (HWVoiceOut *hw)
{
int decr, live;
@@ -294,6 +436,7 @@ static int sdl_run_out (HWVoiceOut *hw)
}
return decr;
}
+#endif /* !NEW_AUDIO */
static void sdl_fini_out (HWVoiceOut *hw)
{
@@ -302,6 +445,54 @@ static void sdl_fini_out (HWVoiceOut *hw)
sdl_close (&glob_sdl);
}
+#if DEBUG
+
+typedef struct { int value; const char* name; } MatchRec;
+typedef const MatchRec* Match;
+
+static const char*
+match_find( Match matches, int value, char* temp )
+{
+ int nn;
+ for ( nn = 0; matches[nn].name != NULL; nn++ ) {
+ if ( matches[nn].value == value )
+ return matches[nn].name;
+ }
+ sprintf( temp, "(%d?)", value );
+ return temp;
+}
+
+static const MatchRec sdl_audio_format_matches[] = {
+ { AUDIO_U8, "AUDIO_U8" },
+ { AUDIO_S8, "AUDIO_S8" },
+ { AUDIO_U16, "AUDIO_U16LE" },
+ { AUDIO_S16, "AUDIO_S16LE" },
+ { AUDIO_U16MSB, "AUDIO_U16BE" },
+ { AUDIO_S16MSB, "AUDIO_S16BE" },
+ { 0, NULL }
+};
+
+static void
+print_sdl_audiospec( SDL_AudioSpec* spec, const char* prefix )
+{
+ char temp[64];
+ const char* fmt;
+
+ if (!prefix)
+ prefix = "";
+
+ printf( "%s audiospec [freq:%d format:%s channels:%d samples:%d bytes:%d",
+ prefix,
+ spec->freq,
+ match_find( sdl_audio_format_matches, spec->format, temp ),
+ spec->channels,
+ spec->samples,
+ spec->size
+ );
+ printf( "]\n" );
+}
+#endif
+
static int sdl_init_out (HWVoiceOut *hw, audsettings_t *as)
{
SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
@@ -322,26 +513,38 @@ static int sdl_init_out (HWVoiceOut *hw, audsettings_t *as)
req.callback = sdl_callback;
req.userdata = sdl;
+#if DEBUG
+ print_sdl_audiospec( &req, "wanted" );
+#endif
+
if (sdl_open (&req, &obt)) {
return -1;
}
+#if DEBUG
+ print_sdl_audiospec( &req, "obtained" );
+#endif
+
err = sdl_to_audfmt (obt.format, &effective_fmt, &endianess);
if (err) {
sdl_close (s);
return -1;
}
- obt_as.freq = obt.freq;
- obt_as.nchannels = obt.channels;
- obt_as.fmt = effective_fmt;
+ obt_as.freq = obt.freq;
+ obt_as.nchannels = obt.channels;
+ obt_as.fmt = effective_fmt;
obt_as.endianness = endianess;
audio_pcm_init_info (&hw->info, &obt_as);
hw->samples = obt.samples;
+#if DEBUG
+ start_time = qemu_get_clock(vm_clock);
+#endif
+
s->initialized = 1;
- s->exit = 0;
+ s->exit = 0;
SDL_PauseAudio (0);
return 0;
}
@@ -377,7 +580,7 @@ static void *sdl_audio_init (void)
SDL_QuitSubSystem (SDL_INIT_AUDIO);
return NULL;
}
-
+#if !NEW_AUDIO
s->sem = SDL_CreateSemaphore (0);
if (!s->sem) {
sdl_logerr ("Failed to create SDL semaphore\n");
@@ -385,7 +588,7 @@ static void *sdl_audio_init (void)
SDL_QuitSubSystem (SDL_INIT_AUDIO);
return NULL;
}
-
+#endif
return s;
}
@@ -393,8 +596,16 @@ static void sdl_audio_fini (void *opaque)
{
SDLAudioState *s = opaque;
sdl_close (s);
- SDL_DestroySemaphore (s->sem);
- SDL_DestroyMutex (s->mutex);
+#if !NEW_AUDIO
+ if (s->sem) {
+ SDL_DestroySemaphore (s->sem);
+ s->sem = NULL;
+ }
+#endif
+ if (s->mutex) {
+ SDL_DestroyMutex (s->mutex);
+ s->mutex = NULL;
+ }
SDL_QuitSubSystem (SDL_INIT_AUDIO);
}
@@ -420,7 +631,7 @@ static struct audio_pcm_ops sdl_pcm_ops = {
struct audio_driver sdl_audio_driver = {
INIT_FIELD (name = ) "sdl",
- INIT_FIELD (descr = ) "SDL http://www.libsdl.org",
+ INIT_FIELD (descr = ) "SDL audio (www.libsdl.org)",
INIT_FIELD (options = ) sdl_options,
INIT_FIELD (init = ) sdl_audio_init,
INIT_FIELD (fini = ) sdl_audio_fini,
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index c359fc4..7d4fed1 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -1,6 +1,7 @@
/*
* QEMU WAV audio driver
*
+ * Copyright (c) 2007 The Android Open Source Project
* Copyright (c) 2004-2005 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -26,6 +27,10 @@
#define AUDIO_CAP "wav"
#include "audio_int.h"
+#define WAV_AUDIO_IN 1
+
+/** VOICE OUT (Saving to a .WAV file)
+ **/
typedef struct WAVVoiceOut {
HWVoiceOut hw;
QEMUFile *f;
@@ -37,7 +42,7 @@ typedef struct WAVVoiceOut {
static struct {
audsettings_t settings;
const char *wav_path;
-} conf = {
+} conf_out = {
{
44100,
2,
@@ -46,7 +51,7 @@ static struct {
"qemu.wav"
};
-static int wav_run_out (HWVoiceOut *hw)
+static int wav_out_run (HWVoiceOut *hw)
{
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
int rpos, live, decr, samples;
@@ -91,7 +96,7 @@ static int wav_run_out (HWVoiceOut *hw)
return decr;
}
-static int wav_write_out (SWVoiceOut *sw, void *buf, int len)
+static int wav_out_write (SWVoiceOut *sw, void *buf, int len)
{
return audio_pcm_sw_write (sw, buf, len);
}
@@ -106,7 +111,7 @@ static void le_store (uint8_t *buf, uint32_t val, int len)
}
}
-static int wav_init_out (HWVoiceOut *hw, audsettings_t *as)
+static int wav_out_init (HWVoiceOut *hw, audsettings_t *as)
{
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
int bits16 = 0, stereo = 0;
@@ -116,7 +121,7 @@ static int wav_init_out (HWVoiceOut *hw, audsettings_t *as)
0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
};
- audsettings_t wav_as = conf.settings;
+ audsettings_t wav_as = conf_out.settings;
(void) as;
@@ -151,10 +156,10 @@ static int wav_init_out (HWVoiceOut *hw, audsettings_t *as)
le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
le_store (hdr + 32, 1 << (bits16 + stereo), 2);
- wav->f = fopen (conf.wav_path, "wb");
+ wav->f = fopen (conf_out.wav_path, "wb");
if (!wav->f) {
dolog ("Failed to open wave file `%s'\nReason: %s\n",
- conf.wav_path, strerror (errno));
+ conf_out.wav_path, strerror (errno));
qemu_free (wav->pcm_buf);
wav->pcm_buf = NULL;
return -1;
@@ -164,7 +169,7 @@ static int wav_init_out (HWVoiceOut *hw, audsettings_t *as)
return 0;
}
-static void wav_fini_out (HWVoiceOut *hw)
+static void wav_out_fini (HWVoiceOut *hw)
{
WAVVoiceOut *wav = (WAVVoiceOut *) hw;
uint8_t rlen[4];
@@ -192,16 +197,212 @@ static void wav_fini_out (HWVoiceOut *hw)
wav->pcm_buf = NULL;
}
-static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
+static int wav_out_ctl (HWVoiceOut *hw, int cmd, ...)
+{
+ (void) hw;
+ (void) cmd;
+ return 0;
+}
+
+
+#if WAV_AUDIO_IN
+
+/** WAV IN (Reading from a .WAV file)
+ **/
+
+ static struct {
+ const char *wav_path;
+} conf_in = {
+ "qemu.wav"
+};
+
+typedef struct WAVVoiceIn {
+ HWVoiceIn hw;
+ QEMUFile* f;
+ int64_t old_ticks;
+ void* pcm_buf;
+ int total_samples;
+ int total_size;
+} WAVVoiceIn;
+
+
+static int
+le_read( const uint8_t* p, int size ) {
+ int shift = 0;
+ int result = 0;
+ for ( ; size > 0; size-- ) {
+ result = result | (p[0] << shift);
+ p += 1;
+ shift += 8;
+ }
+ return result;
+}
+
+static int
+wav_in_init (HWVoiceIn *hw, audsettings_t *as)
+{
+ WAVVoiceIn* wav = (WAVVoiceIn *) hw;
+ const char* path = conf_in.wav_path;
+ uint8_t hdr[44];
+ audsettings_t wav_as = *as;
+ int nchannels, freq, format, bits;
+
+ wav->f = fopen (path, "rb");
+ if (wav->f == NULL) {
+ dolog("Failed to open wave file '%s'\nReason: %s\n", path,
+ strerror(errno));
+ return -1;
+ }
+
+ if (qemu_get_buffer (wav->f, hdr, sizeof(hdr)) != (int)sizeof(hdr)) {
+ dolog("File '%s' to be a .wav file\n", path);
+ goto Fail;
+ }
+
+ /* check that this is a wave file */
+ if ( hdr[0] != 'R' || hdr[1] != 'I' || hdr[2] != 'F' || hdr[3] != 'F' ||
+ hdr[8] != 'W' || hdr[9] != 'A' || hdr[10]!= 'V' || hdr[11]!= 'E' ||
+ hdr[12]!= 'f' || hdr[13]!= 'm' || hdr[14]!= 't' || hdr[15]!= ' ' ||
+ hdr[40]!= 'd' || hdr[41]!= 'a' || hdr[42]!= 't' || hdr[43]!= 'a') {
+ dolog("File '%s' is not a valid .wav file\n", path);
+ goto Fail;
+ }
+
+ nchannels = le_read( hdr+22, 2 );
+ freq = le_read( hdr+24, 4 );
+ format = le_read( hdr+32, 2 );
+ bits = le_read( hdr+34, 2 );
+
+ wav->total_size = le_read( hdr+40, 4 );
+
+ /* perform some sainty checks */
+ switch (nchannels) {
+ case 1:
+ case 2: break;
+ default:
+ dolog("unsupported number of channels (%d) in '%s'\n",
+ nchannels, path);
+ goto Fail;
+ }
+
+ switch (format) {
+ case 1:
+ case 2:
+ case 4: break;
+ default:
+ dolog("unsupported bytes per sample (%d) in '%s'\n",
+ format, path);
+ goto Fail;
+ }
+
+ if (format*8/nchannels != bits) {
+ dolog("invalid bits per sample (%d, expected %d) in '%s'\n",
+ bits, format*8/nchannels, path);
+ goto Fail;
+ }
+
+ wav_as.nchannels = nchannels;
+ wav_as.fmt = (bits == 8) ? AUD_FMT_U8 : AUD_FMT_S16;
+ wav_as.freq = freq;
+ wav_as.endianness = 0; /* always little endian */
+
+ audio_pcm_init_info (&hw->info, &wav_as);
+
+ hw->samples = 1024;
+ wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ if (!wav->pcm_buf) {
+ goto Fail;
+ }
+ return 0;
+
+Fail:
+ fclose (wav->f);
+ wav->f = NULL;
+ return -1;
+}
+
+
+static void wav_in_fini (HWVoiceIn *hw)
+{
+ WAVVoiceIn *wav = (WAVVoiceIn *) hw;
+
+ if (!wav->f) {
+ return;
+ }
+
+ fclose (wav->f);
+ wav->f = NULL;
+
+ qemu_free (wav->pcm_buf);
+ wav->pcm_buf = NULL;
+}
+
+static int wav_in_run (HWVoiceIn *hw)
+{
+ WAVVoiceIn* wav = (WAVVoiceIn *) hw;
+ int wpos, live, decr, samples;
+ uint8_t* src;
+ st_sample_t* dst;
+
+ int64_t now = qemu_get_clock (vm_clock);
+ int64_t ticks = now - wav->old_ticks;
+ int64_t bytes = (ticks * hw->info.bytes_per_second) / ticks_per_sec;
+
+ if (bytes > INT_MAX) {
+ samples = INT_MAX >> hw->info.shift;
+ }
+ else {
+ samples = bytes >> hw->info.shift;
+ }
+
+ live = audio_pcm_hw_get_live_in (hw);
+ if (!live) {
+ return 0;
+ }
+
+ wav->old_ticks = now;
+
+ decr = audio_MIN (live, samples);
+ samples = decr;
+ wpos = hw->wpos;
+ while (samples) {
+ int left_till_end_samples = hw->samples - wpos;
+ int convert_samples = audio_MIN (samples, left_till_end_samples);
+
+ dst = hw->conv_buf + wpos;
+ src = advance (wav->pcm_buf, wpos << hw->info.shift);
+
+ qemu_get_buffer (wav->f, src, convert_samples << hw->info.shift);
+ memcpy (dst, src, convert_samples << hw->info.shift);
+
+ wpos = (wpos + convert_samples) % hw->samples;
+ samples -= convert_samples;
+ wav->total_samples += convert_samples;
+ }
+
+ hw->wpos = wpos;
+ return decr;
+}
+
+static int wav_in_read (SWVoiceIn *sw, void *buf, int len)
+{
+ return audio_pcm_sw_read (sw, buf, len);
+}
+
+static int wav_in_ctl (HWVoiceIn *hw, int cmd, ...)
{
(void) hw;
(void) cmd;
return 0;
}
+#endif /* WAV_AUDIO_IN */
+
+/** COMMON CODE
+ **/
static void *wav_audio_init (void)
{
- return &conf;
+ return &conf_out;
}
static void wav_audio_fini (void *opaque)
@@ -211,45 +412,65 @@ static void wav_audio_fini (void *opaque)
}
struct audio_option wav_options[] = {
- {"FREQUENCY", AUD_OPT_INT, &conf.settings.freq,
+ {"FREQUENCY", AUD_OPT_INT, &conf_out.settings.freq,
"Frequency", NULL, 0},
- {"FORMAT", AUD_OPT_FMT, &conf.settings.fmt,
+ {"FORMAT", AUD_OPT_FMT, &conf_out.settings.fmt,
"Format", NULL, 0},
- {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
+ {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf_out.settings.nchannels,
"Number of channels (1 - mono, 2 - stereo)", NULL, 0},
- {"PATH", AUD_OPT_STR, &conf.wav_path,
- "Path to wave file", NULL, 0},
+ {"PATH", AUD_OPT_STR, &conf_out.wav_path,
+ "Path to output .wav file", NULL, 0},
+
+#if WAV_AUDIO_IN
+ {"IN_PATH", AUD_OPT_STR, &conf_in.wav_path,
+ "Path to input .wav file", NULL, 0},
+#endif
{NULL, 0, NULL, NULL, NULL, 0}
};
struct audio_pcm_ops wav_pcm_ops = {
- wav_init_out,
- wav_fini_out,
- wav_run_out,
- wav_write_out,
- wav_ctl_out,
-
+ wav_out_init,
+ wav_out_fini,
+ wav_out_run,
+ wav_out_write,
+ wav_out_ctl,
+
+#if WAV_AUDIO_IN
+ wav_in_init,
+ wav_in_fini,
+ wav_in_run,
+ wav_in_read,
+ wav_in_ctl
+#else
NULL,
NULL,
NULL,
NULL,
NULL
+#endif
};
struct audio_driver wav_audio_driver = {
INIT_FIELD (name = ) "wav",
INIT_FIELD (descr = )
- "WAV renderer http://wikipedia.org/wiki/WAV",
+ "WAV file read/write (www.wikipedia.org/wiki/WAV)",
INIT_FIELD (options = ) wav_options,
INIT_FIELD (init = ) wav_audio_init,
INIT_FIELD (fini = ) wav_audio_fini,
INIT_FIELD (pcm_ops = ) &wav_pcm_ops,
INIT_FIELD (can_be_default = ) 0,
+#if WAV_AUDIO_IN
+ INIT_FIELD (max_voices_in = ) 1,
+ INIT_FIELD (max_voices_out = ) 1,
+ INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut),
+ INIT_FIELD (voice_size_in = ) sizeof (WAVVoiceIn)
+#else
INIT_FIELD (max_voices_out = ) 1,
INIT_FIELD (max_voices_in = ) 0,
INIT_FIELD (voice_size_out = ) sizeof (WAVVoiceOut),
INIT_FIELD (voice_size_in = ) 0
+#endif
};
diff --git a/audio/wavcapture.c b/audio/wavcapture.c
index 0f6f7bf..748b580 100644
--- a/audio/wavcapture.c
+++ b/audio/wavcapture.c
@@ -103,8 +103,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
}
if (nchannels != 1 && nchannels != 2) {
- term_printf ("incorrect channel count %d, must be 1 or 2\n",
- nchannels);
+ term_printf ("incorrect channel count %d, must be 1 or 2\n", bits);
return -1;
}
@@ -122,8 +121,7 @@ int wav_start_capture (CaptureState *s, const char *path, int freq,
wav = qemu_mallocz (sizeof (*wav));
if (!wav) {
- term_printf ("Could not allocate memory for wav capture (%zu bytes)",
- sizeof (*wav));
+ AUD_log ("wav", "Could not allocate memory (%zu bytes)", sizeof (*wav));
return -1;
}
diff --git a/audio/winaudio.c b/audio/winaudio.c
new file mode 100644
index 0000000..63cd351
--- /dev/null
+++ b/audio/winaudio.c
@@ -0,0 +1,668 @@
+/*
+ * 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 "vl.h"
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <mmsystem.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
+
+#if 1
+# define D_ACTIVE 1
+#else
+# define D_ACTIVE DEBUG
+#endif
+
+#if DEBUG
+# define D(...) do{ if (D_ACTIVE) printf(__VA_ARGS__); } while(0)
+#else
+# define D(...) ((void)0)
+#endif
+
+static struct {
+ int nb_samples;
+} conf = {
+ 1024
+};
+
+#if DEBUG
+int64_t start_time;
+int64_t last_time;
+#endif
+
+#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 Win32 callback that is called when a buffer has finished playing */
+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, audsettings_t *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;
+ 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) {
+ 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);
+ st_sample_t* 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 xxDEBUG
+ 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 Win32 callback that is called when a buffer has finished playing */
+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 done playing" 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;
+ //D(".%c",s->read_count + '0'); fflush(stdout);
+ 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, audsettings_t *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;
+ 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) {
+#if 0
+ static int counter;
+ if (++counter == 100) {
+ D("0"); fflush(stdout);
+ counter = 0;
+ }
+#endif
+ return 0;
+ }
+
+ EnterCriticalSection( &s->lock );
+ has_buffer = (s->read_count > 0);
+ LeaveCriticalSection( &s->lock );
+
+ if (has_buffer > 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);
+ st_sample_t* 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
+ 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 win_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)
+};