Unit delay output-feedback form: feedback
Support for variable length delay lines.
Control-rate operators: hold setup
Output feedback is specified using the feedback language primitive, or a small layer of syntactic sugar using the define form.
The feedback operator takes a pure operator (function) and transforms it into a causal stream operator. This abstraction corresponds to a canonical State space representation.
Here is how to express a discrete integrator using the special define form.
#lang s-exp rai/stream (define (integrator (s) (x)) (let ((sum (+ s x))) (values sum ;; state update sum))) ;; system output
The state inputs are grouped separately from the ordinary inputs. The output of the function is the concatenation of state output and ordinary outputs.
It can be interpreted both on the stream level, where input and output streams correspond to shifted versions of each other, or at the scalar level where the next time step’s state vector and current output is computed based on the current state and input.
In music DSP applications, a frequently used form of output feedback system is the delay line. An example of such a system would be
#lang s-exp rai/stream (define (delay (s0 s1 s2 s3) (x)) (let* ((from-delay s3) (to-delay (+ x (* 1/2 from-delay)))) (values to-delay s0 s1 s2 ;; state updates to-delay))) ;; output
This is a 4-tap delay line. Notice the pattern in the state output: all state variables are shifted by one, and the head of the line is updated with a new input, in this case a mix of input and delay output feedback.
In typical applications, the size of the delay state can grow very large. It makes sense to introduce an abstraction to make specification easier, and to also allow the extra annotation to guide a more efficient implementation. The approach taken in RAI is to abstract the delay line state as an indexable array, and to limit the construction of an updated delay state to the shift operation.
#lang s-exp rai/stream (define (delay (s) (x)) (let* ((from-delay (dl-ref s 3)) ;; delay state vector indexed read (to-delay (+ x (* 1/2 from-delay)))) (values (dl-shift s to-delay) ;; delay state vector update to-delay))) ;; output
The hold and setup operators implement a limited form of subsampling, addressing very specific need in the target domain of audio DSP. In the future subsampling could be implemented in a more general way.
The subsampling factor is determined by the block rate of the system, meaning the number of audio samples processed in a single block for the VST, Pd and Jack interfaces.
The hold operator is a sample and hold operator. It will sample a value at the first time instance of a sample block and hold it for subsequent samples. The setup operator is similar, in that it will pass through its first argument at the first time instance, and its second argument at any other.
The combination of both allows the implementation of per-block computations, e.g. the computation of an expensive function evaluation running at control rate, driving a cheaper interpolation scheme running at audio rate. A good example of this is the computation of log scale control parameters in a sound synthesizer or effect, such as frequency or volume controls.