* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
+#include "qemu/osdep.h"
#include "hw/hw.h"
#include "audio.h"
#include "monitor/monitor.h"
#include "qemu/timer.h"
#include "sysemu/sysemu.h"
+#include "qemu/cutils.h"
+#include "sysemu/replay.h"
+#include "trace.h"
#define AUDIO_CAP "audio"
#include "audio_int.h"
-/* #define DEBUG_PLIVE */
/* #define DEBUG_LIVE */
/* #define DEBUG_OUT */
/* #define DEBUG_CAPTURE */
The 1st one is the one used by default, that is the reason
that we generate the list.
*/
-static struct audio_driver *drvtab[] = {
-#ifdef CONFIG_SPICE
- &spice_audio_driver,
-#endif
+static const char *audio_prio_list[] = {
+ "spice",
CONFIG_AUDIO_DRIVERS
- &no_audio_driver,
- &wav_audio_driver
+ "none",
+ "wav",
};
+static QLIST_HEAD(, audio_driver) audio_drivers;
+
+void audio_driver_register(audio_driver *drv)
+{
+ QLIST_INSERT_HEAD(&audio_drivers, drv, next);
+}
+
+audio_driver *audio_driver_lookup(const char *name)
+{
+ struct audio_driver *d;
+
+ QLIST_FOREACH(d, &audio_drivers, next) {
+ if (strcmp(name, d->name) == 0) {
+ return d;
+ }
+ }
+
+ audio_module_load_one(name);
+ QLIST_FOREACH(d, &audio_drivers, next) {
+ if (strcmp(name, d->name) == 0) {
+ return d;
+ }
+ }
+
+ return NULL;
+}
+
+static void audio_module_load_all(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(audio_prio_list); i++) {
+ audio_driver_lookup(audio_prio_list[i]);
+ }
+}
+
struct fixed_settings {
int enabled;
int nb_voices;
int hertz;
int64_t ticks;
} period;
- int plive;
- int log_to_monitor;
int try_poll_in;
int try_poll_out;
} conf = {
}
},
- .period = { .hertz = 250 },
- .plive = 0,
- .log_to_monitor = 0,
+ .period = { .hertz = 100 },
.try_poll_in = 1,
.try_poll_out = 1,
};
char *strval;
strval = getenv (key);
- if (strval) {
+ if (strval && !qemu_strtoi(strval, NULL, 10, &val)) {
*defaultp = 0;
- val = atoi (strval);
return val;
}
else {
void AUD_vlog (const char *cap, const char *fmt, va_list ap)
{
- if (conf.log_to_monitor) {
- if (cap) {
- monitor_printf(default_mon, "%s: ", cap);
- }
-
- monitor_vprintf(default_mon, fmt, ap);
+ if (cap) {
+ fprintf(stderr, "%s: ", cap);
}
- else {
- if (cap) {
- fprintf (stderr, "%s: ", cap);
- }
- vfprintf (stderr, fmt, ap);
- }
+ vfprintf(stderr, fmt, ap);
}
void AUD_log (const char *cap, const char *fmt, ...)
const char qemu_prefix[] = "QEMU_";
size_t preflen, optlen;
- if (audio_bug (AUDIO_FUNC, !prefix)) {
+ if (audio_bug(__func__, !prefix)) {
dolog ("prefix = NULL\n");
return;
}
- if (audio_bug (AUDIO_FUNC, !opt)) {
+ if (audio_bug(__func__, !opt)) {
dolog ("opt = NULL\n");
return;
}
SWVoiceOut *sw;
HWVoiceOut *hw_cap = &cap->hw;
- sc = audio_calloc (AUDIO_FUNC, 1, sizeof (*sc));
+ sc = audio_calloc(__func__, 1, sizeof(*sc));
if (!sc) {
dolog ("Could not allocate soft capture voice (%zu bytes)\n",
sizeof (*sc));
int audio_pcm_hw_get_live_in (HWVoiceIn *hw)
{
int live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw);
- if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+ if (audio_bug(__func__, live < 0 || live > hw->samples)) {
dolog ("live=%d hw->samples=%d\n", live, hw->samples);
return 0;
}
int live = hw->total_samples_captured - sw->total_hw_samples_acquired;
int rpos;
- if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+ if (audio_bug(__func__, live < 0 || live > hw->samples)) {
dolog ("live=%d hw->samples=%d\n", live, hw->samples);
return 0;
}
rpos = audio_pcm_sw_get_rpos_in (sw) % hw->samples;
live = hw->total_samples_captured - sw->total_hw_samples_acquired;
- if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+ if (audio_bug(__func__, live < 0 || live > hw->samples)) {
dolog ("live_in=%d hw->samples=%d\n", live, hw->samples);
return 0;
}
}
osamp = swlim;
- if (audio_bug (AUDIO_FUNC, osamp < 0)) {
+ if (audio_bug(__func__, osamp < 0)) {
dolog ("osamp=%d\n", osamp);
return 0;
}
if (nb_live1) {
int live = smin;
- if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+ if (audio_bug(__func__, live < 0 || live > hw->samples)) {
dolog ("live=%d hw->samples=%d\n", live, hw->samples);
return 0;
}
hwsamples = sw->hw->samples;
live = sw->total_hw_samples_mixed;
- if (audio_bug (AUDIO_FUNC, live < 0 || live > hwsamples)){
+ if (audio_bug(__func__, live < 0 || live > hwsamples)) {
dolog ("live=%d hw->samples=%d\n", live, hwsamples);
return 0;
}
/*
* Timer
*/
+
+static bool audio_timer_running;
+static uint64_t audio_timer_last;
+
static int audio_is_timer_needed (void)
{
HWVoiceIn *hwi = NULL;
static void audio_reset_timer (AudioState *s)
{
if (audio_is_timer_needed ()) {
- timer_mod (s->ts,
+ timer_mod_anticipate_ns(s->ts,
qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + conf.period.ticks);
- }
- else {
- timer_del (s->ts);
+ if (!audio_timer_running) {
+ audio_timer_running = true;
+ audio_timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ trace_audio_timer_start(conf.period.ticks / SCALE_MS);
+ }
+ } else {
+ timer_del(s->ts);
+ if (audio_timer_running) {
+ audio_timer_running = false;
+ trace_audio_timer_stop();
+ }
}
}
static void audio_timer (void *opaque)
{
+ int64_t now, diff;
+
+ now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
+ diff = now - audio_timer_last;
+ if (diff > conf.period.ticks * 3 / 2) {
+ trace_audio_timer_delayed(diff / SCALE_MS);
+ }
+ audio_timer_last = now;
+
audio_run ("timer");
audio_reset_timer (opaque);
}
*/
int AUD_write (SWVoiceOut *sw, void *buf, int size)
{
- int bytes;
-
if (!sw) {
/* XXX: Consider options */
return size;
return 0;
}
- bytes = sw->hw->pcm_ops->write (sw, buf, size);
- return bytes;
+ return sw->hw->pcm_ops->write(sw, buf, size);
}
int AUD_read (SWVoiceIn *sw, void *buf, int size)
{
- int bytes;
-
if (!sw) {
/* XXX: Consider options */
return size;
return 0;
}
- bytes = sw->hw->pcm_ops->read (sw, buf, size);
- return bytes;
+ return sw->hw->pcm_ops->read(sw, buf, size);
}
int AUD_get_buffer_size_out (SWVoiceOut *sw)
}
live = sw->hw->total_samples_captured - sw->total_hw_samples_acquired;
- if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) {
+ if (audio_bug(__func__, live < 0 || live > sw->hw->samples)) {
dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples);
return 0;
}
live = sw->total_hw_samples_mixed;
- if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) {
+ if (audio_bug(__func__, live < 0 || live > sw->hw->samples)) {
dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples);
return 0;
}
live = 0;
}
- if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
+ if (audio_bug(__func__, live < 0 || live > hw->samples)) {
dolog ("live=%d hw->samples=%d\n", live, hw->samples);
continue;
}
prev_rpos = hw->rpos;
played = hw->pcm_ops->run_out (hw, live);
- if (audio_bug (AUDIO_FUNC, hw->rpos >= hw->samples)) {
+ replay_audio_out(&played);
+ if (audio_bug(__func__, hw->rpos >= hw->samples)) {
dolog ("hw->rpos=%d hw->samples=%d played=%d\n",
hw->rpos, hw->samples, played);
hw->rpos = 0;
continue;
}
- if (audio_bug (AUDIO_FUNC, played > sw->total_hw_samples_mixed)) {
+ if (audio_bug(__func__, played > sw->total_hw_samples_mixed)) {
dolog ("played=%d sw->total_hw_samples_mixed=%d\n",
played, sw->total_hw_samples_mixed);
played = sw->total_hw_samples_mixed;
while (sw) {
sw1 = sw->entries.le_next;
if (!sw->active && !sw->callback.fn) {
-#ifdef DEBUG_PLIVE
- dolog ("Finishing with old voice\n");
-#endif
audio_close_out (sw);
}
sw = sw1;
while ((hw = audio_pcm_hw_find_any_enabled_in (hw))) {
SWVoiceIn *sw;
- int captured, min;
+ int captured = 0, min;
- captured = hw->pcm_ops->run_in (hw);
+ if (replay_mode != REPLAY_MODE_PLAY) {
+ captured = hw->pcm_ops->run_in(hw);
+ }
+ replay_audio_in(&captured, hw->conv_buf, &hw->wpos, hw->samples);
min = audio_pcm_hw_find_min_in (hw);
hw->total_samples_captured += captured - min;
continue;
}
- if (audio_bug (AUDIO_FUNC, captured > sw->total_hw_samples_mixed)) {
+ if (audio_bug(__func__, captured > sw->total_hw_samples_mixed)) {
dolog ("captured=%d sw->total_hw_samples_mixed=%d\n",
captured, sw->total_hw_samples_mixed);
captured = sw->total_hw_samples_mixed;
.valp = &conf.period.hertz,
.descr = "Timer period in HZ (0 - use lowest possible)"
},
- {
- .name = "PLIVE",
- .tag = AUD_OPT_BOOL,
- .valp = &conf.plive,
- .descr = "(undocumented)"
- },
- {
- .name = "LOG_TO_MONITOR",
- .tag = AUD_OPT_BOOL,
- .valp = &conf.log_to_monitor,
- .descr = "Print logging messages to monitor instead of stderr"
- },
{ /* End of list */ }
};
void AUD_help (void)
{
- size_t i;
+ struct audio_driver *d;
+
+ /* make sure we print the help text for modular drivers too */
+ audio_module_load_all();
audio_process_options ("AUDIO", audio_options);
- for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
- struct audio_driver *d = drvtab[i];
+ QLIST_FOREACH(d, &audio_drivers, next) {
if (d->options) {
audio_process_options (d->name, d->options);
}
printf ("Available drivers:\n");
- for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
- struct audio_driver *d = drvtab[i];
+ QLIST_FOREACH(d, &audio_drivers, next) {
printf ("Name: %s\n", d->name);
printf ("Description: %s\n", d->descr);
audio_reset_timer (s);
}
-static void audio_atexit (void)
+static bool is_cleaning_up;
+
+bool audio_is_cleaning_up(void)
+{
+ return is_cleaning_up;
+}
+
+void audio_cleanup(void)
{
AudioState *s = &glob_audio_state;
- HWVoiceOut *hwo = NULL;
- HWVoiceIn *hwi = NULL;
+ HWVoiceOut *hwo, *hwon;
+ HWVoiceIn *hwi, *hwin;
- while ((hwo = audio_pcm_hw_find_any_out (hwo))) {
+ is_cleaning_up = true;
+ QLIST_FOREACH_SAFE(hwo, &glob_audio_state.hw_head_out, entries, hwon) {
SWVoiceCap *sc;
if (hwo->enabled) {
cb->ops.destroy (cb->opaque);
}
}
+ QLIST_REMOVE(hwo, entries);
}
- while ((hwi = audio_pcm_hw_find_any_in (hwi))) {
+ QLIST_FOREACH_SAFE(hwi, &glob_audio_state.hw_head_in, entries, hwin) {
if (hwi->enabled) {
hwi->pcm_ops->ctl_in (hwi, VOICE_DISABLE);
}
hwi->pcm_ops->fini_in (hwi);
+ QLIST_REMOVE(hwi, entries);
}
if (s->drv) {
s->drv->fini (s->drv_opaque);
+ s->drv = NULL;
}
}
.name = "audio",
.version_id = 1,
.minimum_version_id = 1,
- .minimum_version_id_old = 1,
- .fields = (VMStateField []) {
+ .fields = (VMStateField[]) {
VMSTATE_END_OF_LIST()
}
};
const char *drvname;
VMChangeStateEntry *e;
AudioState *s = &glob_audio_state;
+ struct audio_driver *driver;
if (s->drv) {
return;
QLIST_INIT (&s->hw_head_out);
QLIST_INIT (&s->hw_head_in);
QLIST_INIT (&s->cap_head);
- atexit (audio_atexit);
+ atexit(audio_cleanup);
s->ts = timer_new_ns(QEMU_CLOCK_VIRTUAL, audio_timer, s);
- if (!s->ts) {
- hw_error("Could not create audio timer\n");
- }
audio_process_options ("AUDIO", audio_options);
}
if (drvname) {
- int found = 0;
-
- for (i = 0; i < ARRAY_SIZE (drvtab); i++) {
- if (!strcmp (drvname, drvtab[i]->name)) {
- done = !audio_driver_init (s, drvtab[i]);
- found = 1;
- break;
- }
- }
-
- if (!found) {
+ driver = audio_driver_lookup(drvname);
+ if (driver) {
+ done = !audio_driver_init(s, driver);
+ } else {
dolog ("Unknown audio driver `%s'\n", drvname);
dolog ("Run with -audio-help to list available drivers\n");
}
}
if (!done) {
- for (i = 0; !done && i < ARRAY_SIZE (drvtab); i++) {
- if (drvtab[i]->can_be_default) {
- done = !audio_driver_init (s, drvtab[i]);
+ for (i = 0; !done && i < ARRAY_SIZE(audio_prio_list); i++) {
+ driver = audio_driver_lookup(audio_prio_list[i]);
+ if (driver && driver->can_be_default) {
+ done = !audio_driver_init(s, driver);
}
}
}
if (!done) {
- done = !audio_driver_init (s, &no_audio_driver);
- if (!done) {
- hw_error("Could not initialize audio subsystem\n");
- }
- else {
- dolog ("warning: Using timer based audio emulation\n");
- }
+ driver = audio_driver_lookup("none");
+ done = !audio_driver_init(s, driver);
+ assert(done);
+ dolog("warning: Using timer based audio emulation\n");
}
if (conf.period.hertz <= 0) {
}
conf.period.ticks = 1;
} else {
- conf.period.ticks =
- muldiv64 (1, get_ticks_per_sec (), conf.period.hertz);
+ conf.period.ticks = NANOSECONDS_PER_SECOND / conf.period.hertz;
}
e = qemu_add_vm_change_state_handler (audio_vm_change_state_handler, s);
goto err0;
}
- cb = audio_calloc (AUDIO_FUNC, 1, sizeof (*cb));
+ cb = audio_calloc(__func__, 1, sizeof(*cb));
if (!cb) {
dolog ("Could not allocate capture callback information, size %zu\n",
sizeof (*cb));
HWVoiceOut *hw;
CaptureVoiceOut *cap;
- cap = audio_calloc (AUDIO_FUNC, 1, sizeof (*cap));
+ cap = audio_calloc(__func__, 1, sizeof(*cap));
if (!cap) {
dolog ("Could not allocate capture voice, size %zu\n",
sizeof (*cap));
/* XXX find a more elegant way */
hw->samples = 4096 * 4;
- hw->mix_buf = audio_calloc (AUDIO_FUNC, hw->samples,
- sizeof (struct st_sample));
+ hw->mix_buf = audio_calloc(__func__, hw->samples,
+ sizeof(struct st_sample));
if (!hw->mix_buf) {
dolog ("Could not allocate capture mix buffer (%d samples)\n",
hw->samples);
audio_pcm_init_info (&hw->info, as);
- cap->buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
+ cap->buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
if (!cap->buf) {
dolog ("Could not allocate capture buffer "
"(%d samples, each %d bytes)\n",
QLIST_INSERT_HEAD (&s->cap_head, cap, entries);
QLIST_INSERT_HEAD (&cap->cb_head, cb, entries);
- hw = NULL;
- while ((hw = audio_pcm_hw_find_any_out (hw))) {
+ QLIST_FOREACH(hw, &glob_audio_state.hw_head_out, entries) {
audio_attach_capture (hw);
}
return cap;
sw = sw1;
}
QLIST_REMOVE (cap, entries);
+ g_free (cap->hw.mix_buf);
+ g_free (cap->buf);
g_free (cap);
}
return;