Skip to content
Snippets Groups Projects
audio.c 60.3 KiB
Newer Older
Fabrice Bellard's avatar
Fabrice Bellard committed
/*
 * QEMU Audio subsystem
 *
 * Copyright (c) 2003-2005 Vassili Karpov (malc)
 *
Fabrice Bellard's avatar
Fabrice Bellard committed
 * 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.
 */
Peter Maydell's avatar
Peter Maydell committed
#include "qemu/osdep.h"
Paul Brook's avatar
Paul Brook committed
#include "audio.h"
#include "migration/vmstate.h"
#include "monitor/monitor.h"
#include "qemu/timer.h"
#include "qapi/clone-visitor.h"
#include "qapi/qobject-input-visitor.h"
#include "qapi/qapi-visit-audio.h"
#include "qapi/qapi-commands-audio.h"
#include "qapi/qmp/qdict.h"
#include "qemu/cutils.h"
#include "qemu/error-report.h"
#include "qemu/help_option.h"
#include "sysemu/sysemu.h"
#include "sysemu/replay.h"
#include "sysemu/runstate.h"
#include "ui/qemu-spice.h"
#include "trace.h"
Fabrice Bellard's avatar
Fabrice Bellard committed

#define AUDIO_CAP "audio"
#include "audio_int.h"
Fabrice Bellard's avatar
Fabrice Bellard committed

/* #define DEBUG_LIVE */
/* #define DEBUG_OUT */
/* #define DEBUG_CAPTURE */
malc's avatar
malc committed
/* #define DEBUG_POLL */
Fabrice Bellard's avatar
Fabrice Bellard committed

Fabrice Bellard's avatar
Fabrice Bellard committed
#define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown"


/* Order of CONFIG_AUDIO_DRIVERS is import.
   The 1st one is the one used by default, that is the reason
    that we generate the list.
*/
const char *audio_prio_list[] = {
    "spice",
    CONFIG_AUDIO_DRIVERS
    "none",
Fabrice Bellard's avatar
Fabrice Bellard committed

static QLIST_HEAD(, audio_driver) audio_drivers;
static AudiodevListHead audiodevs =
    QSIMPLEQ_HEAD_INITIALIZER(audiodevs);
static AudiodevListHead default_audiodevs =
    QSIMPLEQ_HEAD_INITIALIZER(default_audiodevs);


void audio_driver_register(audio_driver *drv)
{
    QLIST_INSERT_HEAD(&audio_drivers, drv, next);
}

static 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;
        }
    }
    rv = audio_module_load(name, &local_err);
    if (rv > 0) {
        QLIST_FOREACH(d, &audio_drivers, next) {
            if (strcmp(name, d->name) == 0) {
                return d;
            }
    } else if (rv < 0) {
        error_report_err(local_err);
    return NULL;
}

static QTAILQ_HEAD(AudioStateHead, AudioState) audio_states =
    QTAILQ_HEAD_INITIALIZER(audio_states);
static AudioState *default_audio_state;
const struct mixeng_volume nominal_volume = {
    .mute = 0,
#ifdef FLOAT_MIXENG
    .r = 1.0,
    .l = 1.0,
    .r = 1ULL << 32,
    .l = 1ULL << 32,
int audio_bug (const char *funcname, int cond)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    if (cond) {
        static int shown;

        AUD_log (NULL, "A bug was just triggered in %s\n", funcname);
        if (!shown) {
            shown = 1;
            AUD_log (NULL, "Save all your work and restart without audio\n");
            AUD_log (NULL, "I am sorry\n");
        }
        AUD_log (NULL, "Context:\n");
static inline int audio_bits_to_index (int bits)
{
    switch (bits) {
    case 8:
        return 0;

    case 16:
        return 1;

    case 32:
        return 2;

    default:
        audio_bug ("bits_to_index", 1);
        AUD_log (NULL, "invalid bits %d\n", bits);
void AUD_vlog (const char *cap, const char *fmt, va_list ap)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    if (cap) {
        fprintf(stderr, "%s: ", cap);
    vfprintf(stderr, fmt, ap);
void AUD_log (const char *cap, const char *fmt, ...)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    va_list ap;

    va_start (ap, fmt);
    AUD_vlog (cap, fmt, ap);
    va_end (ap);
malc's avatar
malc committed
static void audio_print_settings (struct audsettings *as)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels);

    switch (as->fmt) {
Fabrice Bellard's avatar
Fabrice Bellard committed
        AUD_log (NULL, "S8");
        break;
Fabrice Bellard's avatar
Fabrice Bellard committed
        AUD_log (NULL, "U8");
        break;
    case AUDIO_FORMAT_S16:
Fabrice Bellard's avatar
Fabrice Bellard committed
        AUD_log (NULL, "S16");
        break;
    case AUDIO_FORMAT_U16:
Fabrice Bellard's avatar
Fabrice Bellard committed
        AUD_log (NULL, "U16");
        break;
    case AUDIO_FORMAT_S32:
malc's avatar
malc committed
        AUD_log (NULL, "S32");
        break;
    case AUDIO_FORMAT_U32:
malc's avatar
malc committed
        AUD_log (NULL, "U32");
        break;
    case AUDIO_FORMAT_F32:
        AUD_log (NULL, "F32");
        break;
Fabrice Bellard's avatar
Fabrice Bellard committed
    default:
        AUD_log (NULL, "invalid(%d)", as->fmt);
        break;
    }

    AUD_log (NULL, " endianness=");
    switch (as->endianness) {
    case 0:
        AUD_log (NULL, "little");
        break;
    case 1:
        AUD_log (NULL, "big");
        break;
    default:
        AUD_log (NULL, "invalid");
        break;
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
    AUD_log (NULL, "\n");
}

malc's avatar
malc committed
static int audio_validate_settings (struct audsettings *as)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    int invalid;

    invalid = as->nchannels < 1;
    invalid |= as->endianness != 0 && as->endianness != 1;
Fabrice Bellard's avatar
Fabrice Bellard committed

    switch (as->fmt) {
    case AUDIO_FORMAT_S8:
    case AUDIO_FORMAT_U8:
    case AUDIO_FORMAT_S16:
    case AUDIO_FORMAT_U16:
    case AUDIO_FORMAT_S32:
    case AUDIO_FORMAT_U32:
Fabrice Bellard's avatar
Fabrice Bellard committed
        break;
    default:
        invalid = 1;
        break;
    }

    invalid |= as->freq <= 0;
    return invalid ? -1 : 0;
malc's avatar
malc committed
static int audio_pcm_info_eq (struct audio_pcm_info *info, struct audsettings *as)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    int bits = 8;
    bool is_signed = false, is_float = false;
Fabrice Bellard's avatar
Fabrice Bellard committed
    switch (as->fmt) {
        /* fall through */
    case AUDIO_FORMAT_S16:
        /* fall through */
    case AUDIO_FORMAT_U16:
    case AUDIO_FORMAT_F32:
        is_float = true;
        /* fall through */
    case AUDIO_FORMAT_S32:
        /* fall through */
    case AUDIO_FORMAT_U32:
Fabrice Bellard's avatar
Fabrice Bellard committed
    }
Fabrice Bellard's avatar
Fabrice Bellard committed
    return info->freq == as->freq
        && info->nchannels == as->nchannels
        && info->is_signed == is_signed
        && info->is_float == is_float
        && info->bits == bits
        && info->swap_endianness == (as->endianness != AUDIO_HOST_ENDIANNESS);
malc's avatar
malc committed
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as)
    int bits = 8, mul;
    bool is_signed = false, is_float = false;
Fabrice Bellard's avatar
Fabrice Bellard committed
    switch (as->fmt) {
Fabrice Bellard's avatar
Fabrice Bellard committed
        break;

    case AUDIO_FORMAT_S16:
        /* fall through */
    case AUDIO_FORMAT_U16:
Fabrice Bellard's avatar
Fabrice Bellard committed
        bits = 16;
    case AUDIO_FORMAT_F32:
        is_float = true;
        /* fall through */
    case AUDIO_FORMAT_S32:
        /* fall through */
    case AUDIO_FORMAT_U32:
Fabrice Bellard's avatar
Fabrice Bellard committed
        break;
Fabrice Bellard's avatar
Fabrice Bellard committed
    info->freq = as->freq;
    info->bits = bits;
    info->is_signed = is_signed;
    info->is_float = is_float;
Fabrice Bellard's avatar
Fabrice Bellard committed
    info->nchannels = as->nchannels;
    info->bytes_per_frame = as->nchannels * mul;
    info->bytes_per_second = info->freq * info->bytes_per_frame;
    info->swap_endianness = (as->endianness != AUDIO_HOST_ENDIANNESS);
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    if (info->is_signed || info->is_float) {
        memset(buf, 0x00, len * info->bytes_per_frame);
            memset(buf, 0x80, len * info->bytes_per_frame);
        case 16:
            {
                int i;
                uint16_t *p = buf;
                short s = INT16_MAX;

                if (info->swap_endianness) {
                    s = bswap16 (s);
                }

                for (i = 0; i < len * info->nchannels; i++) {
            break;

        case 32:
            {
                int i;
                uint32_t *p = buf;
                int32_t s = INT32_MAX;

                if (info->swap_endianness) {
                    s = bswap32 (s);
                }
                for (i = 0; i < len * info->nchannels; i++) {
            break;

        default:
            AUD_log (NULL, "audio_pcm_info_clear_buf: invalid bits %d\n",
                     info->bits);
            break;
static CaptureVoiceOut *audio_pcm_capture_find_specific(AudioState *s,
                                                        struct audsettings *as)
{
    CaptureVoiceOut *cap;

    for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
        if (audio_pcm_info_eq (&cap->hw.info, as)) {
static void audio_notify_capture (CaptureVoiceOut *cap, audcnotification_e cmd)
    struct capture_callback *cb;

#ifdef DEBUG_CAPTURE
    dolog ("notification %d sent\n", cmd);
#endif
    for (cb = cap->cb_head.lh_first; cb; cb = cb->entries.le_next) {
        cb->ops.notify (cb->opaque, cmd);
    }
}
static void audio_capture_maybe_changed (CaptureVoiceOut *cap, int enabled)
{
    if (cap->hw.enabled != enabled) {
        audcnotification_e cmd;
        cap->hw.enabled = enabled;
        cmd = enabled ? AUD_CNOTIFY_ENABLE : AUD_CNOTIFY_DISABLE;
        audio_notify_capture (cap, cmd);
    }
}

static void audio_recalc_and_notify_capture (CaptureVoiceOut *cap)
{
    HWVoiceOut *hw = &cap->hw;
    SWVoiceOut *sw;
    int enabled = 0;

    for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
        if (sw->active) {
            enabled = 1;
            break;
        }
    }
    audio_capture_maybe_changed (cap, enabled);
}

static void audio_detach_capture (HWVoiceOut *hw)
{
    SWVoiceCap *sc = hw->cap_head.lh_first;

    while (sc) {
        SWVoiceCap *sc1 = sc->entries.le_next;
        SWVoiceOut *sw = &sc->sw;
        CaptureVoiceOut *cap = sc->cap;
        int was_active = sw->active;

        if (sw->rate) {
            st_rate_stop (sw->rate);
            sw->rate = NULL;
        }

        QLIST_REMOVE (sw, entries);
        QLIST_REMOVE (sc, entries);
        if (was_active) {
            /* We have removed soft voice from the capture:
               this might have changed the overall status of the capture
               since this might have been the only active voice */
            audio_recalc_and_notify_capture (cap);
        }
        sc = sc1;
static int audio_attach_capture (HWVoiceOut *hw)
    AudioState *s = hw->s;
    CaptureVoiceOut *cap;

    audio_detach_capture (hw);
    for (cap = s->cap_head.lh_first; cap; cap = cap->entries.le_next) {
        SWVoiceCap *sc;
        HWVoiceOut *hw_cap = &cap->hw;
        sc = g_malloc0(sizeof(*sc));
        sc->cap = cap;
        sw = &sc->sw;
        sw->info = hw->info;
        sw->empty = 1;
        sw->active = hw->enabled;
        sw->vol = nominal_volume;
        sw->rate = st_rate_start (sw->info.freq, hw_cap->info.freq);
        QLIST_INSERT_HEAD (&hw_cap->sw_head, sw, entries);
        QLIST_INSERT_HEAD (&hw->cap_head, sc, entries);
#ifdef DEBUG_CAPTURE
        sw->name = g_strdup_printf ("for %p %d,%d,%d",
                                    hw, sw->info.freq, sw->info.bits,
                                    sw->info.nchannels);
        dolog ("Added %s active = %d\n", sw->name, sw->active);
#endif
            audio_capture_maybe_changed (cap, 1);
/*
 * Hard voice (capture)
 */
static size_t audio_pcm_hw_find_min_in (HWVoiceIn *hw)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    size_t m = hw->total_samples_captured;

    for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
        if (sw->active) {
            m = MIN (m, sw->total_hw_samples_acquired);
Fabrice Bellard's avatar
Fabrice Bellard committed
    }
static size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    size_t live = hw->total_samples_captured - audio_pcm_hw_find_min_in (hw);
    if (audio_bug(__func__, live > hw->conv_buf.size)) {
        dolog("live=%zu hw->conv_buf.size=%zu\n", live, hw->conv_buf.size);
Fabrice Bellard's avatar
Fabrice Bellard committed
    }
static size_t audio_pcm_hw_conv_in(HWVoiceIn *hw, void *pcm_buf, size_t samples)
{
    size_t conv = 0;
    STSampleBuffer *conv_buf = &hw->conv_buf;

    while (samples) {
        uint8_t *src = advance(pcm_buf, conv * hw->info.bytes_per_frame);
        size_t proc = MIN(samples, conv_buf->size - conv_buf->pos);

        hw->conv(conv_buf->buffer + conv_buf->pos, src, proc);
        conv_buf->pos = (conv_buf->pos + proc) % conv_buf->size;
        samples -= proc;
        conv += proc;
    }

    return conv;
}

/*
 * Soft voice (capture)
 */
static void audio_pcm_sw_resample_in(SWVoiceIn *sw,
    size_t frames_in_max, size_t frames_out_max,
    size_t *total_in, size_t *total_out)
{
    HWVoiceIn *hw = sw->hw;
    struct st_sample *src, *dst;
    size_t live, rpos, frames_in, frames_out;

    live = hw->total_samples_captured - sw->total_hw_samples_acquired;
    rpos = audio_ring_posb(hw->conv_buf.pos, live, hw->conv_buf.size);

    /* resample conv_buf from rpos to end of buffer */
    src = hw->conv_buf.buffer + rpos;
    frames_in = MIN(frames_in_max, hw->conv_buf.size - rpos);
    dst = sw->resample_buf.buffer;
    frames_out = frames_out_max;
    st_rate_flow(sw->rate, src, dst, &frames_in, &frames_out);
    rpos += frames_in;
    *total_in = frames_in;
    *total_out = frames_out;

    /* resample conv_buf from start of buffer if there are input frames left */
    if (frames_in_max - frames_in && rpos == hw->conv_buf.size) {
        src = hw->conv_buf.buffer;
        frames_in = frames_in_max - frames_in;
        dst += frames_out;
        frames_out = frames_out_max - frames_out;
        st_rate_flow(sw->rate, src, dst, &frames_in, &frames_out);
        *total_in += frames_in;
        *total_out += frames_out;
    }
}

static size_t audio_pcm_sw_read(SWVoiceIn *sw, void *buf, size_t buf_len)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    HWVoiceIn *hw = sw->hw;
    size_t live, frames_out_max, total_in, total_out;

    live = hw->total_samples_captured - sw->total_hw_samples_acquired;
    if (audio_bug(__func__, live > hw->conv_buf.size)) {
        dolog("live_in=%zu hw->conv_buf.size=%zu\n", live, hw->conv_buf.size);
    frames_out_max = MIN(buf_len / sw->info.bytes_per_frame,
                         sw->resample_buf.size);
    audio_pcm_sw_resample_in(sw, live, frames_out_max, &total_in, &total_out);
    if (!hw->pcm_ops->volume_in) {
        mixeng_volume(sw->resample_buf.buffer, total_out, &sw->vol);
    sw->clip(buf, sw->resample_buf.buffer, total_out);
    sw->total_hw_samples_acquired += total_in;
    return total_out * sw->info.bytes_per_frame;
/*
 * Hard voice (playback)
 */
static size_t audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep)
Fabrice Bellard's avatar
Fabrice Bellard committed
    SWVoiceOut *sw;
    size_t m = SIZE_MAX;
Fabrice Bellard's avatar
Fabrice Bellard committed
    int nb_live = 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
    for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
        if (sw->active || !sw->empty) {
            m = MIN (m, sw->total_hw_samples_mixed);
Fabrice Bellard's avatar
Fabrice Bellard committed
            nb_live += 1;
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
    }
Fabrice Bellard's avatar
Fabrice Bellard committed

    *nb_livep = nb_live;
    return m;
static size_t audio_pcm_hw_get_live_out (HWVoiceOut *hw, int *nb_live)
malc's avatar
malc committed
    int nb_live1;
malc's avatar
malc committed
    smin = audio_pcm_hw_find_min_out (hw, &nb_live1);
    if (nb_live) {
        *nb_live = nb_live1;
Fabrice Bellard's avatar
Fabrice Bellard committed
    }
malc's avatar
malc committed

    if (nb_live1) {
        size_t live = smin;
        if (audio_bug(__func__, live > hw->mix_buf.size)) {
            dolog("live=%zu hw->mix_buf.size=%zu\n", live, hw->mix_buf.size);
Fabrice Bellard's avatar
Fabrice Bellard committed
        }
Fabrice Bellard's avatar
Fabrice Bellard committed
    }
malc's avatar
malc committed
    return 0;
static size_t audio_pcm_hw_get_free(HWVoiceOut *hw)
{
    return (hw->pcm_ops->buffer_get_free ? hw->pcm_ops->buffer_get_free(hw) :
            INT_MAX) / hw->info.bytes_per_frame;
}

static void audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf, size_t len)
{
    size_t clipped = 0;
    size_t pos = hw->mix_buf.pos;
        st_sample *src = hw->mix_buf.buffer + pos;
        uint8_t *dst = advance(pcm_buf, clipped * hw->info.bytes_per_frame);
        size_t samples_till_end_of_buf = hw->mix_buf.size - pos;
        size_t samples_to_clip = MIN(len, samples_till_end_of_buf);

        hw->clip(dst, src, samples_to_clip);

        pos = (pos + samples_to_clip) % hw->mix_buf.size;
        len -= samples_to_clip;
        clipped += samples_to_clip;
    }
}

/*
 * Soft voice (playback)
 */
static void audio_pcm_sw_resample_out(SWVoiceOut *sw,
    size_t frames_in_max, size_t frames_out_max,
    size_t *total_in, size_t *total_out)
{
    HWVoiceOut *hw = sw->hw;
    struct st_sample *src, *dst;
    size_t live, wpos, frames_in, frames_out;

    live = sw->total_hw_samples_mixed;
    wpos = (hw->mix_buf.pos + live) % hw->mix_buf.size;

    /* write to mix_buf from wpos to end of buffer */
    src = sw->resample_buf.buffer;
    frames_in = frames_in_max;
    dst = hw->mix_buf.buffer + wpos;
    frames_out = MIN(frames_out_max, hw->mix_buf.size - wpos);
    st_rate_flow_mix(sw->rate, src, dst, &frames_in, &frames_out);
    wpos += frames_out;
    *total_in = frames_in;
    *total_out = frames_out;

    /* write to mix_buf from start of buffer if there are input frames left */
    if (frames_in_max - frames_in > 0 && wpos == hw->mix_buf.size) {
        src += frames_in;
        frames_in = frames_in_max - frames_in;
        dst = hw->mix_buf.buffer;
        frames_out = frames_out_max - frames_out;
        st_rate_flow_mix(sw->rate, src, dst, &frames_in, &frames_out);
        *total_in += frames_in;
        *total_out += frames_out;
    }
}

static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t buf_len)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    HWVoiceOut *hw = sw->hw;
    size_t live, dead, hw_free, sw_max, fe_max;
    size_t frames_in_max, frames_out_max, total_in, total_out;
    live = sw->total_hw_samples_mixed;
    if (audio_bug(__func__, live > hw->mix_buf.size)) {
        dolog("live=%zu hw->mix_buf.size=%zu\n", live, hw->mix_buf.size);
    if (live == hw->mix_buf.size) {
#ifdef DEBUG_OUT
        dolog ("%s is full %zu\n", sw->name, live);
    dead = hw->mix_buf.size - live;
    hw_free = audio_pcm_hw_get_free(hw);
    hw_free = hw_free > live ? hw_free - live : 0;
    frames_out_max = MIN(dead, hw_free);
    sw_max = st_rate_frames_in(sw->rate, frames_out_max);
    fe_max = MIN(buf_len / sw->info.bytes_per_frame + sw->resample_buf.pos,
                 sw->resample_buf.size);
    frames_in_max = MIN(sw_max, fe_max);
    if (!frames_in_max) {
        return 0;
    if (frames_in_max > sw->resample_buf.pos) {
        sw->conv(sw->resample_buf.buffer + sw->resample_buf.pos,
                 buf, frames_in_max - sw->resample_buf.pos);
        if (!sw->hw->pcm_ops->volume_out) {
            mixeng_volume(sw->resample_buf.buffer + sw->resample_buf.pos,
                          frames_in_max - sw->resample_buf.pos, &sw->vol);
        }
    }

    audio_pcm_sw_resample_out(sw, frames_in_max, frames_out_max,
                              &total_in, &total_out);
    sw->total_hw_samples_mixed += total_out;
    sw->empty = sw->total_hw_samples_mixed == 0;

    /*
     * Upsampling may leave one audio frame in the resample buffer. Decrement
     * total_in by one if there was a leftover frame from the previous resample
     * pass in the resample buffer. Increment total_in by one if the current
     * resample pass left one frame in the resample buffer.
     */
    if (frames_in_max - total_in == 1) {
        /* copy one leftover audio frame to the beginning of the buffer */
        *sw->resample_buf.buffer = *(sw->resample_buf.buffer + total_in);
        total_in += 1 - sw->resample_buf.pos;
        sw->resample_buf.pos = 1;
    } else if (total_in >= sw->resample_buf.pos) {
        total_in -= sw->resample_buf.pos;
        sw->resample_buf.pos = 0;
    }

#ifdef DEBUG_OUT
    dolog (
        "%s: write size %zu written %zu total mixed %zu\n",
        SW_NAME(sw),
        buf_len / sw->info.bytes_per_frame,
        total_in,
Fabrice Bellard's avatar
Fabrice Bellard committed
        sw->total_hw_samples_mixed
    return total_in * sw->info.bytes_per_frame;
#ifdef DEBUG_AUDIO
static void audio_pcm_print_info (const char *cap, struct audio_pcm_info *info)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    dolog("%s: bits %d, sign %d, float %d, freq %d, nchan %d\n",
          cap, info->bits, info->is_signed, info->is_float, info->freq,
          info->nchannels);
Fabrice Bellard's avatar
Fabrice Bellard committed
}
#define DAC
#include "audio_template.h"
#undef DAC
#include "audio_template.h"

malc's avatar
malc committed
/*
 * Timer
 */
static int audio_is_timer_needed(AudioState *s)
malc's avatar
malc committed
{
    HWVoiceIn *hwi = NULL;
    HWVoiceOut *hwo = NULL;

    while ((hwo = audio_pcm_hw_find_any_enabled_out(s, hwo))) {
        if (!hwo->poll_mode) {
            return 1;
        }
malc's avatar
malc committed
    }
    while ((hwi = audio_pcm_hw_find_any_enabled_in(s, hwi))) {
        if (!hwi->poll_mode) {
            return 1;
        }
static void audio_reset_timer (AudioState *s)
malc's avatar
malc committed
{
    if (audio_is_timer_needed(s)) {
        timer_mod_anticipate_ns(s->ts,
            qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + s->period_ticks);
        if (!s->timer_running) {
            s->timer_running = true;
            s->timer_last = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
            trace_audio_timer_start(s->period_ticks / SCALE_MS);
        }
    } else {
        timer_del(s->ts);
        if (s->timer_running) {
            s->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 - s->timer_last;
    if (diff > s->period_ticks * 3 / 2) {
        trace_audio_timer_delayed(diff / SCALE_MS);
    }
    s->timer_last = now;
    audio_run(s, "timer");
malc's avatar
malc committed
/*
 * Public API
 */
size_t AUD_write(SWVoiceOut *sw, void *buf, size_t size)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    HWVoiceOut *hw;

    if (!sw) {
        /* XXX: Consider options */
        return size;
    }
    hw = sw->hw;
    if (!hw->enabled) {
Fabrice Bellard's avatar
Fabrice Bellard committed
        dolog ("Writing to disabled voice %s\n", SW_NAME (sw));
Fabrice Bellard's avatar
Fabrice Bellard committed
        return 0;
    }

    if (audio_get_pdo_out(hw->s->dev)->mixing_engine) {
        return audio_pcm_sw_write(sw, buf, size);
    } else {
        return hw->pcm_ops->write(hw, buf, size);
    }
size_t AUD_read(SWVoiceIn *sw, void *buf, size_t size)
    HWVoiceIn *hw;

    if (!sw) {
        /* XXX: Consider options */
        return size;
Fabrice Bellard's avatar
Fabrice Bellard committed
    }
    hw = sw->hw;
    if (!hw->enabled) {
Fabrice Bellard's avatar
Fabrice Bellard committed
        dolog ("Reading from disabled voice %s\n", SW_NAME (sw));
Fabrice Bellard's avatar
Fabrice Bellard committed
    }
    if (audio_get_pdo_in(hw->s->dev)->mixing_engine) {
        return audio_pcm_sw_read(sw, buf, size);
    } else {
        return hw->pcm_ops->read(hw, buf, size);
    }
int AUD_get_buffer_size_out(SWVoiceOut *sw)
Fabrice Bellard's avatar
Fabrice Bellard committed
{
    return sw->hw->samples * sw->hw->info.bytes_per_frame;
}

void AUD_set_active_out (SWVoiceOut *sw, int on)
{
    HWVoiceOut *hw;
Fabrice Bellard's avatar
Fabrice Bellard committed
        return;
Fabrice Bellard's avatar
Fabrice Bellard committed

    hw = sw->hw;
    if (sw->active != on) {
        AudioState *s = sw->s;
        SWVoiceOut *temp_sw;
        SWVoiceCap *sc;

        if (on) {
            hw->pending_disable = 0;
            if (!hw->enabled) {
                hw->enabled = 1;
                if (s->vm_running) {
                    if (hw->pcm_ops->enable_out) {
                        hw->pcm_ops->enable_out(hw, true);
                    }
                    audio_reset_timer (s);
            if (hw->enabled) {
                int nb_active = 0;

                for (temp_sw = hw->sw_head.lh_first; temp_sw;
                     temp_sw = temp_sw->entries.le_next) {
                    nb_active += temp_sw->active != 0;
                }

                hw->pending_disable = nb_active == 1;
            }
Fabrice Bellard's avatar
Fabrice Bellard committed
        }

        for (sc = hw->cap_head.lh_first; sc; sc = sc->entries.le_next) {
            sc->sw.active = hw->enabled;
                audio_capture_maybe_changed (sc->cap, 1);
        sw->active = on;
    }
}

void AUD_set_active_in (SWVoiceIn *sw, int on)
{
    HWVoiceIn *hw;

    if (!sw) {
        return;
Fabrice Bellard's avatar
Fabrice Bellard committed
    if (sw->active != on) {
        AudioState *s = sw->s;
Fabrice Bellard's avatar
Fabrice Bellard committed
        if (on) {
            if (!hw->enabled) {
                hw->enabled = 1;
                if (s->vm_running) {
                    if (hw->pcm_ops->enable_in) {
                        hw->pcm_ops->enable_in(hw, true);
                    }
                    audio_reset_timer (s);
Fabrice Bellard's avatar
Fabrice Bellard committed
            }
            sw->total_hw_samples_acquired = hw->total_samples_captured;
            if (hw->enabled) {
Fabrice Bellard's avatar
Fabrice Bellard committed
                int nb_active = 0;

                for (temp_sw = hw->sw_head.lh_first; temp_sw;
                     temp_sw = temp_sw->entries.le_next) {
                    nb_active += temp_sw->active != 0;
Fabrice Bellard's avatar
Fabrice Bellard committed
                }

                if (nb_active == 1) {
                    hw->enabled = 0;
                    if (hw->pcm_ops->enable_in) {
                        hw->pcm_ops->enable_in(hw, false);
                    }
Fabrice Bellard's avatar
Fabrice Bellard committed
                }
            }
        }
        sw->active = on;