mp4-sa-> the mp4-sa book-> advanced opcodes->filter core opcodes |
Sections
|
Core Opcodes:allpass bandpass bandstop biquad chorus comb delay delay1 fir firt flange fracdelay hipass iir iirt lopass reverbOther Elements:oparray |
IntroductionIn this chapter, we describe the core opcodes for audio filters. These a-rate opcodes return a filtered version of the signal coded by the a-rate parameter in. Most opcodes in this chapter are normative, and compute filters that sound identical on any compliant decoder. A few opcodes are non-normative, and are provided as convenient tools for algorithm prototyping. We also introduce a SAOL language feature, the oparray, in conjunction with the fracdelay core opcode. Oparrays let several syntactically distinct calls to the same opcode share a single set of internal state variables. |
|
FIR FiltersTwo finite impulse response (FIR) filter core opcodes, fir and firt, are provided in SAOL. The right panel shows the header syntax for the opcodes. Both opcodes return a filtered version of the signal provided by the a-rate parameter in. During the first call to these opcodes, the internal filter state variables are initialized to zero. Both opcodes are normative, in the sense that the filter coefficients parameters define the transfer function of the filter, assuming one opcode call per a-pass. The method for implementing this transfer function is non-normative. The fir opcode specifies the FIR filter coefficients as the list b0, b1, b2 ... of scalar k-rate parameters. The firt opcode specifies the FIR filter coefficients as the table parameter t, whose length is the order of the filter. If the optional k-rate parameter order is less than the table length, this parameter determines the filter order, as specified on the right panel. |
FIR: Parameter Coefficientsaopcode fir(asig in, ksig b0 [, ksig b1, ksig b2 ...]) Implements transfer function -1 -2 H(z) = b0 + b1 z + b2 z ... Exact implementation is decoder dependent. FIR: Wavetable Coefficientsaopcode firt(asig in, table t [, ksig order]) FIR coefficients are stored in the table t. If order is not supplied, let order be the length of table t. The opcode implements the transfer function: -1 -2 H(z) = t[0] + t[2]*z + t[2]*z -(order-1) ... t[order-1]*z Exact implementation is decoder dependent. |
IIR FiltersThe iir and iirt core opcodes implement Infinite Impulse Response (IIR) filters. See the right panel for header syntax for these opcodes. Both opcodes return a filtered version of the signal provided by the a-rate parameter in. During the first call to these opcodes, the internal filter state variables are initialized to zero. The iir opcode specifies the transfer function coefficients as a list of k-rate scalar parameters b0, b1, b2 ... for numerator coefficients, and an interleaved list of k-rate scalar parameters a1, a2 ... for denominator coefficients. The iirt opcode specifies the transfer function coefficients as two wavetables, table b for numerator coefficients and table a for denominator coefficients. These opcodes are normative in the sense that the coefficients supplied as parameters define the filter transfer function, assuming one opcode call per a-pass. The actual method for implementing the transfer function is non-normative. Normative IIR ImplementationFor some IIR transfer functions, control over the filter implementation is crucial to assuring filter stability. The core opcode biquad provides an exact implementation of a second-order filter structure, using the Transposed Direct Form II structure. The right panel shows the header syntax and exact implementation of the biquad core opcode. Normative implementations of higher-order filter structures may be created by composing several biquad opcode calls in a series or parallel manner. |
IIR: Parameter Coefficientsaopcode iir(asig in, ksig b0 [, ksig a1, ksig b1, ksig a2, ksig b2, ...]) Implements transfer function -1 -2 b0 + b1 z + b2 z ... H(z) = -------------------------- -1 -2 1 + a1 z + a2 z ... Exact implementation of the transfer function as a filter is decoder dependent. IIR: Wavetable Coefficientsaopcode iirt(asig in, table a, table b [, ksig order]) Implements transfer function: -1 -2 b[0] + b[1]*z + b[2]*z ... H(z) = -------------------------- -1 -2 1 + a[1]*z + a[2]*z ... Up to coefficients b[order-1] and a[order-1]. If optional parameter order is not supplied, the length of tables a or b determine the order of the numerator and denominator respectively. Exact implementation of the transfer function as a filter is decoder dependent. Transposed Direct Form IIaopcode biquad(asig in, ivar b0, ivar b1, ivar b2, ivar a1, ivar a2) At the first call, initializes internal storage variables d1 and d2 to zero. At each call, computes these equations in order, and returns "ret". ret = d2 + b0*in d2 = d1 - a1*ret + b1*in d1 = - a2*ret + b2*in |
Parametric FiltersThe IIR and FIR core opcodes described in the previous sections may be used to create normative implementations of the classic filter shapes (lowpass, highpass, bandpass, and bandstop) with dynamic transfer functions. However, these opcodes require the programmer to specify the coefficient values for the filters. The non-normative filter core opcodes lopass, hipass, bandpass and bandstop let the programmer specify filter frequency response at a higher level of abstraction. These opcodes return a filtered version of the signal provided by the a-rate parameter in. The right panel shows the header syntax for these opcodes. The lopass and hipass opcodes have a single k-rate control parameter cut that sets the 6 dB cutoff point for the filter. The bandpass and bandstop opcodes have two k-rate control parameters, a cf parameter that sets the center frequency for the passband or stopband, and a bw parameter that sets the bandwidth of the passband or stopband, measured at the 6 dB cutoff points The control parameter definitions assume the opcodes are called once per a-pass. Note that since the opcodes are non-normative, specifications such as the cutoff slope, the passband ripple, and the stopband ripple are decoder-dependent. |
Basic Filter Blocksaopcode lopass(asig in, ksig cut) aopcode hipass(asig in, ksig cut) cut: -6 dB cutoff point of the filter, specified in Hz. aopcode bandpass(asig in, ksig cf, ksig bw) aopcode bandstop(asig in, ksig cf, ksig bw) cf: center frequency of the passband or stopband, in Hz. bw: bandwidth of the passband or stopband, measured as the distance between the -6dB cutoff point below and above the center frequency. |
Integral DelaysThe core opcodes delay1 and delay delay the signal parameter in for a fixed number of opcode calls. These opcodes are useful building blocks for filter design. See the right panel for the header syntax and exact semantics for these opcodes. The delay1 opcode delays a signal for one opcode call. If the opcode is called once per a-pass, it corresponds to a delay of a sample period. The first call to delay1 returns zero. The delay opcode implements a delay line as a shift register structure. The parameter t (units of seconds) sets the time delay of the line, assuming the opcode is called once per a-pass. The delay line state variables are created during the first call to the opcode, and are initialized to zero. During each call to delay, the delay line is shifted forward one position. The new value of the parameter in is inserted into the front of the delay line, and the value that falls off the end of the delay line is returned by the opcode. |
delay1aopcode delay1(asig in) ------- | -1 | in -----| z |----- y | | ------- On first call, initialize delay line value to zero. On each call, return y, and insert parameter in into the unit delay. delayaopcode delay(asig in, ivar t) ------------------ | floor(t*srate) | in --| delay |--- y | units | ------------------ On first call, initialize delay line shift register values to zero. On each call, shift in new value of in, and return value of y that falls out the end of the line. |
Fractional DelaysThe filter and delay opcodes presented in the previous sections share a common calling semantics.
These semantics are incompatible with the function of the core opcode fracdelay (see the right panel for header syntax). This opcode creates a delay line structure, and lets the programmer insert and sum values into arbitrary taps along the line. It also lets the programmer read out the delay line value at a fractional position along the line, by interpolating between delay line positions using the interpolation method specified by the global parameter interp. To provide this functionality, the fracdelay opcode includes a method parameter, that specifies the operation to be performed on the delay line. These operations include creating and initializing the delay line internal state, and shifting the contents of the delay line one cycle forward. The right panel describes each method in detail. Note that unlike previous filter opcodes, the fracdelay opcode does not automatically shift its delay line with each call. Supporting this type of semantics requires SAOL language support, since each syntactically distinct opcode call has its own set of internal variables. For proper operation, multiple calls to fracdelay need to modify the same set of internal variables (i.e. the delay line). The SAOL atomic expression element oparray is designed for this application. Oparray DeclarationsThe right panel shows a typical oparray declaration, as part of the stereo delay line example. An oparray declaration creates the internal state variables for a fixed number of calls to an opcode. Each set of internal state variables is called a context. An oparray declaration shares the syntax of a signal array declaration, substituting the name of an opcode for the name of the variable. The declaration includes a state specifier that sets the number of contexts created. The state specifier may be an integer greater than zero. Alternatively, the state specifier may be keywords inchannels or outchannels, setting the number of contexts equal to the width of the input audio port or output audio port of the instrument, respectively. An oparray declaration may not occur in the global block. Only one oparray may be declared per opcode type per instrument. Oparray CallsOparray calls are opcode calls that use one of the contexts of an oparray declaration. Syntactically, oparray calls add an bracketed index element before the parameter list, that select the context for the call. The right panel shows example oparray calls as part of the stereo delay line example. Oparray calls have the rate of the referenced opcode. The rate of the index expression may not be faster than the rate of the opcode. |
fracdelayaopcode fracdelay(ksig method [, asig p, asig in]) Fracdelay implements multi-tap delay lines. It is meant to be used with the oparray construct, so that several oparray fracdelay calls may access the same set of internal variables. Fracdelay has different behaviors, based on the value of the method parameter, which may take on integral values between 1 and 5. Initialize (method == 1): This method creates internal variables for a delay-line that is p seconds long, and sets all delay-line values to zero. The number of locations in the delay line is floor(p*s_rate). Returns 0. Tap (method == 2): This method returns the current value in the delay line at position p seconds. If necessary, interpolation is done between delay-line taps, using the interpolation method set by the global parameter interp. Set (method == 3): This method sets the delay-line tap at position floor(p*s_rate) to the value of parameter in. Returns 0. Sum (method == 4): This method adds the value of parameter in to the delay-line tap at position floor(p*s_rate). Returns new delay-line tap value. Shift (method == 5): This method shifts the delay line forward one sample, shifting 0 into the beginning of the delay line. Returns the value shifted off the end. Slib defines the constants FRAC_INIT, FRAC_TAP, FRAC_SET, FRAC_SUM, and FRAC_SHIFT to use as the method parameter in fracdelay calls. Oparray Example// a stereo delay line instr delayline(dtime) { asig first, outval[2]; oparray fracdelay[2]; // on first pass, create // space for delay lines if (!first) { first = 1; fracdelay[0](1,dtime); fracdelay[1](1,dtime); } // do shift, collect output outval[0] = fracdelay[0](5); outval[1] = fracdelay[1](5); // insert into front of line fracdelay[0](3,0,input[0]); fracdelay[1](3,0,input[1]); // return result output(outval); } |
allpass and combThe core opcodes allpass and comb are normative implementations of recirculating filter structures. These filters are useful building blocks for reverberation models, and for certain sound effects. These opcodes are also useful for creating waveguide structures for physical instrument models. The right panel shows the header syntax and exact implementation for allpass and comb. Both opcodes use a shift register delay line structure to hold filter state. The parameter t (units of seconds) sets the time delay of the line, assuming the opcode is called once per a-pass. On the first call to allpass and comb, the delay line is created, and all values are initialized to zero. On each call to these opcodes, the delay line is shifted one sample forward, and the return value and delay line insertion value is computed as described on the right panel. Note that the gain parameter for allpass and comb is i-rate. To create a comb or allpass filter structure with a temporally modulated gain, use the delay opcode as a starting point for writing the filter structure directly in SAOL. |
allpassaopcode allpass(asig in, ivar t, ivar gain) ------------------ | floor(t*srate) | x-- | delay |--y | units | ------------------ On first call, initialize delay line shift register to zero. On each call, return out = y - gain*in and insert x = out*gain + in into delay line. combaopcode comb(asig in, ivar t, ivar gain) ------------------ | floor(t*srate) | x-- | delay |--y | units | ------------------ On first call, initialize delay line shift register to zero. On each call, return y, and insert x = in + gain*y into delay line. |
reverb, chorus and flangeThe allpass and comb core opcodes are useful as normative building blocks for modeling room reverbation. The allpass and comb opcodes are also useful for simulating the sound effect created when several detuned versions of the same sustained sound are played simultaneously (called chorusing) and for simulating the flanging effect created by mixing a sound with a delayed version of itself. An alternative normative approach to reverb and effects processing is to use the the fracdelay and delay opcodes as building blocks for recirculating filter designs. The non-normative opcodes flange, chorus, and reverb provide convenient access to delay-based effects at a high level of abstraction for prototyping purposes. The right panel shows the header syntax for these opcodes. These opcodes return the effected version of the signal provided by the a-rate parameter in. The opcode return value should be added to in by the programmer to create the complete effect. These opcodes are designed to be called once per a-pass. Like all core opcodes, flange, chorus, and reverb return a scalar signal, producing a monophonic version of the effect. chorus and flangeChorus and flange effects are delay lines with smoothly varying delays. Chorus effects occur in the 20-40 millisecond range, and flange effects occur in the 1-10 millisecond range. The flange and chorus opcode do not let the user specify the absolute delay used in the effect, and as a result these opcodes are non-normative. However, the rate and depth parameters specify the delay variation for the flange and chorus opcodes. The k-rate depth parameter sets the excursion from the mean time delay, as a percentage from 0 to 100 percent. The k-rate rate parameter sets the frequency, in Hertz, of the low-frequency oscillator (LFO) that modulates the delay time about the mean. For example, a flanger with a 2 ms mean delay, a 50 percent depth, and a sinusoidal LFO with a 5 Hz rate has a time delay that modulates between 1 ms and 3 ms sinusoidally, 5 times a second. Note that the LFO waveform shape for the flange and chorus opcodes is non-normative. reverbThe parameters for the reverb core opcode correspond to sonic characteristics of the reverberation sound. The reverberation algorithm is not specified by the standard. If the reverb opcode is called with a single i-rate control parameter f0, it is taken as the RT60 of the full-bandwidth reverberation signal. An RT60 value is the time it takes for an impulse input signal to fall in amplitude by 60dB. Alternatively, the reverb opcode may be called with an arbitrary number of pairs of i-rate control parameters (f0, r0), (f1, r1) .... In this case, each r value sets the RT60 time, in seconds, at a particular signal frequency f, in Hz. Next section: Part IV/3: Signal Processing Core Opcodes |
chorus and flangeaopcode chorus(asig in, ksig rate, ksig depth) aopcode flange(asig in, ksig rate, ksig depth) rate: the rate of modulation of the time delay, in Hz. depth: the depth of the modulation, in percent. For example, a flanger effect typically uses a 15 ms delay. A depth of 50 percent implies the delay time varies from 7.5ms to 22.5ms, at a rate determined by the rate parameter. note the waveform is non-normative, as is the absolute delay. note that depth is specified as a number between 0 and 100, not as a number between 0.0 and 1.0. reverbaopcode reverb(asig in, ivar f0 [, ivar r0, ivar f1, ivar r0 ...]) with no optional parameters: f0: sets the decay time, in seconds, for an input impulse to fall in amplitude 60dB. known as the RT60. with optional parameters: fk, rk: rk is the RT60 of the reverb (in seconds) at the frequency fk (in Hertz). last parameter must be an r, not an f. |
mp4-sa-> the mp4-sa book-> advanced opcodes->filter core opcodes |