Commit 33f18cf7 authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/kraxel/tags/audio-20190821-pull-request' into staging



audio: second batch of -audiodev support, adding support for multiple backends.

# gpg: Signature made Wed 21 Aug 2019 09:40:37 BST
# gpg:                using RSA key 4CB6D8EED3E87138
# gpg: Good signature from "Gerd Hoffmann (work) <kraxel@redhat.com>" [full]
# gpg:                 aka "Gerd Hoffmann <gerd@kraxel.org>" [full]
# gpg:                 aka "Gerd Hoffmann (private) <kraxel@gmail.com>" [full]
# Primary key fingerprint: A032 8CFF B93A 17A7 9901  FE7D 4CB6 D8EE D3E8 7138

* remotes/kraxel/tags/audio-20190821-pull-request:
  audio: fix memory leak reported by ASAN
  audio: use size_t where makes sense
  audio: remove read and write pcm_ops
  paaudio: fix playback glitches
  audio: do not run each backend in audio_run
  audio: remove audio_MIN, audio_MAX
  paaudio: properly disconnect streams in fini_*
  paaudio: do not move stream when sink/source name is specified
  audio: audiodev= parameters no longer optional when -audiodev present
  paaudio: prepare for multiple audiodev
  audio: add audiodev properties to frontends
  audio: add audiodev property to vnc and wav_capture
  audio: basic support for multi backend audio
  audio: reduce glob_audio_state usage
  audio: Add missing fall through comments

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents e65472c7 e76ba19a
Loading
Loading
Loading
Loading
+20 −29
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ struct pollhlp {
    struct pollfd *pfds;
    int count;
    int mask;
    AudioState *s;
};

typedef struct ALSAVoiceOut {
@@ -199,11 +200,11 @@ static void alsa_poll_handler (void *opaque)
        break;

    case SND_PCM_STATE_PREPARED:
        audio_run ("alsa run (prepared)");
        audio_run(hlp->s, "alsa run (prepared)");
        break;

    case SND_PCM_STATE_RUNNING:
        audio_run ("alsa run (running)");
        audio_run(hlp->s, "alsa run (running)");
        break;

    default:
@@ -269,11 +270,6 @@ static int alsa_poll_in (HWVoiceIn *hw)
    return alsa_poll_helper (alsa->handle, &alsa->pollhlp, POLLIN);
}

static int alsa_write (SWVoiceOut *sw, void *buf, int len)
{
    return audio_pcm_sw_write (sw, buf, len);
}

static snd_pcm_format_t aud_to_alsafmt (AudioFormat fmt, int endianness)
{
    switch (fmt) {
@@ -634,7 +630,7 @@ static void alsa_write_pending (ALSAVoiceOut *alsa)

    while (alsa->pending) {
        int left_till_end_samples = hw->samples - alsa->wpos;
        int len = audio_MIN (alsa->pending, left_till_end_samples);
        int len = MIN (alsa->pending, left_till_end_samples);
        char *src = advance (alsa->pcm_buf, alsa->wpos << hw->info.shift);

        while (len) {
@@ -685,10 +681,10 @@ static void alsa_write_pending (ALSAVoiceOut *alsa)
    }
}

static int alsa_run_out (HWVoiceOut *hw, int live)
static size_t alsa_run_out(HWVoiceOut *hw, size_t live)
{
    ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
    int decr;
    size_t decr;
    snd_pcm_sframes_t avail;

    avail = alsa_get_avail (alsa->handle);
@@ -697,7 +693,7 @@ static int alsa_run_out (HWVoiceOut *hw, int live)
        return 0;
    }

    decr = audio_MIN (live, avail);
    decr = MIN (live, avail);
    decr = audio_pcm_hw_clip_out (hw, alsa->pcm_buf, decr, alsa->pending);
    alsa->pending += decr;
    alsa_write_pending (alsa);
@@ -743,12 +739,13 @@ static int alsa_init_out(HWVoiceOut *hw, struct audsettings *as,

    alsa->pcm_buf = audio_calloc(__func__, obt.samples, 1 << hw->info.shift);
    if (!alsa->pcm_buf) {
        dolog ("Could not allocate DAC buffer (%d samples, each %d bytes)\n",
        dolog("Could not allocate DAC buffer (%zu samples, each %d bytes)\n",
              hw->samples, 1 << hw->info.shift);
        alsa_anal_close1 (&handle);
        return -1;
    }

    alsa->pollhlp.s = hw->s;
    alsa->handle = handle;
    alsa->dev = dev;
    return 0;
@@ -844,12 +841,13 @@ static int alsa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)

    alsa->pcm_buf = audio_calloc(__func__, hw->samples, 1 << hw->info.shift);
    if (!alsa->pcm_buf) {
        dolog ("Could not allocate ADC buffer (%d samples, each %d bytes)\n",
        dolog("Could not allocate ADC buffer (%zu samples, each %d bytes)\n",
              hw->samples, 1 << hw->info.shift);
        alsa_anal_close1 (&handle);
        return -1;
    }

    alsa->pollhlp.s = hw->s;
    alsa->handle = handle;
    alsa->dev = dev;
    return 0;
@@ -865,17 +863,17 @@ static void alsa_fini_in (HWVoiceIn *hw)
    alsa->pcm_buf = NULL;
}

static int alsa_run_in (HWVoiceIn *hw)
static size_t alsa_run_in(HWVoiceIn *hw)
{
    ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
    int hwshift = hw->info.shift;
    int i;
    int live = audio_pcm_hw_get_live_in (hw);
    int dead = hw->samples - live;
    int decr;
    size_t live = audio_pcm_hw_get_live_in (hw);
    size_t dead = hw->samples - live;
    size_t decr;
    struct {
        int add;
        int len;
        size_t add;
        size_t len;
    } bufs[2] = {
        { .add = hw->wpos, .len = 0 },
        { .add = 0,        .len = 0 }
@@ -915,7 +913,7 @@ static int alsa_run_in (HWVoiceIn *hw)
        }
    }

    decr = audio_MIN (dead, avail);
    decr = MIN(dead, avail);
    if (!decr) {
        return 0;
    }
@@ -985,11 +983,6 @@ static int alsa_run_in (HWVoiceIn *hw)
    return read_samples;
}

static int alsa_read (SWVoiceIn *sw, void *buf, int size)
{
    return audio_pcm_sw_read (sw, buf, size);
}

static int alsa_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
    ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
@@ -1073,13 +1066,11 @@ static struct audio_pcm_ops alsa_pcm_ops = {
    .init_out = alsa_init_out,
    .fini_out = alsa_fini_out,
    .run_out  = alsa_run_out,
    .write    = alsa_write,
    .ctl_out  = alsa_ctl_out,

    .init_in  = alsa_init_in,
    .fini_in  = alsa_fini_in,
    .run_in   = alsa_run_in,
    .read     = alsa_read,
    .ctl_in   = alsa_ctl_in,
};

+201 −146

File changed.

Preview size limit exceeded, changes collapsed.

+15 −22
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@

#include "qemu/queue.h"
#include "qapi/qapi-types-audio.h"
#include "hw/qdev-properties.h"

typedef void (*audio_callback_fn) (void *opaque, int avail);

@@ -78,8 +79,10 @@ typedef struct SWVoiceOut SWVoiceOut;
typedef struct CaptureVoiceOut CaptureVoiceOut;
typedef struct SWVoiceIn SWVoiceIn;

typedef struct AudioState AudioState;
typedef struct QEMUSoundCard {
    char *name;
    AudioState *state;
    QLIST_ENTRY (QEMUSoundCard) entries;
} QEMUSoundCard;

@@ -93,6 +96,7 @@ void AUD_log (const char *cap, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
void AUD_register_card (const char *name, QEMUSoundCard *card);
void AUD_remove_card (QEMUSoundCard *card);
CaptureVoiceOut *AUD_add_capture(
    AudioState *s,
    struct audsettings *as,
    struct audio_capture_ops *ops,
    void *opaque
@@ -109,7 +113,7 @@ SWVoiceOut *AUD_open_out (
    );

void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw);
int  AUD_write (SWVoiceOut *sw, void *pcm_buf, int size);
size_t AUD_write (SWVoiceOut *sw, void *pcm_buf, size_t size);
int  AUD_get_buffer_size_out (SWVoiceOut *sw);
void AUD_set_active_out (SWVoiceOut *sw, int on);
int  AUD_is_active_out (SWVoiceOut *sw);
@@ -130,7 +134,7 @@ SWVoiceIn *AUD_open_in (
    );

void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw);
int  AUD_read (SWVoiceIn *sw, void *pcm_buf, int size);
size_t AUD_read (SWVoiceIn *sw, void *pcm_buf, size_t size);
void AUD_set_active_in (SWVoiceIn *sw, int on);
int  AUD_is_active_in (SWVoiceIn *sw);

@@ -143,25 +147,8 @@ static inline void *advance (void *p, int incr)
    return (d + incr);
}

#ifdef __GNUC__
#define audio_MIN(a, b) ( __extension__ ({      \
    __typeof (a) ta = a;                        \
    __typeof (b) tb = b;                        \
    ((ta)>(tb)?(tb):(ta));                      \
}))

#define audio_MAX(a, b) ( __extension__ ({      \
    __typeof (a) ta = a;                        \
    __typeof (b) tb = b;                        \
    ((ta)<(tb)?(tb):(ta));                      \
}))
#else
#define audio_MIN(a, b) ((a)>(b)?(b):(a))
#define audio_MAX(a, b) ((a)<(b)?(b):(a))
#endif

int wav_start_capture (CaptureState *s, const char *path, int freq,
                       int bits, int nchannels);
int wav_start_capture(AudioState *state, CaptureState *s, const char *path,
                      int freq, int bits, int nchannels);

bool audio_is_cleaning_up(void);
void audio_cleanup(void);
@@ -175,4 +162,10 @@ void audio_parse_option(const char *opt);
void audio_init_audiodevs(void);
void audio_legacy_help(void);

AudioState *audio_state_by_name(const char *name);
const char *audio_get_id(QEMUSoundCard *card);

#define DEFINE_AUDIO_PROPERTIES(_s, _f)         \
    DEFINE_PROP_AUDIODEV("audiodev", _s, _f)

#endif /* QEMU_AUDIO_H */
+24 −19
Original line number Diff line number Diff line
@@ -49,9 +49,11 @@ struct audio_pcm_info {
    int swap_endianness;
};

typedef struct AudioState AudioState;
typedef struct SWVoiceCap SWVoiceCap;

typedef struct HWVoiceOut {
    AudioState *s;
    int enabled;
    int poll_mode;
    int pending_disable;
@@ -59,12 +61,12 @@ typedef struct HWVoiceOut {

    f_sample *clip;

    int rpos;
    size_t rpos;
    uint64_t ts_helper;

    struct st_sample *mix_buf;

    int samples;
    size_t samples;
    QLIST_HEAD (sw_out_listhead, SWVoiceOut) sw_head;
    QLIST_HEAD (sw_cap_listhead, SWVoiceCap) cap_head;
    int ctl_caps;
@@ -73,19 +75,20 @@ typedef struct HWVoiceOut {
} HWVoiceOut;

typedef struct HWVoiceIn {
    AudioState *s;
    int enabled;
    int poll_mode;
    struct audio_pcm_info info;

    t_sample *conv;

    int wpos;
    int total_samples_captured;
    size_t wpos;
    size_t total_samples_captured;
    uint64_t ts_helper;

    struct st_sample *conv_buf;

    int samples;
    size_t samples;
    QLIST_HEAD (sw_in_listhead, SWVoiceIn) sw_head;
    int ctl_caps;
    struct audio_pcm_ops *pcm_ops;
@@ -94,12 +97,13 @@ typedef struct HWVoiceIn {

struct SWVoiceOut {
    QEMUSoundCard *card;
    AudioState *s;
    struct audio_pcm_info info;
    t_sample *conv;
    int64_t ratio;
    struct st_sample *buf;
    void *rate;
    int total_hw_samples_mixed;
    size_t total_hw_samples_mixed;
    int active;
    int empty;
    HWVoiceOut *hw;
@@ -111,11 +115,12 @@ struct SWVoiceOut {

struct SWVoiceIn {
    QEMUSoundCard *card;
    AudioState *s;
    int active;
    struct audio_pcm_info info;
    int64_t ratio;
    void *rate;
    int total_hw_samples_acquired;
    size_t total_hw_samples_acquired;
    struct st_sample *buf;
    f_sample *clip;
    HWVoiceIn *hw;
@@ -144,14 +149,12 @@ struct audio_driver {
struct audio_pcm_ops {
    int  (*init_out)(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque);
    void (*fini_out)(HWVoiceOut *hw);
    int  (*run_out) (HWVoiceOut *hw, int live);
    int  (*write)   (SWVoiceOut *sw, void *buf, int size);
    size_t (*run_out)(HWVoiceOut *hw, size_t live);
    int  (*ctl_out) (HWVoiceOut *hw, int cmd, ...);

    int  (*init_in) (HWVoiceIn *hw, struct audsettings *as, void *drv_opaque);
    void (*fini_in) (HWVoiceIn *hw);
    int  (*run_in)  (HWVoiceIn *hw);
    int  (*read)    (SWVoiceIn *sw, void *buf, int size);
    size_t (*run_in)(HWVoiceIn *hw);
    int  (*ctl_in)  (HWVoiceIn *hw, int cmd, ...);
};

@@ -188,6 +191,11 @@ typedef struct AudioState {
    int nb_hw_voices_in;
    int vm_running;
    int64_t period_ticks;

    bool timer_running;
    uint64_t timer_last;

    QTAILQ_ENTRY(AudioState) list;
} AudioState;

extern const struct mixeng_volume nominal_volume;
@@ -200,18 +208,15 @@ audio_driver *audio_driver_lookup(const char *name);
void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);

int  audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len);
int  audio_pcm_hw_get_live_in (HWVoiceIn *hw);

int  audio_pcm_sw_read (SWVoiceIn *sw, void *buf, int len);
size_t audio_pcm_hw_get_live_in(HWVoiceIn *hw);

int audio_pcm_hw_clip_out (HWVoiceOut *hw, void *pcm_buf,
                           int live, int pending);
size_t audio_pcm_hw_clip_out(HWVoiceOut *hw, void *pcm_buf,
                             size_t live, size_t pending);

int audio_bug (const char *funcname, int cond);
void *audio_calloc (const char *funcname, int nmemb, size_t size);

void audio_run (const char *msg);
void audio_run(AudioState *s, const char *msg);

#define VOICE_ENABLE 1
#define VOICE_DISABLE 2
@@ -219,7 +224,7 @@ void audio_run (const char *msg);

#define VOICE_VOLUME_CAP (1 << VOICE_VOLUME)

static inline int audio_ring_dist (int dst, int src, int len)
static inline size_t audio_ring_dist(size_t dst, size_t src, size_t len)
{
    return (dst >= src) ? (dst - src) : (len - src + dst);
}
+30 −32
Original line number Diff line number Diff line
@@ -36,9 +36,9 @@
#define HWBUF hw->conv_buf
#endif

static void glue (audio_init_nb_voices_, TYPE) (struct audio_driver *drv)
static void glue(audio_init_nb_voices_, TYPE)(AudioState *s,
                                              struct audio_driver *drv)
{
    AudioState *s = &glob_audio_state;
    int max_voices = glue (drv->max_voices_, TYPE);
    int voice_size = glue (drv->voice_size_, TYPE);

@@ -75,16 +75,16 @@ static void glue (audio_pcm_hw_free_resources_, TYPE) (HW *hw)
    HWBUF = NULL;
}

static int glue (audio_pcm_hw_alloc_resources_, TYPE) (HW *hw)
static bool glue(audio_pcm_hw_alloc_resources_, TYPE)(HW *hw)
{
    HWBUF = audio_calloc(__func__, hw->samples, sizeof(struct st_sample));
    if (!HWBUF) {
        dolog ("Could not allocate " NAME " buffer (%d samples)\n",
        dolog("Could not allocate " NAME " buffer (%zu samples)\n",
              hw->samples);
        return -1;
        return false;
    }

    return 0;
    return true;
}

static void glue (audio_pcm_sw_free_resources_, TYPE) (SW *sw)
@@ -183,8 +183,8 @@ static void glue (audio_pcm_hw_del_sw_, TYPE) (SW *sw)

static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
{
    AudioState *s = &glob_audio_state;
    HW *hw = *hwp;
    AudioState *s = hw->s;

    if (!hw->sw_head.lh_first) {
#ifdef DAC
@@ -199,15 +199,14 @@ static void glue (audio_pcm_hw_gc_, TYPE) (HW **hwp)
    }
}

static HW *glue (audio_pcm_hw_find_any_, TYPE) (HW *hw)
static HW *glue(audio_pcm_hw_find_any_, TYPE)(AudioState *s, HW *hw)
{
    AudioState *s = &glob_audio_state;
    return hw ? hw->entries.le_next : glue (s->hw_head_, TYPE).lh_first;
}

static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw)
static HW *glue(audio_pcm_hw_find_any_enabled_, TYPE)(AudioState *s, HW *hw)
{
    while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
    while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
        if (hw->enabled) {
            return hw;
        }
@@ -215,12 +214,10 @@ static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (HW *hw)
    return NULL;
}

static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
    HW *hw,
    struct audsettings *as
    )
static HW *glue(audio_pcm_hw_find_specific_, TYPE)(AudioState *s, HW *hw,
                                                   struct audsettings *as)
{
    while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
    while ((hw = glue(audio_pcm_hw_find_any_, TYPE)(s, hw))) {
        if (audio_pcm_info_eq (&hw->info, as)) {
            return hw;
        }
@@ -228,10 +225,10 @@ static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
    return NULL;
}

static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s,
                                             struct audsettings *as)
{
    HW *hw;
    AudioState *s = &glob_audio_state;
    struct audio_driver *drv = s->drv;

    if (!glue (s->nb_hw_voices_, TYPE)) {
@@ -255,6 +252,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
        return NULL;
    }

    hw->s = s;
    hw->pcm_ops = drv->pcm_ops;
    hw->ctl_caps = drv->ctl_caps;

@@ -267,7 +265,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
    }

    if (audio_bug(__func__, hw->samples <= 0)) {
        dolog ("hw->samples=%d\n", hw->samples);
        dolog("hw->samples=%zd\n", hw->samples);
        goto err1;
    }

@@ -281,7 +279,7 @@ static HW *glue (audio_pcm_hw_add_new_, TYPE) (struct audsettings *as)
        [hw->info.swap_endianness]
        [audio_bits_to_index (hw->info.bits)];

    if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) {
    if (!glue(audio_pcm_hw_alloc_resources_, TYPE)(hw)) {
        goto err1;
    }

@@ -328,33 +326,33 @@ AudiodevPerDirectionOptions *glue(audio_get_pdo_, TYPE)(Audiodev *dev)
    abort();
}

static HW *glue (audio_pcm_hw_add_, TYPE) (struct audsettings *as)
static HW *glue(audio_pcm_hw_add_, TYPE)(AudioState *s, struct audsettings *as)
{
    HW *hw;
    AudioState *s = &glob_audio_state;
    AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);

    if (pdo->fixed_settings) {
        hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
        hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
        if (hw) {
            return hw;
        }
    }

    hw = glue (audio_pcm_hw_find_specific_, TYPE) (NULL, as);
    hw = glue(audio_pcm_hw_find_specific_, TYPE)(s, NULL, as);
    if (hw) {
        return hw;
    }

    hw = glue (audio_pcm_hw_add_new_, TYPE) (as);
    hw = glue(audio_pcm_hw_add_new_, TYPE)(s, as);
    if (hw) {
        return hw;
    }

    return glue (audio_pcm_hw_find_any_, TYPE) (NULL);
    return glue(audio_pcm_hw_find_any_, TYPE)(s, NULL);
}

static SW *glue(audio_pcm_create_voice_pair_, TYPE)(
    AudioState *s,
    const char *sw_name,
    struct audsettings *as
    )
@@ -362,7 +360,6 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
    SW *sw;
    HW *hw;
    struct audsettings hw_as;
    AudioState *s = &glob_audio_state;
    AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);

    if (pdo->fixed_settings) {
@@ -378,8 +375,9 @@ static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
               sw_name ? sw_name : "unknown", sizeof (*sw));
        goto err1;
    }
    sw->s = s;

    hw = glue (audio_pcm_hw_add_, TYPE) (&hw_as);
    hw = glue(audio_pcm_hw_add_, TYPE)(s, &hw_as);
    if (!hw) {
        goto err2;
    }
@@ -430,7 +428,7 @@ SW *glue (AUD_open_, TYPE) (
    struct audsettings *as
    )
{
    AudioState *s = &glob_audio_state;
    AudioState *s = card->state;
    AudiodevPerDirectionOptions *pdo = glue(audio_get_pdo_, TYPE)(s->dev);

    if (audio_bug(__func__, !card || !name || !callback_fn || !as)) {
@@ -476,7 +474,7 @@ SW *glue (AUD_open_, TYPE) (
        }
    }
    else {
        sw = glue (audio_pcm_create_voice_pair_, TYPE) (name, as);
        sw = glue(audio_pcm_create_voice_pair_, TYPE)(s, name, as);
        if (!sw) {
            dolog ("Failed to create voice `%s'\n", name);
            return NULL;
Loading