@@ -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);