mirror of
https://github.com/qemu/qemu.git
synced 2024-11-24 11:23:43 +08:00
audio: make playback packet length calculation exact
Introduce the new function st_rate_frames_in() to calculate the exact number of audio input frames needed to get a given number of audio output frames. The exact number of frames depends only on the difference of opos - ipos and the number of output frames. When downsampling, this function returns the maximum number of input frames needed. This new function replaces the audio_frontend_frames_out() function, which calculated the average number of input frames rounded down to the nearest integer. Because audio_frontend_frames_out() also limited the number of input frames to the size of the resample buffer, st_rate_frames_in() is not a direct replacement and two additional MIN() functions are needed. One to prevent resample buffer overflows and one to limit the available bytes for the audio frontends. After this patch the audio packet length calculation for playback is exact. When upsampling, it's still possible that the audio frontends can't write the last audio frame. This will be fixed later. Acked-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Signed-off-by: Volker Rümelin <vr_qemu@t-online.de> Message-Id: <20230224190555.7409-9-vr_qemu@t-online.de>
This commit is contained in:
parent
1fe3cae39f
commit
1a01df3db8
@ -701,8 +701,8 @@ static void audio_pcm_sw_resample_out(SWVoiceOut *sw,
|
||||
static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t buf_len)
|
||||
{
|
||||
HWVoiceOut *hw = sw->hw;
|
||||
size_t live, dead, hw_free;
|
||||
size_t frames_in_max, total_in, total_out;
|
||||
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)) {
|
||||
@ -720,17 +720,21 @@ static size_t audio_pcm_sw_write(SWVoiceOut *sw, void *buf, size_t buf_len)
|
||||
dead = hw->mix_buf.size - live;
|
||||
hw_free = audio_pcm_hw_get_free(hw);
|
||||
hw_free = hw_free > live ? hw_free - live : 0;
|
||||
frames_in_max = ((int64_t)MIN(dead, hw_free) << 32) / sw->ratio;
|
||||
frames_in_max = MIN(frames_in_max, buf_len / sw->info.bytes_per_frame);
|
||||
if (frames_in_max) {
|
||||
sw->conv(sw->resample_buf.buffer, buf, frames_in_max);
|
||||
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);
|
||||
frames_in_max = MIN(sw_max, fe_max);
|
||||
|
||||
if (!sw->hw->pcm_ops->volume_out) {
|
||||
mixeng_volume(sw->resample_buf.buffer, frames_in_max, &sw->vol);
|
||||
}
|
||||
if (!frames_in_max) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
audio_pcm_sw_resample_out(sw, frames_in_max, MIN(dead, hw_free),
|
||||
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);
|
||||
}
|
||||
|
||||
audio_pcm_sw_resample_out(sw, frames_in_max, frames_out_max,
|
||||
&total_in, &total_out);
|
||||
|
||||
sw->total_hw_samples_mixed += total_out;
|
||||
@ -1000,18 +1004,6 @@ static size_t audio_get_avail (SWVoiceIn *sw)
|
||||
return live;
|
||||
}
|
||||
|
||||
/**
|
||||
* audio_frontend_frames_out() - returns the number of frames needed to
|
||||
* get frames_out frames after resampling
|
||||
*
|
||||
* @sw: audio playback frontend
|
||||
* @frames_out: number of frames
|
||||
*/
|
||||
static size_t audio_frontend_frames_out(SWVoiceOut *sw, size_t frames_out)
|
||||
{
|
||||
return ((int64_t)frames_out << 32) / sw->ratio;
|
||||
}
|
||||
|
||||
static size_t audio_get_free(SWVoiceOut *sw)
|
||||
{
|
||||
size_t live, dead;
|
||||
@ -1031,8 +1023,8 @@ static size_t audio_get_free(SWVoiceOut *sw)
|
||||
dead = sw->hw->mix_buf.size - live;
|
||||
|
||||
#ifdef DEBUG_OUT
|
||||
dolog("%s: get_free live %zu dead %zu frontend frames %zu\n",
|
||||
SW_NAME(sw), live, dead, audio_frontend_frames_out(sw, dead));
|
||||
dolog("%s: get_free live %zu dead %zu frontend frames %u\n",
|
||||
SW_NAME(sw), live, dead, st_rate_frames_in(sw->rate, dead));
|
||||
#endif
|
||||
|
||||
return dead;
|
||||
@ -1161,12 +1153,13 @@ static void audio_run_out (AudioState *s)
|
||||
size_t free;
|
||||
|
||||
if (hw_free > sw->total_hw_samples_mixed) {
|
||||
free = audio_frontend_frames_out(sw,
|
||||
free = st_rate_frames_in(sw->rate,
|
||||
MIN(sw_free, hw_free - sw->total_hw_samples_mixed));
|
||||
} else {
|
||||
free = 0;
|
||||
}
|
||||
if (free > 0) {
|
||||
free = MIN(free, sw->resample_buf.size);
|
||||
sw->callback.fn(sw->callback.opaque,
|
||||
free * sw->info.bytes_per_frame);
|
||||
}
|
||||
|
@ -440,6 +440,45 @@ void st_rate_stop (void *opaque)
|
||||
g_free (opaque);
|
||||
}
|
||||
|
||||
/**
|
||||
* st_rate_frames_in() - returns the number of frames needed to
|
||||
* get frames_out frames after resampling
|
||||
*
|
||||
* @opaque: pointer to struct rate
|
||||
* @frames_out: number of frames
|
||||
*
|
||||
* When downsampling, there may be more than one correct result. In this
|
||||
* case, the function returns the maximum number of input frames needed.
|
||||
*/
|
||||
uint32_t st_rate_frames_in(void *opaque, uint32_t frames_out)
|
||||
{
|
||||
struct rate *rate = opaque;
|
||||
uint64_t opos_start, opos_end;
|
||||
uint32_t ipos_start, ipos_end;
|
||||
|
||||
if (rate->opos_inc == 1ULL << 32) {
|
||||
return frames_out;
|
||||
}
|
||||
|
||||
if (frames_out) {
|
||||
opos_start = rate->opos;
|
||||
ipos_start = rate->ipos;
|
||||
} else {
|
||||
uint64_t offset;
|
||||
|
||||
/* add offset = ceil(opos_inc) to opos and ipos to avoid an underflow */
|
||||
offset = (rate->opos_inc + (1ULL << 32) - 1) & ~((1ULL << 32) - 1);
|
||||
opos_start = rate->opos + offset;
|
||||
ipos_start = rate->ipos + (offset >> 32);
|
||||
}
|
||||
/* last frame written was at opos_start - rate->opos_inc */
|
||||
opos_end = opos_start - rate->opos_inc + rate->opos_inc * frames_out;
|
||||
ipos_end = (opos_end >> 32) + 1;
|
||||
|
||||
/* last frame read was at ipos_start - 1 */
|
||||
return ipos_end + 1 > ipos_start ? ipos_end + 1 - ipos_start : 0;
|
||||
}
|
||||
|
||||
void mixeng_clear (struct st_sample *buf, int len)
|
||||
{
|
||||
memset (buf, 0, len * sizeof (struct st_sample));
|
||||
|
@ -52,6 +52,7 @@ void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf,
|
||||
void st_rate_flow_mix(void *opaque, st_sample *ibuf, st_sample *obuf,
|
||||
size_t *isamp, size_t *osamp);
|
||||
void st_rate_stop (void *opaque);
|
||||
uint32_t st_rate_frames_in(void *opaque, uint32_t frames_out);
|
||||
void mixeng_clear (struct st_sample *buf, int len);
|
||||
void mixeng_volume (struct st_sample *buf, int len, struct mixeng_volume *vol);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user