On Thu, 20.08.09 00:52, pl bossart (bossart.nospam at gmail.com) wrote: > > So, basically: if you configure a latency then you should get > > something in the area what you asked for but we cannot make > > guarantees. And the connection between latency and block sizes is even > > fuzzier. > > D'oh. Looks like if I interface with a timing-critical protocol I'd > need to hide this inside a module... When you need equally sized blocks of PCM data then you need to queue things up (e.g. with a pa_memblockq). A number of modules do that. Including Jason Newton's equalizer module. > >> 3. For now the source and sink are synchronous but if they are not, > >> how can I enable a sample-rate converter to correct for clock drifts? > >> I see some code for SRC in both the input and output IO threads, > >> however I don't understand how the tracking would be done. > > > > module-combine handles this already. It probably would make sense to > > copy the basic logic here: in the main thread simply measure the > > latency of the sink and source every now and then, and then update the > > sampling rate of the sink input with pa_sink_input_set_rate(). > > > > (This is actually quite hard to get right, and module-combine doesn't > > entirely get it right. The problem is getting a somewhat atomic > > snapshot of both latencies, since in the time between asking the two > > latencies another memblock might have been sent over.) > > Humm, I didn't realize your definition of latency is different from my > intuitive definition. I thought in terms of samples, but when I > checked the code in alsa-sink.c, I saw that the latency is really the > delta between the wall clock and the audio clock+the delayed samples. > What this means is that if there's a drift between the wall clock and > the audio clock the latency reported will gradually increase or be > reduced Is this correct? Yes, we don't measure the latency in samples since due to resampling, network latencies and other times summed up it really is nothing where the unit "sample" (in the client's sample rate) is appropriate. In the entire pipeline from the per-application buffer to the speakers only the per-application buffer itself has actually a latency which could be measured sensibly in samples (in the client's sample rate). Everything else is either measured in usec anyway or in a different sample rate. That's why I chose to unify time units over the entire pipeline and measure them in usec. If you need them in samples use pa_usec_to_bytes(). > I guess when you substract both latencies you get rid of the wall > clock component, which is fine in this case. > And yes we would need to low-pass filter the deviation to focus only > on the long-term evolution. The clocks shouldn't be more that 1% apart > anyway on most systems. module-combine does not low-pass filter time under the assumption that the underlying source/sink already does that for their timing and we don't need to low-pass things on every level again. Most backend modules (such as the ALSA or Bluetooth one) low-pass filter time by using pa_timer_smoother -- which however does linear regression plus spline interpolation instead of doing a classic low-pass or a PLL/FLL. > > In the rewind callback you you simply must rewind the read pointer in > > the memblockq. It is called whenever we need to rewrite the hardware > > playback buffer. i.e. let's say we have 2s of buffer. Now a new stream > > is added to the mix. We need to remix the whole 2s we already > > wrote. Then we rewind each stream and ask for the data again and write > > it to the buffer. > > > > If you use a memblockq all you need to do is basically forward this > > call to pa_memblockq_rewind() which does the heavy lifting for you. > > This part is unclear. What you are saying is that basically > pa_memblockq_rewind() is the opposite of the drop(), this is just a > play with the read pointer. However when does the data actually get > marked as used by the sink and when can these memory blocks be > reclaimed/reused? Every sink has a value max_rewind associated with it (sink->thread_info.max_rewind) which should be used by streams to size the "history" they keep in their buffers. If you use a memblockq then you should set its max_rewind parameter to this value (though converted to the stream's sample params, which pa_sink_input_get_max_rewind() does for you) The max_rewind value can change, i.e. when the underlying device is reconfigured or your stream is moved between sinks. You should register an update_max_rewind() callback in you pa_sink_input to get notifications each time that happens and then readjust the maxrewind param of your memblockq. Lennart -- Lennart Poettering Red Hat, Inc. lennart [at] poettering [dot] net http://0pointer.net/lennart/ GnuPG 0x1A015CC4