audio: fix bug 1858488
The combined generic buffer management code and buffer run out code in function audio_generic_put_buffer_out has a problematic behaviour. A few hundred milliseconds after playback starts the mixing buffer and the generic buffer are nearly full and the following pattern can be seen. On first call of audio_pcm_hw_run_out the buffer run code in audio_generic_put_buffer_out writes some data to the audio hardware but the generic buffer will fill faster and is full when audio_pcm_hw_run_out returns. This is because emulated audio devices can produce playback data at a higher rate than the audio backend hardware consumes this data. On next call of audio_pcm_hw_run_out the buffer run code in audio_generic_put_buffer_out writes some data to the audio hardware but no audio data is transferred to the generic buffer because the buffer is already full. Then the pattern repeats. For the emulated audio device this looks like the audio timer period has doubled. This patch splits the combined generic buffer management code and buffer run out code and calls the buffer run out code after buffer management code to break this pattern. The bug report is for the wav audio backend. But the problem is not limited to this backend. All audio backends which use the audio_generic_put_buffer_out function show this problem. Buglink: https://bugs.launchpad.net/qemu/+bug/1858488 Signed-off-by: Volker Rümelin <vr_qemu@t-online.de> Message-Id: <20200123074943.6699-5-vr_qemu@t-online.de> Signed-off-by: Gerd Hoffmann <kraxel@redhat.com>
This commit is contained in:
parent
69ac078632
commit
fdc8c5f471
@ -906,6 +906,7 @@ static struct audio_pcm_ops alsa_pcm_ops = {
|
||||
.init_out = alsa_init_out,
|
||||
.fini_out = alsa_fini_out,
|
||||
.write = alsa_write,
|
||||
.run_buffer_out = audio_generic_run_buffer_out,
|
||||
.enable_out = alsa_enable_out,
|
||||
|
||||
.init_in = alsa_init_in,
|
||||
|
@ -1097,6 +1097,10 @@ static size_t audio_pcm_hw_run_out(HWVoiceOut *hw, size_t live)
|
||||
}
|
||||
}
|
||||
|
||||
if (hw->pcm_ops->run_buffer_out) {
|
||||
hw->pcm_ops->run_buffer_out(hw);
|
||||
}
|
||||
|
||||
return clipped;
|
||||
}
|
||||
|
||||
@ -1413,6 +1417,28 @@ void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size)
|
||||
hw->pending_emul -= size;
|
||||
}
|
||||
|
||||
void audio_generic_run_buffer_out(HWVoiceOut *hw)
|
||||
{
|
||||
while (hw->pending_emul) {
|
||||
size_t write_len, written;
|
||||
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
|
||||
|
||||
if (start < 0) {
|
||||
start += hw->size_emul;
|
||||
}
|
||||
assert(start >= 0 && start < hw->size_emul);
|
||||
|
||||
write_len = MIN(hw->pending_emul, hw->size_emul - start);
|
||||
|
||||
written = hw->pcm_ops->write(hw, hw->buf_emul + start, write_len);
|
||||
hw->pending_emul -= written;
|
||||
|
||||
if (written < write_len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
||||
{
|
||||
if (unlikely(!hw->buf_emul)) {
|
||||
@ -1428,8 +1454,7 @@ void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
||||
return hw->buf_emul + hw->pos_emul;
|
||||
}
|
||||
|
||||
size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
|
||||
size_t size)
|
||||
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
|
||||
{
|
||||
assert(buf == hw->buf_emul + hw->pos_emul &&
|
||||
size + hw->pending_emul <= hw->size_emul);
|
||||
@ -1440,35 +1465,6 @@ size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size)
|
||||
{
|
||||
audio_generic_put_buffer_out_nowrite(hw, buf, size);
|
||||
|
||||
while (hw->pending_emul) {
|
||||
size_t write_len, written;
|
||||
ssize_t start = ((ssize_t) hw->pos_emul) - hw->pending_emul;
|
||||
if (start < 0) {
|
||||
start += hw->size_emul;
|
||||
}
|
||||
assert(start >= 0 && start < hw->size_emul);
|
||||
|
||||
write_len = MIN(hw->pending_emul, hw->size_emul - start);
|
||||
|
||||
written = hw->pcm_ops->write(hw, hw->buf_emul + start, write_len);
|
||||
hw->pending_emul -= written;
|
||||
|
||||
if (written < write_len) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* fake we have written everything. non-written data remain in pending_emul,
|
||||
* so we do not have to clip them multiple times
|
||||
*/
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size)
|
||||
{
|
||||
size_t dst_size, copy_size;
|
||||
|
@ -152,6 +152,7 @@ struct audio_pcm_ops {
|
||||
int (*init_out)(HWVoiceOut *hw, audsettings *as, void *drv_opaque);
|
||||
void (*fini_out)(HWVoiceOut *hw);
|
||||
size_t (*write) (HWVoiceOut *hw, void *buf, size_t size);
|
||||
void (*run_buffer_out)(HWVoiceOut *hw);
|
||||
/*
|
||||
* get a buffer that after later can be passed to put_buffer_out; optional
|
||||
* returns the buffer, and writes it's size to size (in bytes)
|
||||
@ -178,10 +179,9 @@ struct audio_pcm_ops {
|
||||
|
||||
void *audio_generic_get_buffer_in(HWVoiceIn *hw, size_t *size);
|
||||
void audio_generic_put_buffer_in(HWVoiceIn *hw, void *buf, size_t size);
|
||||
void audio_generic_run_buffer_out(HWVoiceOut *hw);
|
||||
void *audio_generic_get_buffer_out(HWVoiceOut *hw, size_t *size);
|
||||
size_t audio_generic_put_buffer_out(HWVoiceOut *hw, void *buf, size_t size);
|
||||
size_t audio_generic_put_buffer_out_nowrite(HWVoiceOut *hw, void *buf,
|
||||
size_t size);
|
||||
size_t audio_generic_write(HWVoiceOut *hw, void *buf, size_t size);
|
||||
size_t audio_generic_read(HWVoiceIn *hw, void *buf, size_t size);
|
||||
|
||||
|
@ -411,7 +411,7 @@ static int coreaudio_unlock (coreaudioVoiceOut *core, const char *fn_name)
|
||||
}
|
||||
COREAUDIO_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
|
||||
(hw, size))
|
||||
COREAUDIO_WRAPPER_FUNC(put_buffer_out_nowrite, size_t,
|
||||
COREAUDIO_WRAPPER_FUNC(put_buffer_out, size_t,
|
||||
(HWVoiceOut *hw, void *buf, size_t size),
|
||||
(hw, buf, size))
|
||||
COREAUDIO_WRAPPER_FUNC(write, size_t, (HWVoiceOut *hw, void *buf, size_t size),
|
||||
@ -687,9 +687,12 @@ static void coreaudio_audio_fini (void *opaque)
|
||||
static struct audio_pcm_ops coreaudio_pcm_ops = {
|
||||
.init_out = coreaudio_init_out,
|
||||
.fini_out = coreaudio_fini_out,
|
||||
/* wrapper for audio_generic_write */
|
||||
.write = coreaudio_write,
|
||||
/* wrapper for audio_generic_get_buffer_out */
|
||||
.get_buffer_out = coreaudio_get_buffer_out,
|
||||
.put_buffer_out = coreaudio_put_buffer_out_nowrite,
|
||||
/* wrapper for audio_generic_put_buffer_out */
|
||||
.put_buffer_out = coreaudio_put_buffer_out,
|
||||
.enable_out = coreaudio_enable_out
|
||||
};
|
||||
|
||||
|
@ -118,6 +118,7 @@ static struct audio_pcm_ops no_pcm_ops = {
|
||||
.init_out = no_init_out,
|
||||
.fini_out = no_fini_out,
|
||||
.write = no_write,
|
||||
.run_buffer_out = audio_generic_run_buffer_out,
|
||||
.enable_out = no_enable_out,
|
||||
|
||||
.init_in = no_init_in,
|
||||
|
@ -382,6 +382,15 @@ static size_t oss_get_available_bytes(OSSVoiceOut *oss)
|
||||
return audio_ring_dist(cntinfo.ptr, oss->hw.pos_emul, oss->hw.size_emul);
|
||||
}
|
||||
|
||||
static void oss_run_buffer_out(HWVoiceOut *hw)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *)hw;
|
||||
|
||||
if (!oss->mmapped) {
|
||||
audio_generic_run_buffer_out(hw);
|
||||
}
|
||||
}
|
||||
|
||||
static void *oss_get_buffer_out(HWVoiceOut *hw, size_t *size)
|
||||
{
|
||||
OSSVoiceOut *oss = (OSSVoiceOut *) hw;
|
||||
@ -748,6 +757,7 @@ static struct audio_pcm_ops oss_pcm_ops = {
|
||||
.init_out = oss_init_out,
|
||||
.fini_out = oss_fini_out,
|
||||
.write = oss_write,
|
||||
.run_buffer_out = oss_run_buffer_out,
|
||||
.get_buffer_out = oss_get_buffer_out,
|
||||
.put_buffer_out = oss_put_buffer_out,
|
||||
.enable_out = oss_enable_out,
|
||||
|
@ -227,7 +227,7 @@ static void sdl_callback (void *opaque, Uint8 *buf, int len)
|
||||
|
||||
SDL_WRAPPER_FUNC(get_buffer_out, void *, (HWVoiceOut *hw, size_t *size),
|
||||
(hw, size), *size = 0, sdl_unlock)
|
||||
SDL_WRAPPER_FUNC(put_buffer_out_nowrite, size_t,
|
||||
SDL_WRAPPER_FUNC(put_buffer_out, size_t,
|
||||
(HWVoiceOut *hw, void *buf, size_t size), (hw, buf, size),
|
||||
/*nothing*/, sdl_unlock_and_post)
|
||||
SDL_WRAPPER_FUNC(write, size_t,
|
||||
@ -320,9 +320,12 @@ static void sdl_audio_fini (void *opaque)
|
||||
static struct audio_pcm_ops sdl_pcm_ops = {
|
||||
.init_out = sdl_init_out,
|
||||
.fini_out = sdl_fini_out,
|
||||
/* wrapper for audio_generic_write */
|
||||
.write = sdl_write,
|
||||
/* wrapper for audio_generic_get_buffer_out */
|
||||
.get_buffer_out = sdl_get_buffer_out,
|
||||
.put_buffer_out = sdl_put_buffer_out_nowrite,
|
||||
/* wrapper for audio_generic_put_buffer_out */
|
||||
.put_buffer_out = sdl_put_buffer_out,
|
||||
.enable_out = sdl_enable_out,
|
||||
};
|
||||
|
||||
|
@ -197,6 +197,7 @@ static struct audio_pcm_ops wav_pcm_ops = {
|
||||
.init_out = wav_init_out,
|
||||
.fini_out = wav_fini_out,
|
||||
.write = wav_write_out,
|
||||
.run_buffer_out = audio_generic_run_buffer_out,
|
||||
.enable_out = wav_enable_out,
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user