aboutsummaryrefslogtreecommitdiffstats
path: root/audio/audio.c
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/audio.c
parent413f05aaf54fa08c0ae7e997327a4f4a473c0a8d (diff)
downloadexternal_qemu-55f4e4a5ec657a017e3bf75299ad71fd1c968dd3.zip
external_qemu-55f4e4a5ec657a017e3bf75299ad71fd1c968dd3.tar.gz
external_qemu-55f4e4a5ec657a017e3bf75299ad71fd1c968dd3.tar.bz2
Initial Contribution
Diffstat (limited to 'audio/audio.c')
-rw-r--r--audio/audio.c349
1 files changed, 272 insertions, 77 deletions
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);