The window object is used to implement a sliding window buffer. It is essentially a first-in, first-out queue but with the constraint that a fixed number of elements is always available, and the ability to read the entire queue at once. This is particularly useful for filtering objects which use time-domain convolution of a fixed length to compute its outputs. The window objects operate on a known data type, e.g. float ( windowf ), and float complex ( windowcf ).
The buffer has a fixed number of elements which are initially zeros. Values may be pushed into the end of the buffer (into the "right" side) using the push() method, or written in blocks via write() . In both cases the oldest data samples are removed from the buffer (out of the "left" side). When it is necessary to read the contents of the buffer, the read() method returns a pointer to its contents. liquid implements this shifting method in the same manner as a ring buffer, and linearizes the data very efficiently, without performing any unnecessary data memory copies. Effectively, the window looks like:
Listed below is the full interface for the window family of objects. While each method is listed for windowcf (a window with float complex elements), the same functionality applies to the windowf object.
- windowcf_create(n) creates a new window with an internal length of \(n\) samples.
- windowcf_recreate(q,n) extends an existing window's size, similar to the standard C library's realloc() to \(n\) samples. If the size of the new window is larger than the old one, the newest values are retained at the beginning of the buffer and the oldest values are truncated. If the size of the new window is smaller than the old one, the oldest values are truncated.
- windowcf_destroy(q) destroys the object, freeing all internally-allocated memory.
- windowcf_clear(q) clears the contents of the buffer by setting all internal values to zero.
- windowcf_index(q,i,*v) retrieves the \(i^{th}\) sample in the window, storing the output value in \(v\) . This is equivalent to first invoking read() and then indexing on the resulting pointer; however the result is obtained much faster. Therefore invoking windowcf_index(q,0,*v) returns the oldest value in the window.
- windowcf_read(q,**r) reads the contents of the window by returning a pointer to the aligned internal memory array. This method guarantees that the elements are linearized. This method should only be used for reading; writing values to the buffer has unspecified results.
- windowcf_push(q,v) shifts a single sample \(v\) into the right side of the window, pushing the oldest (left-most) sample out of the end. Unlike stacks, the windowcf object has no equivalent "pop" method, as values are retained in memory until they are overwritten.
- windowcf_write(q,*v,n) writes a block of \(n\) samples in the array \(\vec{v}\) to the window. Effectively, it is equivalent to pushing each sample one at a time, but executes much faster.
Here is an example demonstrating the basic functionality of the window object. The comments show the internal state of the window after each function call as if the window were a simple C array.
#include <liquid/liquid.h>
int main() {
// initialize array for writing
float v[] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
// create window with 10 elements
windowf w = windowf_create(10);
// window[10] : {0 0 0 0 0 0 0 0 0 0}
// push 4 elements into the window
windowf_push(w, 1);
windowf_push(w, 3);
windowf_push(w, 6);
windowf_push(w, 2);
// window[10] : {0 0 0 0 0 0 1 3 6 2}
// push 4 elements at a time
windowf_write(w, v, 4);
// window[10] : {0 0 1 3 6 2 9 8 7 6}
// recreate window (truncate to last 6 elements)
w = windowf_recreate(w,6);
// window[6] : {6 2 9 8 7 6}
// recreate window (extend to 12 elements)
w = windowf_recreate(w,12);
// window[12] : {0 0 0 0 0 0 6 2 9 8 7 6}
// read buffer (return pointer to aligned memory)
float * r;
windowf_read(w, &r);
// r[12] : {0 0 0 0 0 0 6 2 9 8 7 6}
// clean up allocated object
windowf_destroy(w);
}