audio merge (malc)

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1636 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
bellard 2005-11-20 16:24:34 +00:00
parent 5e941d4b51
commit 571ec3d68d
6 changed files with 406 additions and 458 deletions

View File

@ -31,15 +31,12 @@ typedef struct ALSAVoiceOut {
HWVoiceOut hw; HWVoiceOut hw;
void *pcm_buf; void *pcm_buf;
snd_pcm_t *handle; snd_pcm_t *handle;
int can_pause;
int was_enabled;
} ALSAVoiceOut; } ALSAVoiceOut;
typedef struct ALSAVoiceIn { typedef struct ALSAVoiceIn {
HWVoiceIn hw; HWVoiceIn hw;
snd_pcm_t *handle; snd_pcm_t *handle;
void *pcm_buf; void *pcm_buf;
int can_pause;
} ALSAVoiceIn; } ALSAVoiceIn;
static struct { static struct {
@ -58,6 +55,7 @@ static struct {
int buffer_size_out_overriden; int buffer_size_out_overriden;
int period_size_out_overriden; int period_size_out_overriden;
int verbose;
} conf = { } conf = {
#ifdef HIGH_LATENCY #ifdef HIGH_LATENCY
.size_in_usec_in = 1, .size_in_usec_in = 1,
@ -73,8 +71,8 @@ static struct {
#else #else
#define DEFAULT_BUFFER_SIZE 1024 #define DEFAULT_BUFFER_SIZE 1024
#define DEFAULT_PERIOD_SIZE 256 #define DEFAULT_PERIOD_SIZE 256
.buffer_size_in = DEFAULT_BUFFER_SIZE, .buffer_size_in = DEFAULT_BUFFER_SIZE * 4,
.period_size_in = DEFAULT_PERIOD_SIZE, .period_size_in = DEFAULT_PERIOD_SIZE * 4,
.buffer_size_out = DEFAULT_BUFFER_SIZE, .buffer_size_out = DEFAULT_BUFFER_SIZE,
.period_size_out = DEFAULT_PERIOD_SIZE, .period_size_out = DEFAULT_PERIOD_SIZE,
.buffer_size_in_overriden = 0, .buffer_size_in_overriden = 0,
@ -82,7 +80,8 @@ static struct {
.period_size_in_overriden = 0, .period_size_in_overriden = 0,
.period_size_out_overriden = 0, .period_size_out_overriden = 0,
#endif #endif
.threshold = 0 .threshold = 0,
.verbose = 0
}; };
struct alsa_params_req { struct alsa_params_req {
@ -97,7 +96,6 @@ struct alsa_params_obt {
int freq; int freq;
audfmt_e fmt; audfmt_e fmt;
int nchannels; int nchannels;
int can_pause;
snd_pcm_uframes_t samples; snd_pcm_uframes_t samples;
}; };
@ -474,12 +472,6 @@ static int alsa_open (int in, struct alsa_params_req *req,
goto err; goto err;
} }
obt->can_pause = snd_pcm_hw_params_can_pause (hw_params);
if (obt->can_pause < 0) {
alsa_logerr (err, "Could not get pause capability for %s\n", typ);
obt->can_pause = 0;
}
if (!in && conf.threshold) { if (!in && conf.threshold) {
snd_pcm_uframes_t threshold; snd_pcm_uframes_t threshold;
int bytes_per_sec; int bytes_per_sec;
@ -527,6 +519,28 @@ static int alsa_recover (snd_pcm_t *handle)
return 0; return 0;
} }
static snd_pcm_sframes_t alsa_get_avail (snd_pcm_t *handle)
{
snd_pcm_sframes_t avail;
avail = snd_pcm_avail_update (handle);
if (avail < 0) {
if (avail == -EPIPE) {
if (!alsa_recover (handle)) {
avail = snd_pcm_avail_update (handle);
}
}
if (avail < 0) {
alsa_logerr (avail,
"Could not obtain number of available frames\n");
return -1;
}
}
return avail;
}
static int alsa_run_out (HWVoiceOut *hw) static int alsa_run_out (HWVoiceOut *hw)
{ {
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
@ -541,57 +555,53 @@ static int alsa_run_out (HWVoiceOut *hw)
return 0; return 0;
} }
avail = snd_pcm_avail_update (alsa->handle); avail = alsa_get_avail (alsa->handle);
if (avail < 0) { if (avail < 0) {
if (avail == -EPIPE) { dolog ("Could not get number of available playback frames\n");
if (!alsa_recover (alsa->handle)) {
avail = snd_pcm_avail_update (alsa->handle);
if (avail >= 0) {
goto ok;
}
}
}
alsa_logerr (avail, "Could not get amount free space\n");
return 0; return 0;
} }
ok:
decr = audio_MIN (live, avail); decr = audio_MIN (live, avail);
samples = decr; samples = decr;
rpos = hw->rpos; rpos = hw->rpos;
while (samples) { while (samples) {
int left_till_end_samples = hw->samples - rpos; int left_till_end_samples = hw->samples - rpos;
int convert_samples = audio_MIN (samples, left_till_end_samples); int len = audio_MIN (samples, left_till_end_samples);
snd_pcm_sframes_t written; snd_pcm_sframes_t written;
src = hw->mix_buf + rpos; src = hw->mix_buf + rpos;
dst = advance (alsa->pcm_buf, rpos << hw->info.shift); dst = advance (alsa->pcm_buf, rpos << hw->info.shift);
hw->clip (dst, src, convert_samples); hw->clip (dst, src, len);
while (convert_samples) { while (len) {
written = snd_pcm_writei (alsa->handle, dst, convert_samples); written = snd_pcm_writei (alsa->handle, dst, len);
if (written < 0) { if (written <= 0) {
switch (written) { switch (written) {
case -EPIPE: case 0:
if (!alsa_recover (alsa->handle)) { if (conf.verbose) {
continue; dolog ("Failed to write %d frames (wrote zero)\n", len);
} }
dolog ("Failed to write %d frames to %p, "
"handle %p not prepared\n",
convert_samples,
dst,
alsa->handle);
goto exit; goto exit;
case -EAGAIN: case -EPIPE:
if (alsa_recover (alsa->handle)) {
alsa_logerr (written, "Failed to write %d frames\n",
len);
goto exit;
}
if (conf.verbose) {
dolog ("Recovering from playback xrun\n");
}
continue; continue;
case -EAGAIN:
goto exit;
default: default:
alsa_logerr (written, "Failed to write %d frames to %p\n", alsa_logerr (written, "Failed to write %d frames to %p\n",
convert_samples, dst); len, dst);
goto exit; goto exit;
} }
} }
@ -599,7 +609,7 @@ static int alsa_run_out (HWVoiceOut *hw)
mixeng_clear (src, written); mixeng_clear (src, written);
rpos = (rpos + written) % hw->samples; rpos = (rpos + written) % hw->samples;
samples -= written; samples -= written;
convert_samples -= written; len -= written;
dst = advance (dst, written << hw->info.shift); dst = advance (dst, written << hw->info.shift);
src += written; src += written;
} }
@ -659,7 +669,6 @@ static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as)
&obt_as, &obt_as,
audio_need_to_swap_endian (endianness) audio_need_to_swap_endian (endianness)
); );
alsa->can_pause = obt.can_pause;
hw->samples = obt.samples; hw->samples = obt.samples;
alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift); alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift);
@ -671,46 +680,46 @@ static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as)
} }
alsa->handle = handle; alsa->handle = handle;
alsa->was_enabled = 0; return 0;
}
static int alsa_voice_ctl (snd_pcm_t *handle, const char *typ, int pause)
{
int err;
if (pause) {
err = snd_pcm_drop (handle);
if (err < 0) {
alsa_logerr (err, "Could not stop %s", typ);
return -1;
}
}
else {
err = snd_pcm_prepare (handle);
if (err < 0) {
alsa_logerr (err, "Could not prepare handle for %s", typ);
return -1;
}
}
return 0; return 0;
} }
static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...) static int alsa_ctl_out (HWVoiceOut *hw, int cmd, ...)
{ {
int err;
ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw; ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
switch (cmd) { switch (cmd) {
case VOICE_ENABLE: case VOICE_ENABLE:
ldebug ("enabling voice\n"); ldebug ("enabling voice\n");
audio_pcm_info_clear_buf (&hw->info, alsa->pcm_buf, hw->samples); return alsa_voice_ctl (alsa->handle, "playback", 0);
if (alsa->can_pause) {
/* Why this was_enabled madness is needed at all?? */
if (alsa->was_enabled) {
err = snd_pcm_pause (alsa->handle, 0);
if (err < 0) {
alsa_logerr (err, "Failed to resume playing\n");
/* not fatal really */
}
}
else {
alsa->was_enabled = 1;
}
}
break;
case VOICE_DISABLE: case VOICE_DISABLE:
ldebug ("disabling voice\n"); ldebug ("disabling voice\n");
if (alsa->can_pause) { return alsa_voice_ctl (alsa->handle, "playback", 1);
err = snd_pcm_pause (alsa->handle, 1);
if (err < 0) {
alsa_logerr (err, "Failed to stop playing\n");
/* not fatal really */
}
}
break;
} }
return 0;
return -1;
} }
static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as) static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as)
@ -749,7 +758,6 @@ static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as)
&obt_as, &obt_as,
audio_need_to_swap_endian (endianness) audio_need_to_swap_endian (endianness)
); );
alsa->can_pause = obt.can_pause;
hw->samples = obt.samples; hw->samples = obt.samples;
alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift); alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
@ -783,6 +791,7 @@ static int alsa_run_in (HWVoiceIn *hw)
int i; int i;
int live = audio_pcm_hw_get_live_in (hw); int live = audio_pcm_hw_get_live_in (hw);
int dead = hw->samples - live; int dead = hw->samples - live;
int decr;
struct { struct {
int add; int add;
int len; int len;
@ -790,21 +799,35 @@ static int alsa_run_in (HWVoiceIn *hw)
{ hw->wpos, 0 }, { hw->wpos, 0 },
{ 0, 0 } { 0, 0 }
}; };
snd_pcm_sframes_t avail;
snd_pcm_uframes_t read_samples = 0; snd_pcm_uframes_t read_samples = 0;
if (!dead) { if (!dead) {
return 0; return 0;
} }
if (hw->wpos + dead > hw->samples) { avail = alsa_get_avail (alsa->handle);
bufs[0].len = (hw->samples - hw->wpos); if (avail < 0) {
bufs[1].len = (dead - (hw->samples - hw->wpos)); dolog ("Could not get number of captured frames\n");
} return 0;
else {
bufs[0].len = dead;
} }
if (!avail && (snd_pcm_state (alsa->handle) == SND_PCM_STATE_PREPARED)) {
avail = hw->samples;
}
decr = audio_MIN (dead, avail);
if (!decr) {
return 0;
}
if (hw->wpos + decr > hw->samples) {
bufs[0].len = (hw->samples - hw->wpos);
bufs[1].len = (decr - (hw->samples - hw->wpos));
}
else {
bufs[0].len = decr;
}
for (i = 0; i < 2; ++i) { for (i = 0; i < 2; ++i) {
void *src; void *src;
@ -820,24 +843,27 @@ static int alsa_run_in (HWVoiceIn *hw)
while (len) { while (len) {
nread = snd_pcm_readi (alsa->handle, src, len); nread = snd_pcm_readi (alsa->handle, src, len);
if (nread < 0) { if (nread <= 0) {
switch (nread) { switch (nread) {
case -EPIPE: case 0:
if (!alsa_recover (alsa->handle)) { if (conf.verbose) {
continue; dolog ("Failed to read %ld frames (read zero)\n", len);
} }
dolog (
"Failed to read %ld frames from %p, "
"handle %p not prepared\n",
len,
src,
alsa->handle
);
goto exit; goto exit;
case -EAGAIN: case -EPIPE:
if (alsa_recover (alsa->handle)) {
alsa_logerr (nread, "Failed to read %ld frames\n", len);
goto exit;
}
if (conf.verbose) {
dolog ("Recovering from capture xrun\n");
}
continue; continue;
case -EAGAIN:
goto exit;
default: default:
alsa_logerr ( alsa_logerr (
nread, nread,
@ -871,9 +897,19 @@ static int alsa_read (SWVoiceIn *sw, void *buf, int size)
static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...) static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
{ {
(void) hw; ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
(void) cmd;
return 0; switch (cmd) {
case VOICE_ENABLE:
ldebug ("enabling voice\n");
return alsa_voice_ctl (alsa->handle, "capture", 0);
case VOICE_DISABLE:
ldebug ("disabling voice\n");
return alsa_voice_ctl (alsa->handle, "capture", 1);
}
return -1;
} }
static void *alsa_audio_init (void) static void *alsa_audio_init (void)
@ -909,6 +945,10 @@ static struct audio_option alsa_options[] = {
{"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in, {"ADC_DEV", AUD_OPT_STR, &conf.pcm_name_in,
"ADC device name", NULL, 0}, "ADC device name", NULL, 0},
{"VERBOSE", AUD_OPT_BOOL, &conf.verbose,
"Behave in a more verbose way", NULL, 0},
{NULL, 0, NULL, NULL, NULL, 0} {NULL, 0, NULL, NULL, NULL, 0}
}; };

View File

@ -96,7 +96,7 @@ static struct {
{ 0 }, /* period */ { 0 }, /* period */
0, /* plive */ 0, /* plive */
0 0 /* log_to_monitor */
}; };
static AudioState glob_audio_state; static AudioState glob_audio_state;
@ -623,25 +623,6 @@ void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
/* /*
* Hard voice (capture) * Hard voice (capture)
*/ */
static void audio_pcm_hw_free_resources_in (HWVoiceIn *hw)
{
if (hw->conv_buf) {
qemu_free (hw->conv_buf);
}
hw->conv_buf = NULL;
}
static int audio_pcm_hw_alloc_resources_in (HWVoiceIn *hw)
{
hw->conv_buf = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (st_sample_t));
if (!hw->conv_buf) {
dolog ("Could not allocate ADC conversion buffer (%d samples)\n",
hw->samples);
return -1;
}
return 0;
}
static int audio_pcm_hw_find_min_in (HWVoiceIn *hw) static int audio_pcm_hw_find_min_in (HWVoiceIn *hw)
{ {
SWVoiceIn *sw; SWVoiceIn *sw;
@ -668,64 +649,6 @@ int audio_pcm_hw_get_live_in (HWVoiceIn *hw)
/* /*
* Soft voice (capture) * Soft voice (capture)
*/ */
static void audio_pcm_sw_free_resources_in (SWVoiceIn *sw)
{
if (sw->conv_buf) {
qemu_free (sw->conv_buf);
}
if (sw->rate) {
st_rate_stop (sw->rate);
}
sw->conv_buf = NULL;
sw->rate = NULL;
}
static int audio_pcm_sw_alloc_resources_in (SWVoiceIn *sw)
{
int samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
sw->conv_buf = audio_calloc (AUDIO_FUNC, samples, sizeof (st_sample_t));
if (!sw->conv_buf) {
dolog ("Could not allocate buffer for `%s' (%d samples)\n",
SW_NAME (sw), samples);
return -1;
}
sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq);
if (!sw->rate) {
qemu_free (sw->conv_buf);
sw->conv_buf = NULL;
return -1;
}
return 0;
}
static int audio_pcm_sw_init_in (
SWVoiceIn *sw,
HWVoiceIn *hw,
const char *name,
audsettings_t *as
)
{
/* None of the cards emulated by QEMU are big-endian
hence following shortcut */
audio_pcm_init_info (&sw->info, as, audio_need_to_swap_endian (0));
sw->hw = hw;
sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq;
sw->clip =
mixeng_clip
[sw->info.nchannels == 2]
[sw->info.sign]
[sw->info.swap_endian]
[sw->info.bits == 16];
sw->name = qemu_strdup (name);
audio_pcm_sw_free_resources_in (sw);
return audio_pcm_sw_alloc_resources_in (sw);
}
static int audio_pcm_sw_get_rpos_in (SWVoiceIn *sw) static int audio_pcm_sw_get_rpos_in (SWVoiceIn *sw)
{ {
HWVoiceIn *hw = sw->hw; HWVoiceIn *hw = sw->hw;
@ -750,7 +673,7 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size)
{ {
HWVoiceIn *hw = sw->hw; HWVoiceIn *hw = sw->hw;
int samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0; int samples, live, ret = 0, swlim, isamp, osamp, rpos, total = 0;
st_sample_t *src, *dst = sw->conv_buf; st_sample_t *src, *dst = sw->buf;
rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples; rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples;
@ -794,7 +717,7 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size)
total += isamp; total += isamp;
} }
sw->clip (buf, sw->conv_buf, ret); sw->clip (buf, sw->buf, ret);
sw->total_hw_samples_acquired += total; sw->total_hw_samples_acquired += total;
return ret << sw->info.shift; return ret << sw->info.shift;
} }
@ -802,27 +725,6 @@ int audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int size)
/* /*
* Hard voice (playback) * Hard voice (playback)
*/ */
static void audio_pcm_hw_free_resources_out (HWVoiceOut *hw)
{
if (hw->mix_buf) {
qemu_free (hw->mix_buf);
}
hw->mix_buf = NULL;
}
static int audio_pcm_hw_alloc_resources_out (HWVoiceOut *hw)
{
hw->mix_buf = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (st_sample_t));
if (!hw->mix_buf) {
dolog ("Could not allocate DAC mixing buffer (%d samples)\n",
hw->samples);
return -1;
}
return 0;
}
static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep) static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep)
{ {
SWVoiceOut *sw; SWVoiceOut *sw;
@ -876,66 +778,6 @@ int audio_pcm_hw_get_live_out (HWVoiceOut *hw)
/* /*
* Soft voice (playback) * Soft voice (playback)
*/ */
static void audio_pcm_sw_free_resources_out (SWVoiceOut *sw)
{
if (sw->buf) {
qemu_free (sw->buf);
}
if (sw->rate) {
st_rate_stop (sw->rate);
}
sw->buf = NULL;
sw->rate = NULL;
}
static int audio_pcm_sw_alloc_resources_out (SWVoiceOut *sw)
{
sw->buf = audio_calloc (AUDIO_FUNC, sw->hw->samples, sizeof (st_sample_t));
if (!sw->buf) {
dolog ("Could not allocate buffer for `%s' (%d samples)\n",
SW_NAME (sw), sw->hw->samples);
return -1;
}
sw->rate = st_rate_start (sw->info.freq, sw->hw->info.freq);
if (!sw->rate) {
qemu_free (sw->buf);
sw->buf = NULL;
return -1;
}
return 0;
}
static int audio_pcm_sw_init_out (
SWVoiceOut *sw,
HWVoiceOut *hw,
const char *name,
audsettings_t *as
)
{
/* None of the cards emulated by QEMU are big-endian
hence following shortcut */
audio_pcm_init_info (&sw->info, as, audio_need_to_swap_endian (0));
sw->hw = hw;
sw->empty = 1;
sw->active = 0;
sw->ratio = ((int64_t) sw->hw->info.freq << 32) / sw->info.freq;
sw->total_hw_samples_mixed = 0;
sw->conv =
mixeng_conv
[sw->info.nchannels == 2]
[sw->info.sign]
[sw->info.swap_endian]
[sw->info.bits == 16];
sw->name = qemu_strdup (name);
audio_pcm_sw_free_resources_out (sw);
return audio_pcm_sw_alloc_resources_out (sw);
}
int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size) int audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int size)
{ {
int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck; int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck;
@ -1316,6 +1158,16 @@ static void audio_run_in (AudioState *s)
} }
} }
static void audio_timer (void *opaque)
{
AudioState *s = opaque;
audio_run_out (s);
audio_run_in (s);
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
}
static struct audio_option audio_options[] = { static struct audio_option audio_options[] = {
/* DAC */ /* DAC */
{"DAC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_out.enabled, {"DAC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_out.enabled,
@ -1356,13 +1208,31 @@ static struct audio_option audio_options[] = {
{"PLIVE", AUD_OPT_BOOL, &conf.plive, {"PLIVE", AUD_OPT_BOOL, &conf.plive,
"(undocumented)", NULL, 0}, "(undocumented)", NULL, 0},
{"LOG_TO_MONITOR", AUD_OPT_BOOL, &conf.log_to_monitor, {"LOG_TO_MONITOR", AUD_OPT_BOOL, &conf.log_to_monitor,
"print logging messages to montior instead of stderr", NULL, 0}, "print logging messages to montior instead of stderr", NULL, 0},
{NULL, 0, NULL, NULL, NULL, 0} {NULL, 0, NULL, NULL, NULL, 0}
}; };
static void audio_pp_nb_voices (const char *typ, int nb)
{
switch (nb) {
case 0:
printf ("Does not support %s\n", typ);
break;
case 1:
printf ("One %s voice\n", typ);
break;
case INT_MAX:
printf ("Theoretically supports many %s voices\n", typ);
break;
default:
printf ("Theoretically supports upto %d %s voices\n", nb, typ);
break;
}
}
void AUD_help (void) void AUD_help (void)
{ {
size_t i; size_t i;
@ -1387,37 +1257,8 @@ void AUD_help (void)
printf ("Name: %s\n", d->name); printf ("Name: %s\n", d->name);
printf ("Description: %s\n", d->descr); printf ("Description: %s\n", d->descr);
switch (d->max_voices_out) { audio_pp_nb_voices ("playback", d->max_voices_out);
case 0: audio_pp_nb_voices ("capture", d->max_voices_in);
printf ("Does not support DAC\n");
break;
case 1:
printf ("One DAC voice\n");
break;
case INT_MAX:
printf ("Theoretically supports many DAC voices\n");
break;
default:
printf ("Theoretically supports upto %d DAC voices\n",
d->max_voices_out);
break;
}
switch (d->max_voices_in) {
case 0:
printf ("Does not support ADC\n");
break;
case 1:
printf ("One ADC voice\n");
break;
case INT_MAX:
printf ("Theoretically supports many ADC voices\n");
break;
default:
printf ("Theoretically supports upto %d ADC voices\n",
d->max_voices_in);
break;
}
if (d->options) { if (d->options) {
printf ("Options:\n"); printf ("Options:\n");
@ -1434,7 +1275,7 @@ void AUD_help (void)
"Example:\n" "Example:\n"
#ifdef _WIN32 #ifdef _WIN32
" set QEMU_AUDIO_DRV=wav\n" " set QEMU_AUDIO_DRV=wav\n"
" set QEMU_WAV_PATH=c:/tune.wav\n" " set QEMU_WAV_PATH=c:\\tune.wav\n"
#else #else
" export QEMU_AUDIO_DRV=wav\n" " export QEMU_AUDIO_DRV=wav\n"
" export QEMU_WAV_PATH=$HOME/tune.wav\n" " export QEMU_WAV_PATH=$HOME/tune.wav\n"
@ -1444,16 +1285,6 @@ void AUD_help (void)
); );
} }
void audio_timer (void *opaque)
{
AudioState *s = opaque;
audio_run_out (s);
audio_run_in (s);
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
}
static int audio_driver_init (AudioState *s, struct audio_driver *drv) static int audio_driver_init (AudioState *s, struct audio_driver *drv)
{ {
if (drv->options) { if (drv->options) {
@ -1462,62 +1293,8 @@ static int audio_driver_init (AudioState *s, struct audio_driver *drv)
s->drv_opaque = drv->init (); s->drv_opaque = drv->init ();
if (s->drv_opaque) { if (s->drv_opaque) {
if (s->nb_hw_voices_out > drv->max_voices_out) { audio_init_nb_voices_out (s, drv);
if (!drv->max_voices_out) { audio_init_nb_voices_in (s, drv);
dolog ("`%s' does not support DAC\n", drv->name);
}
else {
dolog (
"`%s' does not support %d multiple DAC voicess\n"
"Resetting to %d\n",
drv->name,
s->nb_hw_voices_out,
drv->max_voices_out
);
}
s->nb_hw_voices_out = drv->max_voices_out;
}
if (!drv->voice_size_in && drv->max_voices_in) {
ldebug ("warning: No ADC voice size defined for `%s'\n",
drv->name);
drv->max_voices_in = 0;
}
if (!drv->voice_size_out && drv->max_voices_out) {
ldebug ("warning: No DAC voice size defined for `%s'\n",
drv->name);
}
if (drv->voice_size_in && !drv->max_voices_in) {
ldebug ("warning: `%s' ADC voice size %d, zero voices \n",
drv->name, drv->voice_size_out);
}
if (drv->voice_size_out && !drv->max_voices_out) {
ldebug ("warning: `%s' DAC voice size %d, zero voices \n",
drv->name, drv->voice_size_in);
}
if (s->nb_hw_voices_in > drv->max_voices_in) {
if (!drv->max_voices_in) {
ldebug ("`%s' does not support ADC\n", drv->name);
}
else {
dolog (
"`%s' does not support %d multiple ADC voices\n"
"Resetting to %d\n",
drv->name,
s->nb_hw_voices_in,
drv->max_voices_in
);
}
s->nb_hw_voices_in = drv->max_voices_in;
}
LIST_INIT (&s->hw_head_out);
LIST_INIT (&s->hw_head_in);
s->drv = drv; s->drv = drv;
return 0; return 0;
} }
@ -1549,25 +1326,13 @@ static void audio_atexit (void)
HWVoiceOut *hwo = NULL; HWVoiceOut *hwo = NULL;
HWVoiceIn *hwi = NULL; HWVoiceIn *hwi = NULL;
while ((hwo = audio_pcm_hw_find_any_out (s, hwo))) { while ((hwo = audio_pcm_hw_find_any_enabled_out (s, hwo))) {
if (!hwo->pcm_ops) { hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE);
continue;
}
if (hwo->enabled) {
hwo->pcm_ops->ctl_out (hwo, VOICE_DISABLE);
}
hwo->pcm_ops->fini_out (hwo); hwo->pcm_ops->fini_out (hwo);
} }
while ((hwi = audio_pcm_hw_find_any_in (s, hwi))) { while ((hwi = audio_pcm_hw_find_any_enabled_in (s, hwi))) {
if (!hwi->pcm_ops) { hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE);
continue;
}
if (hwi->enabled) {
hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE);
}
hwi->pcm_ops->fini_in (hwi); hwi->pcm_ops->fini_in (hwi);
} }
@ -1616,21 +1381,31 @@ AudioState *AUD_init (void)
const char *drvname; const char *drvname;
AudioState *s = &glob_audio_state; AudioState *s = &glob_audio_state;
LIST_INIT (&s->hw_head_out);
LIST_INIT (&s->hw_head_in);
atexit (audio_atexit);
s->ts = qemu_new_timer (vm_clock, audio_timer, s);
if (!s->ts) {
dolog ("Could not create audio timer\n");
return NULL;
}
audio_process_options ("AUDIO", audio_options); audio_process_options ("AUDIO", audio_options);
s->nb_hw_voices_out = conf.fixed_out.nb_voices; s->nb_hw_voices_out = conf.fixed_out.nb_voices;
s->nb_hw_voices_in = conf.fixed_in.nb_voices; s->nb_hw_voices_in = conf.fixed_in.nb_voices;
if (s->nb_hw_voices_out <= 0) { if (s->nb_hw_voices_out <= 0) {
dolog ("Bogus number of DAC voices %d\n", dolog ("Bogus number of playback voices %d, setting to 1\n",
s->nb_hw_voices_out); s->nb_hw_voices_out);
s->nb_hw_voices_out = 1; s->nb_hw_voices_out = 1;
} }
if (s->nb_hw_voices_in <= 0) { if (s->nb_hw_voices_in <= 0) {
dolog ("Bogus number of ADC voices %d\n", dolog ("Bogus number of capture voices %d, setting to 0\n",
s->nb_hw_voices_in); s->nb_hw_voices_in);
s->nb_hw_voices_in = 1; s->nb_hw_voices_in = 0;
} }
{ {
@ -1638,12 +1413,6 @@ AudioState *AUD_init (void)
drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def); drvname = audio_get_conf_str ("QEMU_AUDIO_DRV", NULL, &def);
} }
s->ts = qemu_new_timer (vm_clock, audio_timer, s);
if (!s->ts) {
dolog ("Could not create audio timer\n");
return NULL;
}
if (drvname) { if (drvname) {
int found = 0; int found = 0;
@ -1680,6 +1449,8 @@ AudioState *AUD_init (void)
} }
if (done) { if (done) {
VMChangeStateEntry *e;
if (conf.period.hz <= 0) { if (conf.period.hz <= 0) {
if (conf.period.hz < 0) { if (conf.period.hz < 0) {
dolog ("warning: Timer period is negative - %d " dolog ("warning: Timer period is negative - %d "
@ -1692,7 +1463,11 @@ AudioState *AUD_init (void)
conf.period.ticks = ticks_per_sec / conf.period.hz; conf.period.ticks = ticks_per_sec / conf.period.hz;
} }
qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s); e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
if (!e) {
dolog ("warning: Could not register change state handler\n"
"(Audio can continue looping even after stopping the VM)\n");
}
} }
else { else {
qemu_del_timer (s->ts); qemu_del_timer (s->ts);
@ -1701,7 +1476,6 @@ AudioState *AUD_init (void)
LIST_INIT (&s->card_head); LIST_INIT (&s->card_head);
register_savevm ("audio", 0, 1, audio_save, audio_load, s); register_savevm ("audio", 0, 1, audio_save, audio_load, s);
atexit (audio_atexit);
qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks); qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
return s; return s;
} }

View File

@ -73,7 +73,8 @@ SWVoiceOut *AUD_open_out (
const char *name, const char *name,
void *callback_opaque, void *callback_opaque,
audio_callback_fn_t callback_fn, audio_callback_fn_t callback_fn,
audsettings_t *settings audsettings_t *settings,
int sw_endian
); );
void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw); void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw);
@ -91,7 +92,8 @@ SWVoiceIn *AUD_open_in (
const char *name, const char *name,
void *callback_opaque, void *callback_opaque,
audio_callback_fn_t callback_fn, audio_callback_fn_t callback_fn,
audsettings_t *settings audsettings_t *settings,
int sw_endian
); );
void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw); void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw);

View File

@ -123,7 +123,7 @@ struct SWVoiceIn {
int64_t ratio; int64_t ratio;
void *rate; void *rate;
int total_hw_samples_acquired; int total_hw_samples_acquired;
st_sample_t *conv_buf; st_sample_t *buf;
f_sample *clip; f_sample *clip;
HWVoiceIn *hw; HWVoiceIn *hw;
char *name; char *name;

View File

@ -23,52 +23,159 @@
*/ */
#ifdef DAC #ifdef DAC
#define NAME "playback"
#define HWBUF hw->mix_buf
#define TYPE out #define TYPE out
#define HW glue (HWVoice, Out) #define HW HWVoiceOut
#define SW glue (SWVoice, Out) #define SW SWVoiceOut
#else #else
#define NAME "capture"
#define TYPE in #define TYPE in
#define HW glue (HWVoice, In) #define HW HWVoiceIn
#define SW glue (SWVoice, In) #define SW SWVoiceIn
#define HWBUF hw->conv_buf
#endif #endif
static int glue (audio_pcm_hw_init_, TYPE) ( static void glue (audio_init_nb_voices_, TYPE) (
HW *hw, AudioState *s,
audsettings_t *as struct audio_driver *drv
) )
{ {
glue (audio_pcm_hw_free_resources_, TYPE) (hw); int max_voices = glue (drv->max_voices_, TYPE);
int voice_size = glue (drv->voice_size_, TYPE);
if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) { if (glue (s->nb_hw_voices_, TYPE) > max_voices) {
return -1; if (!max_voices) {
}
if (audio_bug (AUDIO_FUNC, hw->samples <= 0)) {
dolog ("hw->samples=%d\n", hw->samples);
return -1;
}
LIST_INIT (&hw->sw_head);
#ifdef DAC #ifdef DAC
hw->clip = dolog ("Driver `%s' does not support " NAME "\n", drv->name);
mixeng_clip
#else
hw->conv =
mixeng_conv
#endif #endif
[hw->info.nchannels == 2] }
[hw->info.sign] else {
[hw->info.swap_endian] dolog ("Driver `%s' does not support %d " NAME " voices, max %d\n",
[hw->info.bits == 16]; drv->name,
glue (s->nb_hw_voices_, TYPE),
max_voices);
}
glue (s->nb_hw_voices_, TYPE) = max_voices;
}
if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) { if (audio_bug (AUDIO_FUNC, !voice_size && max_voices)) {
glue (hw->pcm_ops->fini_, TYPE) (hw); dolog ("drv=`%s' voice_size=0 max_voices=%d\n",
drv->name, max_voices);
glue (s->nb_hw_voices_, TYPE) = 0;
}
if (audio_bug (AUDIO_FUNC, voice_size && !max_voices)) {
dolog ("drv=`%s' voice_size=%d max_voices=0\n",
drv->name, voice_size);
}
}
static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
{
if (HWBUF) {
qemu_free (HWBUF);
}
HWBUF = NULL;
}
static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw)
{
HWBUF = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (st_sample_t));
if (!HWBUF) {
dolog ("Could not allocate " NAME " buffer (%d samples)\n",
hw->samples);
return -1; return -1;
} }
return 0; return 0;
} }
static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
{
if (sw->buf) {
qemu_free (sw->buf);
}
if (sw->rate) {
st_rate_stop (sw->rate);
}
sw->buf = NULL;
sw->rate = NULL;
}
static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw)
{
int samples;
#ifdef DAC
samples = sw->hw->samples;
#else
samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
#endif
sw->buf = audio_calloc (AUDIO_FUNC, samples, sizeof (st_sample_t));
if (!sw->buf) {
dolog ("Could not allocate buffer for `%s' (%d samples)\n",
SW_NAME (sw), samples);
return -1;
}
#ifdef DAC
sw->rate = st_rate_start (sw->info.freq, sw->hw->info.freq);
#else
sw->rate = st_rate_start (sw->hw->info.freq, sw->info.freq);
#endif
if (!sw->rate) {
qemu_free (sw->buf);
sw->buf = NULL;
return -1;
}
return 0;
}
static int glue (audio_pcm_sw_init_, TYPE) (
SW *sw,
HW *hw,
const char *name,
audsettings_t *as,
int endian
)
{
int err;
audio_pcm_init_info (&sw->info, as, audio_need_to_swap_endian (endian));
sw->hw = hw;
sw->active = 0;
#ifdef DAC
sw->ratio = ((int64_t) sw->hw->info.freq << 32) / sw->info.freq;
sw->total_hw_samples_mixed = 0;
sw->empty = 1;
#else
sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq;
#endif
#ifdef DAC
sw->conv = mixeng_conv
#else
sw->clip = mixeng_clip
#endif
[sw->info.nchannels == 2]
[sw->info.sign]
[sw->info.swap_endian]
[sw->info.bits == 16];
sw->name = qemu_strdup (name);
err = glue (audio_pcm_sw_alloc_resources_, TYPE) (sw);
if (err) {
qemu_free (sw->name);
sw->name = NULL;
}
return err;
}
static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw) static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw)
{ {
glue (audio_pcm_sw_free_resources_, TYPE) (sw); glue (audio_pcm_sw_free_resources_, TYPE) (sw);
@ -117,31 +224,6 @@ static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (AudioState *s, HW *hw)
return NULL; return NULL;
} }
static HW *glue (audio_pcm_hw_find_any_passive_, TYPE) (AudioState *s)
{
if (glue (s->nb_hw_voices_, TYPE)) {
struct audio_driver *drv = s->drv;
if (audio_bug (AUDIO_FUNC, !drv)) {
dolog ("No host audio driver\n");
return NULL;
}
HW *hw = audio_calloc (AUDIO_FUNC, 1, glue (drv->voice_size_, TYPE));
if (!hw) {
dolog ("Can not allocate voice `%s' size %d\n",
drv->name, glue (drv->voice_size_, TYPE));
return NULL;
}
LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
glue (s->nb_hw_voices_, TYPE) -= 1;
return hw;
}
return NULL;
}
static HW *glue (audio_pcm_hw_find_specific_, TYPE) ( static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
AudioState *s, AudioState *s,
HW *hw, HW *hw,
@ -159,23 +241,63 @@ static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as) static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as)
{ {
HW *hw; HW *hw;
struct audio_driver *drv = s->drv;
hw = glue (audio_pcm_hw_find_any_passive_, TYPE) (s); if (!glue (s->nb_hw_voices_, TYPE)) {
if (hw) { return NULL;
hw->pcm_ops = s->drv->pcm_ops;
if (!hw->pcm_ops) {
return NULL;
}
if (glue (audio_pcm_hw_init_, TYPE) (hw, as)) {
glue (audio_pcm_hw_gc_, TYPE) (s, &hw);
return NULL;
}
else {
return hw;
}
} }
if (audio_bug (AUDIO_FUNC, !drv)) {
dolog ("No host audio driver\n");
return NULL;
}
if (audio_bug (AUDIO_FUNC, !drv->pcm_ops)) {
dolog ("Host audio driver without pcm_ops\n");
return NULL;
}
hw = audio_calloc (AUDIO_FUNC, 1, glue (drv->voice_size_, TYPE));
if (!hw) {
dolog ("Can not allocate voice `%s' size %d\n",
drv->name, glue (drv->voice_size_, TYPE));
return NULL;
}
hw->pcm_ops = drv->pcm_ops;
LIST_INIT (&hw->sw_head);
if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
goto err0;
}
if (audio_bug (AUDIO_FUNC, hw->samples <= 0)) {
dolog ("hw->samples=%d\n", hw->samples);
goto err1;
}
#ifdef DAC
hw->clip = mixeng_clip
#else
hw->conv = mixeng_conv
#endif
[hw->info.nchannels == 2]
[hw->info.sign]
[hw->info.swap_endian]
[hw->info.bits == 16];
if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) {
goto err1;
}
LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
glue (s->nb_hw_voices_, TYPE) -= 1;
return hw;
err1:
glue (hw->pcm_ops->fini_, TYPE) (hw);
err0:
qemu_free (hw);
return NULL; return NULL;
} }
@ -206,7 +328,8 @@ static HW *glue (audio_pcm_hw_add_, TYPE) (AudioState *s, audsettings_t *as)
static SW *glue (audio_pcm_create_voice_pair_, TYPE) ( static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
AudioState *s, AudioState *s,
const char *sw_name, const char *sw_name,
audsettings_t *as audsettings_t *as,
int sw_endian
) )
{ {
SW *sw; SW *sw;
@ -234,7 +357,7 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw); glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw);
if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as)) { if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as, sw_endian)) {
goto err3; goto err3;
} }
@ -256,6 +379,7 @@ static void glue (audio_close_, TYPE) (AudioState *s, SW *sw)
glue (audio_pcm_hw_gc_, TYPE) (s, &sw->hw); glue (audio_pcm_hw_gc_, TYPE) (s, &sw->hw);
qemu_free (sw); qemu_free (sw);
} }
void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw) void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
{ {
if (sw) { if (sw) {
@ -275,7 +399,8 @@ SW *glue (AUD_open_, TYPE) (
const char *name, const char *name,
void *callback_opaque , void *callback_opaque ,
audio_callback_fn_t callback_fn, audio_callback_fn_t callback_fn,
audsettings_t *as audsettings_t *as,
int sw_endian
) )
{ {
AudioState *s; AudioState *s;
@ -347,15 +472,16 @@ SW *glue (AUD_open_, TYPE) (
goto fail; goto fail;
} }
if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as)) { glue (audio_pcm_sw_fini_, TYPE) (sw);
if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as, sw_endian)) {
goto fail; goto fail;
} }
} }
else { else {
sw = glue (audio_pcm_create_voice_pair_, TYPE) (s, name, as); sw = glue (audio_pcm_create_voice_pair_, TYPE) (s, name, as, sw_endian);
if (!sw) { if (!sw) {
dolog ("Failed to create voice `%s'\n", name); dolog ("Failed to create voice `%s'\n", name);
goto fail; return NULL;
} }
} }
@ -435,3 +561,5 @@ uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
#undef TYPE #undef TYPE
#undef HW #undef HW
#undef SW #undef SW
#undef HWBUF
#undef NAME

View File

@ -75,11 +75,11 @@ static void GCC_FMT_ATTR (2, 3) oss_logerr (int err, const char *fmt, ...)
{ {
va_list ap; va_list ap;
AUD_vlog (AUDIO_CAP, fmt, ap);
va_start (ap, fmt); va_start (ap, fmt);
AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err)); AUD_vlog (AUDIO_CAP, fmt, ap);
va_end (ap); va_end (ap);
AUD_log (AUDIO_CAP, "Reason: %s\n", strerror (err));
} }
static void GCC_FMT_ATTR (3, 4) oss_logerr2 ( static void GCC_FMT_ATTR (3, 4) oss_logerr2 (
@ -422,6 +422,8 @@ static int oss_init_out (HWVoiceOut *hw, audsettings_t *as)
audfmt_e effective_fmt; audfmt_e effective_fmt;
audsettings_t obt_as; audsettings_t obt_as;
oss->fd = -1;
req.fmt = aud_to_ossfmt (as->fmt); req.fmt = aud_to_ossfmt (as->fmt);
req.freq = as->freq; req.freq = as->freq;
req.nchannels = as->nchannels; req.nchannels = as->nchannels;
@ -565,6 +567,8 @@ static int oss_init_in (HWVoiceIn *hw, audsettings_t *as)
audfmt_e effective_fmt; audfmt_e effective_fmt;
audsettings_t obt_as; audsettings_t obt_as;
oss->fd = -1;
req.fmt = aud_to_ossfmt (as->fmt); req.fmt = aud_to_ossfmt (as->fmt);
req.freq = as->freq; req.freq = as->freq;
req.nchannels = as->nchannels; req.nchannels = as->nchannels;