Commit 857271a2 authored by Kővágó, Zoltán's avatar Kővágó, Zoltán Committed by Gerd Hoffmann
Browse files

audio: common rate control code for timer based outputs



This commit removes the ad-hoc rate-limiting code from noaudio and
wavaudio, and replaces them with a (slightly modified) code from
spiceaudio.  This way multiple write calls (for example when the
circular buffer wraps around) do not cause problems.

Signed-off-by: default avatarKővágó, Zoltán <DirtY.iCE.hu@gmail.com>
Message-id: fd0fe5b95b13fa26d09ae77a72f99d0ea411de14.1568927990.git.DirtY.iCE.hu@gmail.com
Signed-off-by: default avatarGerd Hoffmann <kraxel@redhat.com>
parent dc88e38f
Loading
Loading
Loading
Loading
+30 −0
Original line number Diff line number Diff line
@@ -2051,3 +2051,33 @@ const char *audio_get_id(QEMUSoundCard *card)
        return "";
    }
}

void audio_rate_start(RateCtl *rate)
{
    memset(rate, 0, sizeof(RateCtl));
    rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
}

size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
                            size_t bytes_avail)
{
    int64_t now;
    int64_t ticks;
    int64_t bytes;
    int64_t samples;
    size_t ret;

    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    ticks = now - rate->start_ticks;
    bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND);
    samples = (bytes - rate->bytes_sent) >> info->shift;
    if (samples < 0 || samples > 65536) {
        AUD_log(NULL, "Resetting rate control (%" PRId64 " samples)\n", samples);
        audio_rate_start(rate);
        samples = 0;
    }

    ret = MIN(samples << info->shift, bytes_avail);
    rate->bytes_sent += ret;
    return ret;
}
+9 −0
Original line number Diff line number Diff line
@@ -242,6 +242,15 @@ void *audio_calloc (const char *funcname, int nmemb, size_t size);

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

typedef struct RateCtl {
    int64_t start_ticks;
    int64_t bytes_sent;
} RateCtl;

void audio_rate_start(RateCtl *rate);
size_t audio_rate_get_bytes(struct audio_pcm_info *info, RateCtl *rate,
                            size_t bytes_avail);

#define VOICE_ENABLE 1
#define VOICE_DISABLE 2
#define VOICE_VOLUME 3
+22 −27
Original line number Diff line number Diff line
@@ -33,33 +33,27 @@

typedef struct NoVoiceOut {
    HWVoiceOut hw;
    int64_t old_ticks;
    RateCtl rate;
} NoVoiceOut;

typedef struct NoVoiceIn {
    HWVoiceIn hw;
    int64_t old_ticks;
    RateCtl rate;
} NoVoiceIn;

static size_t no_write(HWVoiceOut *hw, void *buf, size_t len)
{
    NoVoiceOut *no = (NoVoiceOut *) hw;
    int64_t now;
    int64_t ticks;
    int64_t bytes;

    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    ticks = now - no->old_ticks;
    bytes = muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);

    no->old_ticks = now;
    return MIN(len, bytes);
    return audio_rate_get_bytes(&hw->info, &no->rate, len);
}

static int no_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
{
    NoVoiceOut *no = (NoVoiceOut *) hw;

    audio_pcm_init_info (&hw->info, as);
    hw->samples = 1024;
    audio_rate_start(&no->rate);
    return 0;
}

@@ -70,15 +64,21 @@ static void no_fini_out (HWVoiceOut *hw)

static int no_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
    (void) hw;
    (void) cmd;
    NoVoiceOut *no = (NoVoiceOut *) hw;

    if (cmd == VOICE_ENABLE) {
        audio_rate_start(&no->rate);
    }
    return 0;
}

static int no_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
{
    NoVoiceIn *no = (NoVoiceIn *) hw;

    audio_pcm_init_info (&hw->info, as);
    hw->samples = 1024;
    audio_rate_start(&no->rate);
    return 0;
}

@@ -89,25 +89,20 @@ static void no_fini_in (HWVoiceIn *hw)

static size_t no_read(HWVoiceIn *hw, void *buf, size_t size)
{
    size_t to_clear;
    NoVoiceIn *no = (NoVoiceIn *) hw;
    int64_t bytes = audio_rate_get_bytes(&hw->info, &no->rate, size);

    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    int64_t ticks = now - no->old_ticks;
    int64_t bytes =
        muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);

    no->old_ticks = now;
    to_clear = MIN(bytes, size);

    audio_pcm_info_clear_buf(&hw->info, buf, to_clear >> hw->info.shift);
    return to_clear;
    audio_pcm_info_clear_buf(&hw->info, buf, bytes >> hw->info.shift);
    return bytes;
}

static int no_ctl_in (HWVoiceIn *hw, int cmd, ...)
{
    (void) hw;
    (void) cmd;
    NoVoiceIn *no = (NoVoiceIn *) hw;

    if (cmd == VOICE_ENABLE) {
        audio_rate_start(&no->rate);
    }
    return 0;
}

+8 −43
Original line number Diff line number Diff line
@@ -40,15 +40,10 @@
#define LINE_IN_SAMPLES (256 * 4)
#endif

typedef struct SpiceRateCtl {
    int64_t               start_ticks;
    int64_t               bytes_sent;
} SpiceRateCtl;

typedef struct SpiceVoiceOut {
    HWVoiceOut            hw;
    SpicePlaybackInstance sin;
    SpiceRateCtl          rate;
    RateCtl               rate;
    int                   active;
    uint32_t              *frame;
    uint32_t              fpos;
@@ -58,7 +53,7 @@ typedef struct SpiceVoiceOut {
typedef struct SpiceVoiceIn {
    HWVoiceIn             hw;
    SpiceRecordInstance   sin;
    SpiceRateCtl          rate;
    RateCtl               rate;
    int                   active;
} SpiceVoiceIn;

@@ -89,32 +84,6 @@ static void spice_audio_fini (void *opaque)
    /* nothing */
}

static void rate_start (SpiceRateCtl *rate)
{
    memset (rate, 0, sizeof (*rate));
    rate->start_ticks = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
}

static int rate_get_samples (struct audio_pcm_info *info, SpiceRateCtl *rate)
{
    int64_t now;
    int64_t ticks;
    int64_t bytes;
    int64_t samples;

    now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    ticks = now - rate->start_ticks;
    bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND);
    samples = (bytes - rate->bytes_sent) >> info->shift;
    if (samples < 0 || samples > 65536) {
        error_report("Resetting rate control (%" PRId64 " samples)", samples);
        rate_start(rate);
        samples = 0;
    }
    rate->bytes_sent += samples << info->shift;
    return samples;
}

/* playback */

static int line_out_init(HWVoiceOut *hw, struct audsettings *as,
@@ -154,7 +123,6 @@ static void line_out_fini (HWVoiceOut *hw)
static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size)
{
    SpiceVoiceOut *out = container_of(hw, SpiceVoiceOut, hw);
    size_t decr;

    if (!out->frame) {
        spice_server_playback_get_buffer(&out->sin, &out->frame, &out->fsize);
@@ -162,12 +130,10 @@ static void *line_out_get_buffer(HWVoiceOut *hw, size_t *size)
    }

    if (out->frame) {
        decr = rate_get_samples(&hw->info, &out->rate);
        decr = MIN(out->fsize - out->fpos, decr);

        *size = decr << hw->info.shift;
        *size = audio_rate_get_bytes(
            &hw->info, &out->rate, (out->fsize - out->fpos) << hw->info.shift);
    } else {
        rate_start(&out->rate);
        audio_rate_start(&out->rate);
    }
    return out->frame + out->fpos;
}
@@ -197,7 +163,7 @@ static int line_out_ctl (HWVoiceOut *hw, int cmd, ...)
            break;
        }
        out->active = 1;
        rate_start (&out->rate);
        audio_rate_start(&out->rate);
        spice_server_playback_start (&out->sin);
        break;
    case VOICE_DISABLE:
@@ -273,8 +239,7 @@ static void line_in_fini (HWVoiceIn *hw)
static size_t line_in_read(HWVoiceIn *hw, void *buf, size_t len)
{
    SpiceVoiceIn *in = container_of (hw, SpiceVoiceIn, hw);
    uint64_t delta_samp = rate_get_samples(&hw->info, &in->rate);
    uint64_t to_read = MIN(len >> 2, delta_samp);
    uint64_t to_read = audio_rate_get_bytes(&hw->info, &in->rate, len) >> 2;
    size_t ready = spice_server_record_get_samples(&in->sin, buf, to_read);

    /* XXX: do we need this? */
@@ -296,7 +261,7 @@ static int line_in_ctl (HWVoiceIn *hw, int cmd, ...)
            break;
        }
        in->active = 1;
        rate_start (&in->rate);
        audio_rate_start(&in->rate);
        spice_server_record_start (&in->sin);
        break;
    case VOICE_DISABLE:
+10 −11
Original line number Diff line number Diff line
@@ -35,21 +35,15 @@
typedef struct WAVVoiceOut {
    HWVoiceOut hw;
    FILE *f;
    int64_t old_ticks;
    RateCtl rate;
    int total_samples;
} WAVVoiceOut;

static size_t wav_write_out(HWVoiceOut *hw, void *buf, size_t len)
{
    WAVVoiceOut *wav = (WAVVoiceOut *) hw;
    int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
    int64_t ticks = now - wav->old_ticks;
    int64_t bytes =
        muldiv64(ticks, hw->info.bytes_per_second, NANOSECONDS_PER_SECOND);

    bytes = MIN(bytes, len);
    bytes = bytes >> hw->info.shift << hw->info.shift;
    wav->old_ticks = now;
    int64_t bytes = audio_rate_get_bytes(&hw->info, &wav->rate, len);
    assert(bytes >> hw->info.shift << hw->info.shift == bytes);

    if (bytes && fwrite(buf, bytes, 1, wav->f) != 1) {
        dolog("wav_write_out: fwrite of %" PRId64 " bytes failed\nReason: %s\n",
@@ -130,6 +124,8 @@ static int wav_init_out(HWVoiceOut *hw, struct audsettings *as,
               strerror(errno));
        return -1;
    }

    audio_rate_start(&wav->rate);
    return 0;
}

@@ -179,8 +175,11 @@ static void wav_fini_out (HWVoiceOut *hw)

static int wav_ctl_out (HWVoiceOut *hw, int cmd, ...)
{
    (void) hw;
    (void) cmd;
    WAVVoiceOut *wav = (WAVVoiceOut *) hw;

    if (cmd == VOICE_ENABLE) {
        audio_rate_start(&wav->rate);
    }
    return 0;
}