On Tue, 2013-03-26 at 19:34 -0700, Justin Chudgar wrote: > https://github.com/justinzane/pulseaudio/blob/master/src/modules/module-lfe-lp.c > > First, thanks to both of you for helping me get this far. I've got a mostly > correct (I think) filter setup based on Alexander's feedback. Now I'm trying to > get rewind buffering done. I've read the wiki page that Tanu sent and I've been > talking to ohsix on IRC. > > All of which has left me more educated but just as clueless about what to > actually implement.... :( > > My understanding is that since I've got a causal filter, I need to do one of > the two things when a rewind request appears: > 1 - buffer the biquad samples so that I can restart filtering the rewound input > memblock_q immediately > - or - > 2 - buffer the input samples for at least sample_rate/20 and when a rewind come > in, set the biquad samples to 0.0 and filter the buffered input. > > Is this even remotely correct? I think option 1 is better. Rewinding means that you jump back in time. Let T be the point in time to which you jump. Option 1 ensures that you restore exactly the same state that you had when you reached T the first time. I don't know where that sample_rate/20 comes from, but I think I understand what you mean in option 2 anyway. You'd save a bit more than max_rewind amount of input samples in a history buffer. When a rewind happens, you'd jump back in time a bit more than what was requested. Let D be the difference between the requested rewind size and how much you actually jumped back. You'd then set the biquad samples to zero and process D amount of the history data, which should result in roughly the same state as what option 1 would have achieved. The keyword is "roughly", so there may be some discontinuity in the signal. It's not exactly the same, because the filter has infinite impulse response, that is, the very first input sample in the stream can affect all future output samples, so for this approach to work perfectly, you'd have to save all input you ever process and reprocess it all again on each rewind. I have no idea how close the resulting state would be to the optimal with this limited input history approach, maybe it would be virtually perfect. But to avoid this uncertainty, I prefer option 1. > Also, is it possible? likely? for a rewind to occur during the processing of a > sink_input_pop_cb? Since this is where the filtering occurs, how does one > manage locking of the shared userdata? sink_input_pop_cb() and sink_input_process_rewind_cb() are both called from the "IO thread" of the master sink, so they can't be called concurrently. There's no need for locking. > The more focused on my little bit of code, the better, and thanks again. In sink_input_pop_cb(), I think you should push to the biquad history buffer the amount that you return to the caller in the "chunk" function argument, that is, the amount that you processed. If the history buffer, as a result, contains more data than max_rewind, you should drop the excessive history data. sink_input_process_rewind_cb() is a bit more tricky, I have some trouble wrapping my head around what is happening and what should be done... nbytes is the amount of our old output that has been discarded and which we should therefore regenerate. It sounds logical that this would be the amount that we should jump back in the biquad sample history. But the comment about resetting the filter is in a branch that is only executed when we seek in u->memblockq, which would suggest that we should care about the amount variable instead of (or in addition to) the nbytes variable. If u->memblockq contains data, it is unprocessed input, so I don't think we should care about the amount of seeking we do in that buffer. Unprocessed input hasn't yet had effect on our filter state. It seems to me that the right thing to do is to care only about nbytes and jump back in the biquad history buffer by that amount (and restore the filter state accordingly). -- Tanu