Message ID | 20230206185237.8358-15-vr_qemu@t-online.de (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | audio: improve callback interface for audio frontends | expand |
On Mon, Feb 6, 2023 at 10:53 PM Volker Rümelin <vr_qemu@t-online.de> wrote: > > Upsampling may leave one remaining audio frame in the input > buffer. The emulated audio playback devices are currently > resposible to write this audio frame again in the next write > cycle. Push that task down to audio_pcm_sw_write. > > This is another step towards an audio callback interface that > guarantees that when audio frontends are told they can write > n audio frames, they can actually do so. > > Acked-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> > Signed-off-by: Volker Rümelin <vr_qemu@t-online.de> Acked-by: Marc-André Lureau <marcandre.lureau@redhat.com> > --- > audio/audio.c | 34 ++++++++++++++++++++++++++++------ > audio/audio_template.h | 6 ++++++ > 2 files changed, 34 insertions(+), 6 deletions(-) > > diff --git a/audio/audio.c b/audio/audio.c > index dad17e59b8..4836ab8ca8 100644 > --- a/audio/audio.c > +++ b/audio/audio.c > @@ -731,16 +731,21 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t buf_len) > 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.size); > + 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; > } > > - sw->conv(sw->resample_buf.buffer, buf, frames_in_max); > - if (!sw->hw->pcm_ops->volume_out) { > - mixeng_volume(sw->resample_buf.buffer, frames_in_max, &sw->vol); > + 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, > @@ -749,6 +754,22 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t buf_len) > 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", > @@ -1155,8 +1176,9 @@ static void audio_run_out (AudioState *s) > } else { > free = 0; > } > - if (free > 0) { > - free = MIN(free, sw->resample_buf.size); > + if (free > sw->resample_buf.pos) { > + free = MIN(free, sw->resample_buf.size) > + - sw->resample_buf.pos; > sw->callback.fn(sw->callback.opaque, > free * sw->info.bytes_per_frame); > } > diff --git a/audio/audio_template.h b/audio/audio_template.h > index a0b653f52c..0d8aab6fad 100644 > --- a/audio/audio_template.h > +++ b/audio/audio_template.h > @@ -138,6 +138,12 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw) > return -1; > } > > + /* > + * Allocate one additional audio frame that is needed for upsampling > + * if the resample buffer size is small. For large buffer sizes take > + * care of overflows. > + */ > + samples = samples < INT_MAX ? samples + 1 : INT_MAX; > sw->resample_buf.buffer = g_new0(st_sample, samples); > sw->resample_buf.size = samples; > sw->resample_buf.pos = 0; > -- > 2.35.3 > -- Marc-André Lureau
diff --git a/audio/audio.c b/audio/audio.c index dad17e59b8..4836ab8ca8 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -731,16 +731,21 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t buf_len) 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.size); + 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; } - sw->conv(sw->resample_buf.buffer, buf, frames_in_max); - if (!sw->hw->pcm_ops->volume_out) { - mixeng_volume(sw->resample_buf.buffer, frames_in_max, &sw->vol); + 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, @@ -749,6 +754,22 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t buf_len) 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", @@ -1155,8 +1176,9 @@ static void audio_run_out (AudioState *s) } else { free = 0; } - if (free > 0) { - free = MIN(free, sw->resample_buf.size); + if (free > sw->resample_buf.pos) { + free = MIN(free, sw->resample_buf.size) + - sw->resample_buf.pos; sw->callback.fn(sw->callback.opaque, free * sw->info.bytes_per_frame); } diff --git a/audio/audio_template.h b/audio/audio_template.h index a0b653f52c..0d8aab6fad 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -138,6 +138,12 @@ static int glue (audio_pcm_sw_alloc_resources_, TYPE) (SW *sw) return -1; } + /* + * Allocate one additional audio frame that is needed for upsampling + * if the resample buffer size is small. For large buffer sizes take + * care of overflows. + */ + samples = samples < INT_MAX ? samples + 1 : INT_MAX; sw->resample_buf.buffer = g_new0(st_sample, samples); sw->resample_buf.size = samples; sw->resample_buf.pos = 0;