diff --git a/spa/include/spa/node/io.h b/spa/include/spa/node/io.h index a0433a09e..c1c725ebf 100644 --- a/spa/include/spa/node/io.h +++ b/spa/include/spa/node/io.h @@ -305,14 +305,50 @@ struct spa_io_position { struct spa_io_segment segments[SPA_IO_POSITION_MAX_SEGMENTS]; /**< segments */ }; -/** rate matching */ +/** + * Rate matching. + * + * It is usually set on the nodes that process resampled data, by + * the component (audioadapter) that handles resampling between graph + * and node rates. The \a flags and \a rate fields may be modified by the node. + * + * The node can request a correction to the resampling rate in its process(), by setting + * \ref SPA_IO_RATE_MATCH_ACTIVE on \a flags, and setting \a rate to the desired rate + * correction. Usually the rate is obtained from DLL or other adaptive mechanism that + * e.g. drives the node buffer fill level toward a specific value. + * + * When resampling to (graph->node) direction, the number of samples produced + * by the resampler varies on each cycle, as the rates are not commensurate. + * + * When resampling to (node->graph) direction, the number of samples consumed by the + * resampler varies. Node output ports in process() should produce \a size number of + * samples to match what the resampler needs to produce one graph quantum of output + * samples. + * + * Resampling filters introduce processing delay, given by \a delay and \a delay_frac, in + * samples at node rate. The delay varies on each cycle e.g. when resampling between + * noncommensurate rates. + * + * The first sample output (graph->node) or consumed (node->graph) by the resampler is + * offset by \a delay + \a delay_frac / 1e9 node samples relative to the nominal graph + * cycle start position: + * + * \code{.unparsed} + * first_resampled_sample_nsec = + * first_original_sample_nsec + * - (rate_match->delay * SPA_NSEC_PER_SEC + rate_match->delay_frac) / node_rate + * \endcode + */ struct spa_io_rate_match { - uint32_t delay; /**< extra delay in samples for resampler */ + uint32_t delay; /**< resampling delay, in samples at + * node rate */ uint32_t size; /**< requested input size for resampler */ - double rate; /**< rate for resampler */ + double rate; /**< rate for resampler (set by node) */ #define SPA_IO_RATE_MATCH_FLAG_ACTIVE (1 << 0) - uint32_t flags; /**< extra flags */ - uint32_t padding[7]; + uint32_t flags; /**< extra flags (set by node) */ + int32_t delay_frac; /**< resampling delay fractional part, + * in units of nanosamples (1/10^9 sample) at node rate */ + uint32_t padding[6]; }; /** async buffers */ diff --git a/spa/plugins/audioconvert/audioconvert.c b/spa/plugins/audioconvert/audioconvert.c index e0deefe31..056d5eafd 100644 --- a/spa/plugins/audioconvert/audioconvert.c +++ b/spa/plugins/audioconvert/audioconvert.c @@ -2162,23 +2162,30 @@ static int ensure_tmp(struct impl *this, uint32_t maxsize, uint32_t maxports) static uint32_t resample_update_rate_match(struct impl *this, bool passthrough, uint32_t size, uint32_t queued) { uint32_t delay, match_size; + int32_t delay_frac; if (passthrough) { delay = 0; + delay_frac = 0; match_size = size; } else { double rate = this->rate_scale / this->props.rate; + double fdelay; + if (this->io_rate_match && SPA_FLAG_IS_SET(this->io_rate_match->flags, SPA_IO_RATE_MATCH_FLAG_ACTIVE)) rate *= this->io_rate_match->rate; resample_update_rate(&this->resample, rate); - delay = resample_delay(&this->resample); + fdelay = resample_delay(&this->resample) + resample_phase(&this->resample); if (this->direction == SPA_DIRECTION_INPUT) { match_size = resample_in_len(&this->resample, size); } else { + fdelay *= rate * this->resample.o_rate / this->resample.i_rate; match_size = resample_out_len(&this->resample, size); - delay = resample_out_len(&this->resample, delay); } + + delay = (uint32_t)round(fdelay); + delay_frac = (int32_t)((fdelay - delay) * 1e9); } match_size -= SPA_MIN(match_size, queued); @@ -2186,6 +2193,7 @@ static uint32_t resample_update_rate_match(struct impl *this, bool passthrough, if (this->io_rate_match) { this->io_rate_match->delay = delay + queued; + this->io_rate_match->delay_frac = delay_frac; this->io_rate_match->size = match_size; } return match_size;