You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1107 lines
243 KiB
1107 lines
243 KiB
11 years ago
|
{
|
||
|
"metadata": {
|
||
|
"name": ""
|
||
|
},
|
||
|
"nbformat": 3,
|
||
|
"nbformat_minor": 0,
|
||
|
"worksheets": [
|
||
|
{
|
||
|
"cells": [
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 1,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Zero delay the easy way"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Raph Levien \n",
|
||
|
"25 Jan 2014\n",
|
||
|
"\n",
|
||
|
"So-called \"zero delay\" filters have become popular in synthesizer and audio applications, due to their\n",
|
||
|
"accurate emulation of analog filter designs and efficient implementation. The techniques used\n",
|
||
|
"to create zero delay filters are considered standard in the circuit emulation field, but even so the\n",
|
||
|
"idea of creating a zero delay filter from scratch can be intimidating to those just wanting to design\n",
|
||
|
"a decent audio filter.\n",
|
||
|
"\n",
|
||
|
"This notebook presents a very easy design methodology, using cookbook tools. There are references to\n",
|
||
|
"the literature for those wanting to dig deeper into the theory. None of these techniques are new, but\n",
|
||
|
"it is my hope that presenting them in this form will make them more accessible.\n",
|
||
|
"\n",
|
||
|
"In broad outline, the approach recommended here is:\n",
|
||
|
"\n",
|
||
|
"* Choose an analog filter \"prototype\".\n",
|
||
|
"\n",
|
||
|
"* Write down the filter response in state space representation.\n",
|
||
|
"\n",
|
||
|
"* Choose a discretization method. Bilinear transform is most popular.\n",
|
||
|
"\n",
|
||
|
"* Discretize (convert from continuous time to discrete time).\n",
|
||
|
"\n",
|
||
|
"* Implement the filter in discrete state space representation.\n",
|
||
|
"\n",
|
||
|
"The resulting filter will be a reasonably (but not perfectly) accurate emulation of the analog filter.\n",
|
||
|
"It will share the same stability properties as the prototype, as well as similar behavior under\n",
|
||
|
"modulation.\n",
|
||
|
"\n",
|
||
|
"It is presented here as an IPython notebook, so you can easily read it as a static document, but you\n",
|
||
|
"can also load it into your IPython environment, play with the tools, and adapt the code. Have fun!"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"%pylab inline\n",
|
||
|
"from scipy import signal\n",
|
||
|
"from scipy import linalg"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"output_type": "stream",
|
||
|
"stream": "stdout",
|
||
|
"text": [
|
||
|
"Populating the interactive namespace from numpy and matplotlib\n"
|
||
|
]
|
||
|
}
|
||
|
],
|
||
|
"prompt_number": 1
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 2,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Choose a prototype filter"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"I'll assume you already have a good idea which analog filter you want to simulate. We're going to concentrate on the _linear_\n",
|
||
|
"response, but one of the great advantages of the state space approach is that it can be extended to nonlinearities without\n",
|
||
|
"too much trouble.\n",
|
||
|
"\n",
|
||
|
"One of the most important factors is the _order_ of the filter, which corresponds directly to the dimensionality of the state\n",
|
||
|
"vector. This technique can handle arbitraty order filters, but direct state space evaluation is definitely most efficient for\n",
|
||
|
"smaller orders (2 or 4 especially). If you have a very high order filter, consider decomposing it into a series of smaller\n",
|
||
|
"filters. There are tools for doing this in transfer function space; it's particularly easy if you know where your poles and\n",
|
||
|
"zeros are. (Search for tf2sos for more information)\n",
|
||
|
"\n",
|
||
|
"We'll use three filter designs as running examples, all of which are popular as filters in analog synthesizers: a simple\n",
|
||
|
"one-pole lowpass (also known as the \"RC lowpass\"), the classic two-pole \"state variable filter\", and the 4-pole Moog resonant lowpass\n",
|
||
|
"filter."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 3,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"One-pole lowpass"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"This is also known as the RC filter, as it can be trivially implemented with one resistor and one capacitor (generally in audio\n",
|
||
|
"applications you'd want an opamp to buffer the output so it's available at low impedance, but that's not central to the\n",
|
||
|
"response of the filter).\n",
|
||
|
"\n",
|
||
|
"Being only one pole, this filter is super easy to analyze. Its impulse response is just exponential decay.\n",
|
||
|
"\n",
|
||
|
"(TODO: images for circuit diagrams for these filters)"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 3,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"State variable filter"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"See Figure 3 in https://ccrma.stanford.edu/~jos/svf/svf.pdf for a circuit diagram. This is given in highly idealized form,\n",
|
||
|
"with integrators drawn as integrators rather than expanded out as opamps. However, this is the same as the classic state\n",
|
||
|
"variable filter, for example, the one presented in Don Lancaster's Active Filter Cookbook, which has been the basis of many\n",
|
||
|
"synth builds.\n"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 3,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Moog 4-pole ladder filter"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"The Moog 4-pole filter is of course one of the classic designs. There's quite a bit of literature on it\n",
|
||
|
"(see http://www.timstinchcombe.co.uk/index.php?pge=papers).\n",
|
||
|
"\n",
|
||
|
"A good simple description of the filter is given in https://ccrma.stanford.edu/~stilti/papers/moogvcf.pdf (Analyzing the\n",
|
||
|
"Moog VCF with Considerations for Digital Implementation, by Tom Stilson and Julious Smith). This paper presents a very\n",
|
||
|
"simple analog model of the linear response: it is simply 4 one pole lowpass filters in series, all tuned to the same corner\n",
|
||
|
"frequency, with a feedback path which controls resonance. Ignore the discretization in this paper, however; we'll do much\n",
|
||
|
"better."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 2,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"State space representation"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Once you've chosen your filter, get it into state space representation. This will be four matrices $(A, B, C, D)$ that together\n",
|
||
|
"represent the differential equations of the filter. The general equations are:\n",
|
||
|
"\n",
|
||
|
"$$ \\begin{align}\n",
|
||
|
"\\mathbf{\\dot{x}}(t) = & A\\mathbf{x}(t) + B\\mathbf{u}(t) \\\\\n",
|
||
|
"\\mathbf{y}(t) = & C\\mathbf{x}(t) + D\\mathbf{u}(t)\n",
|
||
|
"\\end{align} $$\n",
|
||
|
"\n",
|
||
|
"Here, $\\mathbf{u}(t)$ represents the input, $\\mathbf{x}(t)$ represents the state, and $\\mathbf{y}(t)$\n",
|
||
|
"represents the output. Such systems are known as \"single input\" or \"multiple input\" depending on the\n",
|
||
|
"size of $\\mathbf{u}$, and \"single output\" or \"multiple output\" depending on the size of $\\mathbf{y}$.\n",
|
||
|
"In this presentation, we'll concentrate on the \"single input, single output\" case."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 3,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"One-pole lowpass"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Because it's just one pole, the state can be a scalar rather than a vector, so we don't even need to write this in matrix form.\n",
|
||
|
"The differential equation is:\n",
|
||
|
"\n",
|
||
|
"$ \\dot{x}(t) = -\\alpha x(t) + u(t) $\n",
|
||
|
"\n",
|
||
|
"Variables are as in standard state variable representation. Lowpass is $y(t) = x(t)$, and highpass is $y(t) = u(t) - x(t)$.\n",
|
||
|
"\n",
|
||
|
"Here, $\\alpha$ controls the corner frequency. It's easiest just to normalize it to 1. The state space reprentation could hardly\n",
|
||
|
"be simpler:\n",
|
||
|
"\n",
|
||
|
"$$ \\begin{align}\n",
|
||
|
"A & = -1 \\\\\n",
|
||
|
"B & = 1 \\\\\n",
|
||
|
"C & = 1 \\\\\n",
|
||
|
"D & = 0\n",
|
||
|
"\\end{align} $$\n",
|
||
|
"\n",
|
||
|
"For highpass, $C = -1$ and $D = 1$ instead."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"def rc_lp_system():\n",
|
||
|
" return array([[-1]]), array([1]), array([1]), 0\n",
|
||
|
"\n",
|
||
|
"def rc_hp_system():\n",
|
||
|
" return array([[-1]]), array([1]), array([-1]), 1\n"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"prompt_number": 2
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 3,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"State variable filter"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"The jos paper gives the state variable representation (here we switch the order of the state variables so they\n",
|
||
|
"respresent left-to-right flow, and to emphasize the similarity to the other filters).\n",
|
||
|
"\n",
|
||
|
"$$ \\begin{align}\n",
|
||
|
"A & = \\left[\\begin{array}{cc} -k & -1 \\\\ 1 & 0 \\\\ \\end{array}\\right] \\\\\n",
|
||
|
"B & = \\left[\\begin{array}{c} 1 \\\\ 0 \\\\ \\end{array}\\right] \\\\\n",
|
||
|
"C & = [ \\begin{array}{cc} 0 & 1 \\end{array} ] \\\\\n",
|
||
|
"D & = 0\n",
|
||
|
"\\end{align} $$\n",
|
||
|
"\n",
|
||
|
"Here, $k$ controls resonance. It is equal to $1/Q$, and ranges from 2 for no resonance to 0 for undamped resonance,\n",
|
||
|
"i.e. self-oscillation. For a resonance control that varies from 0 to 1, simply set $k = 2 - 2 \\cdot \\mathit{resonance}$.\n",
|
||
|
"\n",
|
||
|
"For bandpass, $C = [ 1 \\: 0 ]$ instead, and for highpass,\n",
|
||
|
"$C = [ {-k} \\: {-1} ]$ and $D=1$. In fact, all \"Audio EQ Cookbook\" responses can easily be done with a state variable\n",
|
||
|
"filter, making it an appealing alternative to canonical form biquads. See [Second order sections in matrix form](Second%20order%20sections%20in%20matrix%20form.ipynb) notebook for all of these recipes. Also see [Solving the continuous SVF equations using trapezoidal \n",
|
||
|
"integration and equivalent currents](http://www.cytomic.com/files/dsp/SvfLinearTrapOptimised2.pdf) for a different\n",
|
||
|
"derivation."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"def svf_lp_system(res):\n",
|
||
|
" k = 2 - 2 * res\n",
|
||
|
" A = array([[-k, -1], [1, 0]])\n",
|
||
|
" B = array([1, 0])\n",
|
||
|
" C = array([0, 1])\n",
|
||
|
" D = 0\n",
|
||
|
" return A, B, C, D\n",
|
||
|
"\n",
|
||
|
"def svf_hp_system(res):\n",
|
||
|
" k = 2 - 2 * res\n",
|
||
|
" A = array([[-k, -1], [1, 0]])\n",
|
||
|
" B = array([1, 0])\n",
|
||
|
" C = array([-k, -1])\n",
|
||
|
" D = 1\n",
|
||
|
" return A, B, C, D"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"prompt_number": 3
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 3,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Moog filter"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"The Moog filter also has a simple state-space representation. It is easy to build the total system by stitching\n",
|
||
|
"together four one-pole lowpass filters and adding a feedback path.\n",
|
||
|
"\n",
|
||
|
"$$ \\begin{align}\n",
|
||
|
"A & = \\left[\\begin{array}{cc} -1 & 0 & 0 & -k \\\\\n",
|
||
|
"1 & -1 & 0 & 0 \\\\\n",
|
||
|
"0 & 1 & -1 & 0 \\\\\n",
|
||
|
"0 & 0 & 1 & -1 \\\\\n",
|
||
|
"\\end{array}\\right] \\\\\n",
|
||
|
"B & = \\left[\\begin{array}{c} 1 \\\\ 0 \\\\ 0 \\\\ 0 \\\\ \\end{array}\\right] \\\\\n",
|
||
|
"C & = [ \\begin{array}{cc} 0 & 0 & 0 & 1 \\end{array} ] \\\\\n",
|
||
|
"D & = 0\n",
|
||
|
"\\end{align} $$\n",
|
||
|
"\n",
|
||
|
"Here, k varies from 0 (no resonance) to 4 (self oscillation)."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"def moog_system(res):\n",
|
||
|
" k = 4 * res\n",
|
||
|
" A = array([[-1, 0, 0, -k], [1, -1, 0, 0], [0, 1, -1, 0], [0, 0, 1, -1]])\n",
|
||
|
" B = array([1, 0, 0, 0])\n",
|
||
|
" C = array([0, 0, 0, 1])\n",
|
||
|
" D = 0\n",
|
||
|
" return A, B, C, D"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"prompt_number": 4
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 2,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Response of continuous time filter in state space representation"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"It's possible to accurately compute the frequency and phase response of a state space filter, without for\n",
|
||
|
"example having to expand out an impulse response and compute a fourier transform.\n",
|
||
|
"\n",
|
||
|
"The transfer function of a filter in (continuous) state space formulation is simple:\n",
|
||
|
"\n",
|
||
|
"$$ H(s) = D + C(sI-A)^{-1}B $$\n",
|
||
|
"\n",
|
||
|
"The Wikipedia article on State space representation isn't a bad source for this: http://en.wikipedia.org/wiki/State_space_representation\n",
|
||
|
"\n",
|
||
|
"We can use it to plot out the frequency response of some of the filters we defined above. (Note: if you're interested in\n",
|
||
|
"phase response, just plot the angle(response) instead of abs(response))."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"def transfer(sys, s):\n",
|
||
|
" A, B, C, D = sys\n",
|
||
|
" return D + dot(C, dot(inv(s * identity(len(A)) - A), B))"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"prompt_number": 5
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"xs = logspace(-3, 0, num=200)\n",
|
||
|
"def db(x):\n",
|
||
|
" return 20 * log10(abs(x))\n",
|
||
|
"def freqz_ss_continuous(sys, f):\n",
|
||
|
" return array([transfer(sys, 1j * x / f) for x in xs])"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"prompt_number": 6
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"The one-pole lowpass and highpass filters have the expected response, with both at -3dB at the corner frequency,\n",
|
||
|
"and 6 dB/octave rolloff."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"f = sqrt(1e-3)\n",
|
||
|
"semilogx(xs, db(freqz_ss_continuous(rc_lp_system(), f)))\n",
|
||
|
"semilogx(xs, db(freqz_ss_continuous(rc_hp_system(), f)))\n",
|
||
|
"show()"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data",
|
||
|
"png": "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\nC0aO1ANeKT5acE2cXs7pHFpN
|
||
|
"text": [
|
||
|
"<matplotlib.figure.Figure at 0x108a38bd0>"
|
||
|
]
|
||
|
}
|
||
|
],
|
||
|
"prompt_number": 7
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Now we plot the response of the Moog filter model, and also see the expected response."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"for i in range(5):\n",
|
||
|
" sys = moog_system(0.2 * i)\n",
|
||
|
" semilogx(xs, db(freqz_ss_continuous(sys, f)))"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data",
|
||
|
"png": "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\nsroCX5iu6vzqXFZl11VYeLEr
|
||
|
"text": [
|
||
|
"<matplotlib.figure.Figure at 0x108a95b10>"
|
||
|
]
|
||
|
}
|
||
|
],
|
||
|
"prompt_number": 8
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 2,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Choose a discretization"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"By far, the most popular discretization method in audio applications is the bilinear transform. A second, valid choice is\n",
|
||
|
"step invariance. These two are the most popular discretization methods, though others exist, as they both preserve the\n",
|
||
|
"stability properties of the original filter.\n",
|
||
|
"\n",
|
||
|
"A very simple discretization is forward Euler. You'll see this quite a bit in older papers (particularly if they seem to\n",
|
||
|
"be struggling with how to accommodate the delay-free feedback path), but it's basically rubbish. It works okay at very low\n",
|
||
|
"frequencies, but can easily transform a stable filter into an unstable one (as happens in the discretized state variable\n",
|
||
|
"filter), or totally lose the resonance properties (as in the Moog). It turns out to be totally fine for the one-pole lowpass,\n",
|
||
|
"so you'll see it there, and as a consequence works for the Moog filter in the zero resonance case, because that's just\n",
|
||
|
"four of those strung together.\n",
|
||
|
"\n",
|
||
|
"Both bilinear transform and step invariant lose accuracy, but in different ways. The bilinear transform has exactly the same\n",
|
||
|
"_frequency_ response as the analog prototype, but with frequencies warped, so that infinite frequency in the analog domain\n",
|
||
|
"maps to the Nyquist frequency. By contrast, step invariance has exactly the same _impulse_ response assuming that the input\n",
|
||
|
"is a step exactly one sample period wide (also known as zero order hold), but since that signal is not band-limited, it\n",
|
||
|
"means that the frequency response is aliased. Note that it's just the response that's aliased; there are no frequencies in\n",
|
||
|
"the output that are not present in the input.\n",
|
||
|
"\n",
|
||
|
"A good general criterion for choosing is: does the frequency response have a sharp lowpass characteristic? If so, then\n",
|
||
|
"with step invariance, the aliased components will be small, and primarily affect only the near-Nyquist frequencies. Otherwise,\n",
|
||
|
"for example for a high-pass response, bilinear transform is a better choice. We'll show this with graphs after implementing\n",
|
||
|
"the discretization techniques."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 3,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Bilinear transform"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"First, we need to pre-warp the frequency response, so that the response of the digital filter matches that of the analog filter\n",
|
||
|
"at the cutoff frequency. Here, frequency $f$ is as a fraction of the sampling rate, so that 1/2 is the Nyquist frequency.\n",
|
||
|
"\n",
|
||
|
"$ g = tan( \\pi f ) $\n",
|
||
|
"\n",
|
||
|
"Then, the discretization is as given by [Comparing digital implementation\n",
|
||
|
"via the bilinear and step-invariant transformations](http://myweb.polyu.edu.hk/~magzhang/Research/ZC04_auto.pdf), Guofeng Zhang and Tongwen Chen.\n",
|
||
|
"\n",
|
||
|
"$$ \\begin{align}\n",
|
||
|
"A_d & = (\\mathbf{I} - gA)^{-1} (\\mathbf{I} + gA) \\\\\n",
|
||
|
"B_d & = 2g(\\mathbf{I} - gA)^{-1} B \\\\\n",
|
||
|
"C_d & = (\\mathbf{I} - gA)^{-1} C \\\\\n",
|
||
|
"D_d & = g C (\\mathbf{I} - gA)^{-1} B + D\n",
|
||
|
"\\end{align} $$\n",
|
||
|
"\n",
|
||
|
"Here, the $(\\mathbf{I} - gA)^{-1}$ term represents solving the entire set of equations simultaneously, and includes the zero-delay\n",
|
||
|
"feedback term. To me, it's much easier just to throw the system into a matrix and solve it by matrix inverse in this way,\n",
|
||
|
"rather than solving all the individual equations by hand. Your mileage may vary."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"def bilinear(sys, f):\n",
|
||
|
" A, B, C, D = sys\n",
|
||
|
" g = tan(pi * f)\n",
|
||
|
" im = inv(identity(len(A)) - g * A)\n",
|
||
|
" Ad = dot(im, identity(len(A)) + g * A)\n",
|
||
|
" Bd = 2 * dot(im, g * B)\n",
|
||
|
" Cd = dot(C, im)\n",
|
||
|
" Dd = D + dot(C, dot(im, g * B))\n",
|
||
|
" return Ad, Bd, Cd, Dd"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"prompt_number": 9
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Some historical notes. To my knowledge, the first publication of a proper bilinear transform of the analog Moog filter\n",
|
||
|
"was: http://quod.lib.umich.edu/cgi/p/pod/dod-idx/preserving-the-structure-of-the-moog-vcf-in-the-digital.pdf, Preserving the Structure\n",
|
||
|
"of the Moog VCF in the Digital Domain, Federico Fontana.\n",
|
||
|
"\n",
|
||
|
"The earlier publications by Stilson and Smith, then Huovilainen, contained defective discretizations that attempted to deal with\n",
|
||
|
"the delay free loop some other technique than simply solving the equations.\n",
|
||
|
"\n",
|
||
|
"These days, however, the proper bilinear transform is standard. In synthesizer circles, the technique goes by the name\n",
|
||
|
"\"zero delay filters\" and also \"topology preserving transform\". An important milestone in popularizing this technique was\n",
|
||
|
"the publication of http://www.native-instruments.com/fileadmin/ni_media/downloads/pdf/VAFilterDesign_1.0.3.pdf, The Art of\n",
|
||
|
"VA Filter Design, by Vadim Zavalishin.\n",
|
||
|
"\n",
|
||
|
"Meanwhile, outside the published literature, there is an excellent discussion of this topic in the KVR DSP Forum thread,\n",
|
||
|
"http://www.kvraudio.com/forum/viewtopic.php?f=33&t=349859, Cheap non-linear zero-delay filters."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 3,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Step-invariant transform"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"The step-invariant transform represents the exact response under the assumption that the input is a sequence of steps\n",
|
||
|
"each one sample period in width (also known as zero order hold).\n",
|
||
|
"There are a few ways to compute it; here is the one I find\n",
|
||
|
"easiest.\n",
|
||
|
"\n",
|
||
|
"$ A' = \\left[\\begin{array}{cc} 0 & 0 \\\\ B & A \\end{array}\\right] $\n",
|
||
|
"\n",
|
||
|
"Then,\n",
|
||
|
"\n",
|
||
|
"$ \\left[\\begin{array}{cc} \\_ & \\_ \\\\ B_d & A_d \\end{array}\\right] = \\exp(2 \\pi f A') $\n",
|
||
|
"\n",
|
||
|
"Here, $\\exp(A)$ represents the matrix exponential, and the underscores respresent throwing away that part of the\n",
|
||
|
"resulting matrix.\n",
|
||
|
"\n",
|
||
|
"And\n",
|
||
|
"\n",
|
||
|
"$$ \\begin{align}\n",
|
||
|
"C_d & = C \\\\\n",
|
||
|
"D_d & = D\n",
|
||
|
"\\end{align} $$"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"def step_invariant(sys, f):\n",
|
||
|
" A, B, C, D = sys\n",
|
||
|
" A_ = concatenate([[zeros(len(A) + 1)], concatenate([[B], A.T]).T])\n",
|
||
|
" eA = linalg.expm(2 * pi * f * A_)\n",
|
||
|
" Ad = eA[1:, 1:]\n",
|
||
|
" Bd = eA[1:, 0]\n",
|
||
|
" return Ad, Bd, C, D"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"prompt_number": 10
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Matrix exponential is a cookbook technique (implemented as linalg.expm in NumPy). It is defined in terms of the straightforward extension of the Taylor series for\n",
|
||
|
"ordinary exp(x) generalized to matrices:\n",
|
||
|
"\n",
|
||
|
"$$ exp(A) = \\sum\\limits_{k=0}^{\\infty} \\frac{A^k}{k!} $$\n",
|
||
|
"\n",
|
||
|
"If you're implementing it by yourself, for small matrices, the \"scaling and squaring\"\n",
|
||
|
"technique works best. See http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.156.9316, The scaling and squaring method for the matrix exponential revisited, Nicholas Higham. Here's a simple implementation that computes the first n1 terms of the Taylor's series expansion\n",
|
||
|
"for exp, followed by n2 repeated squarings."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"# should give the same results as linalg.expm for well-behaved systems\n",
|
||
|
"def my_expm(A, n1 = 8, n2 = 8):\n",
|
||
|
" A1 = A / (1 << n2)\n",
|
||
|
" A2 = np.identity(len(A))\n",
|
||
|
" A3 = np.identity(len(A))\n",
|
||
|
" for i in range(1, n1):\n",
|
||
|
" A3 = dot(A1, A3) / i\n",
|
||
|
" A2 = A2 + A3\n",
|
||
|
" for i in range(n2):\n",
|
||
|
" A2 = dot(A2, A2)\n",
|
||
|
" return A2"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"prompt_number": 11
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 2,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Implementing state space filters"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Actually running a filter given in discrete time state space formulation is pretty straightforward. Mathematically, it is\n",
|
||
|
"given as:\n",
|
||
|
"\n",
|
||
|
"$$ \\begin{align}\n",
|
||
|
"\\mathbf{x}(n + 1) = & A\\mathbf{x}(n) + B\\mathbf{u}(n) \\\\\n",
|
||
|
"\\mathbf{y}(n) = & C\\mathbf{x}(n) + D\\mathbf{u}(n)\n",
|
||
|
"\\end{align} $$\n",
|
||
|
"\n",
|
||
|
"But it's probably just as easy to show the code:"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"def runss(sys, inp):\n",
|
||
|
" A, B, C, D = sys\n",
|
||
|
" x = zeros(len(A))\n",
|
||
|
" result = []\n",
|
||
|
" for u in inp:\n",
|
||
|
" result.append(dot(C, x) + D * u)\n",
|
||
|
" x = dot(A, x) + B * u\n",
|
||
|
" return result"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"prompt_number": 12
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"We can see that the impulse response for the bilinear and step-invariant transformations of the Moog filter are pretty similar,\n",
|
||
|
"but off in phase by a half sample or so."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"sys = moog_system(0.8)\n",
|
||
|
"impulse = zeros(100)\n",
|
||
|
"impulse[10] = 1\n",
|
||
|
"plot(runss(bilinear(sys, 0.1), impulse))\n",
|
||
|
"plot(runss(step_invariant(sys, 0.1), impulse))\n",
|
||
|
"show()"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data",
|
||
|
"png": "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\nyHOYHYIgnJ+IMNhBUw9DO13P
|
||
|
"text": [
|
||
|
"<matplotlib.figure.Figure at 0x108a803d0>"
|
||
|
]
|
||
|
}
|
||
|
],
|
||
|
"prompt_number": 13
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Matrix-based implementations in state space can be very, very fast. The \"raising\" trick of [Implementation of recursive\n",
|
||
|
"digital filters into vector SIMD DSP architectures](https://mns.ifn.et.tu-dresden.de/Lists/nPublications/Attachments/360/Robelly_P_ICASSP_04.pdf) by Robelly et al for an especially useful technique for implementing second\n",
|
||
|
"order filters on SIMD hardware tuned for 4xfloat vectors.\n",
|
||
|
"\n",
|
||
|
"See [Second order sections in matrix form](Second%20order%20sections%20in%20matrix%20form.ipynb) notebook for more details\n",
|
||
|
"on matrix implementation, including favorable comparisons of roundoff error compared to canonical direct form IIR implementations."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 2,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Response of state space filters"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"It's possible to accurately compute the frequency and phase response of a state space filter, without for\n",
|
||
|
"example having to expand out an impulse response and compute a fourier transform. Of course, it doesn't\n",
|
||
|
"hurt to plot this Fourier transform of an impulse anyway, if for no other reason than a sanity check:"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"impulse = concatenate([[1], zeros(255)])\n",
|
||
|
"w, h = signal.freqz(runss(bilinear(sys, 0.1), impulse))\n",
|
||
|
"semilogx(w, db(h))\n",
|
||
|
"show()"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data",
|
||
|
"png": "iVBORw0KGgoAAAANSUhEUgAAAYQAAAEFCAYAAADjUZCuAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAHPxJREFUeJzt3X1Y1GW+x/E3CqZW5no0H2ZITEBEMT0m6ilz2sCH3Dim\nyUH3lB3z9GCtuuWa/bEb7qbo7na8ssLNVkstk6xVssu4JHP09KC4dMrVaRONWhhEaxUfykRlzh8/\nRBBEGGbm95uZz+u65prhnpnffLwvnC/3ff8eIjwejwcREQl7rcwOICIi1qCCICIigAqCiIhUU0EQ\nERFABUFERKqpIIiICODHglBSUsLtt99Ov3796N+/P0uXLgXg6NGjpKamEh8fz6hRo6ioqPBXBBER\naYYIfx2HUF5eTnl5OQMHDuTUqVMMHjyYjRs38sorr9C5c2fmzp3L4sWLOXbsGIsWLfJHBBERaQa/\njRC6devGwIEDAbjmmmvo27cvbrebd955h6lTpwIwdepUNm7c6K8IIiLSDH4bIdT29ddfM3LkSPbu\n3csNN9zAsWPHAPB4PHTq1KnmZxERMY/fF5VPnTrFxIkTee6557j22mvrPBcREUFERIS/I4iISBNE\n+nPjZ8+eZeLEidx7772MHz8egK5du1JeXk63bt04dOgQ119/fb33xcbGcvDgQX9GExEJOb179+bA\ngQNev99vIwSPx8MDDzxAYmIis2fPrmlPS0tj1apVAKxataqmUNR28OBBPB6PX29PP/203997pdc1\n9vzlnru0vaHXNeU1VunP5rzP1/3pTd9ZuS8D1Z/NaQ+X/gzE//Wm9F9L/5D22wjho48+4rXXXmPA\ngAEMGjQIgKysLObNm0d6ejorVqwgJiaGN998018RGuVwOPz+3iu9rrHnL/fcpe0Nva4l/zZvefuZ\nzXmfr/uzKW3B1JfNfa+3/dmc9nDpz0D8X2+ozdf9GZBF5eaKiIjAgrGCVmZmJpmZmWbHCAnqS99S\nf/pWS787daRyGDDjr7JQpb70LfWntWiEICISIjRCEBERn1BBEBERQAVBRESqqSCIiAiggiAiItVU\nEEREBFBBEBGRaioIIiICqCCIiEg1FQQREQFUEEREpJoKgoiIACoIIiJSTQVBREQAFQQREammgiAi\nIoAKgog0wzffwP79ZqcQf9EV00SkSU6cgNhYOHkSiouhWzezE8mldMU0EQmIN96AESPg5z+H1183\nO434gwqCiDTJq6/CtGkwfjxs2mR2GvEHTRmJyBUdPAjDh0NZGZw9C127wj/+AR07mp1MagvKKaO8\nvDwSEhKIi4tj8eLFZkQQkWbIyYFJkyAyEtq1g2HDYPt2s1OJrwW8IJw/f57HHnuMvLw8XC4Xb7zx\nBl988UWgY4hIM6xbBxkZF3++4w7YutW8POIfAS8IBQUFxMbGEhMTQ1RUFBkZGeTm5gY6hog00b59\ncOwY3HLLxbaUFHj/ffMyiX8EvCC43W6io6Nrfrbb7bjd7kDHEJEmev11+I//gFa1vi0GDoTDh401\nBQkdAS8IERERgf5IEfHSmTOwciVMn163vXVrcDjggw9MiSV+EhnoD7TZbJSUlNT8XFJSgt1ur/e6\nzMzMmscOhwOHwxGAdCJS2/r1kJQECQn1n7uwjvCf/xn4XGJwOp04nU6fbS/gu52eO3eOPn36sHXr\nVnr06EFycjJvvPEGffv2vRhKu52KmM7jgSFD4De/gbS0+s9/+SWkphqns9DA3xpa+t0Z8BFCZGQk\nL7zwAqNHj+b8+fM88MADdYqBiFjDhg1w/jz87GcNPx8fD1VVcOAAxMUFNpv4hw5ME5F6zp0zpoqW\nLIExYy7/uqlTjQPWHn44cNnk8oLywDQRsbY//AF69oTRoxt/3R13aPfTUKIRgojUsW+fsQdRYSHc\ncEPjry0vh759jfurrgpIPGmERggi4jMVFTBhgjFCuFIxAOMU2ElJkJ/v/2zifyoIIgIYxxykpxvT\nRPff3/T3TZoEb73lt1gSQJoyEhF+/BEmToT27Y3rHkQ2Y/9DtxsGDIBDh6BNG/9llCvTlJGItIjb\nDSNHwnXXwdq1zSsGADabsY6gxeXgp4IgEsa2bIHkZOOiN6+/DlFR3m1nyhRYvdq32STwNGUkEoaO\nHIE5c+B//xeWLzeOOG6JigqIiTEOUuvc2ScRxQuaMhKRJvv2W3jySWOK5/rr4W9/a3kxAOPKaWlp\nGiUEOxUEkRDn8cBHH8F99xmnmDhxAj7/HP74R7jmGt99zn//N7z8svF5EpxUEERC0Llz4HTCrFnG\nEcfTpxvXMDh4EJYtgwZOMNxit95q3H/4oe+3LYGhNQSREFBRAZ99ZnwZ79gBO3dCbKxxkNmECcYU\nUSDOSPrii8Ypsf/yF/9/ltTX0u9OFQSRIHH+PJSWwldfQXGxsYD7t7/Bnj1w9KhxxPAtt8CIEcb9\nv/xL4DN+/72xuPzxxzoDqhlUEESCVFUVnDplzOmfOAHHjxt7/xw5Ylye8vDhi49LS6GkBLp0gRtv\nvHhLSjIOCuvVq+4lLs3061/DP/8J2dlmJwk/KghhpHaXNPT4Ss8357WhuK3z5xu/nTt35ddcuFVW\nGkf3njlz8b7240vvT5+++MV/4XbqlHFkcIcOF29dukDXrsbt+usvPu7Rw/jLu21bLO/wYeMKa19+\nafwbJHBUEELUnXfCe+81/prac8IXHjfU5u1rQ21brVtf/hYZ2fjzl96uuurirW3buvcNtbVrV/eL\nv0MHuPZaY1uh6Be/ME5j8eyzZicJLyoIIaqq6vJfdiJWV1YG/fvD3r3G6EYCQwVBRCzp8ceNabil\nS81OEj5UEETEko4cMXZ3/ewziI42O014UEEQEcuaN8/Y4+jll81OEh5UEETEsioqoE8f49TYSUlm\npwl9OrmdiFhWx47GcQmPP65zHAUDFQQR8auHHjIOrLvSbtRiPr8UhF/96lf07duXm266iQkTJnD8\n+PGa57KysoiLiyMhIYEtW7b44+NFxEKioowzqz7xBJw9a3YaaYxfCsKoUaPYt28fn3/+OfHx8WRl\nZQHgcrnIycnB5XKRl5fHjBkzqKqq8kcEEbGQO+80zrD60ktmJ5HG+KUgpKam0qr6xCpDhw6ltLQU\ngNzcXCZPnkxUVBQxMTHExsZSUFDgjwgiYiEREfA//wO//a1xkR6xJr+vIaxcuZI777wTgLKyMuy1\nTsRut9txu93+jiAiFpCUBD//uXHFNrGmSG/fmJqaSnl5eb32hQsXctdddwGwYMEC2rRpw5QpUy67\nnQidk0EkbMyfbxys9tFHxim6xVq8Lgj5+fmNPv/qq6+yefNmtm7dWtNms9koKSmp+bm0tBSbzdbg\n+zMzM2seOxwOHA6Ht1FFxCI6dDAWmGfMgMJC46SC4j2n04nT6fTZ9vxyYFpeXh5PPPEE27dvp3Pn\nzjXtLpeLKVOmUFBQgNvtJiUlhQMHDtQbJejANJHQ5fHAHXfA+PEwc6bZaUKLJY9UjouLo7Kykk6d\nOgEwfPhwsquvlrFw4UJWrlxJZGQkzz33HKNHj64fSgVBJKR98QXcdptxtbfu3c1OEzosWRBaSgVB\nJPTNm2ccsPbaa2YnCR0qCCISlL7/HhITYdUq0BKhb+hcRiISlK6+GpYsgUcfNS5JKuZTQRAR09x9\nN/TsCc89Z3YSAU0ZiYjJDhyAYcOMC+nUOm5VvKApIxEJarGxxrTRL39pdhLRCEFETHf6NPTvD8uW\nwahRZqcJXhohiEjQa9cOli6Fxx6DM2fMThO+VBBExBLGjTN2Q/3DH8xOEr40ZSQilvHNNzB4MOze\nDb16mZ0m+GjKSERCRs+eMHs2zJljdpLwpBGCiFjKjz8aU0fLl0NKitlpgotGCCISUtq2NY5gnjlT\n12AONBUEEbGctDSIjoYXXzQ7SXjRlJGIWJLLZZz0bv9+6NjR7DTBQVNGIhKSEhONkcKiRWYnCR8a\nIYiIZbndMGAAfP65znPUFLoe
|
||
|
"text": [
|
||
|
"<matplotlib.figure.Figure at 0x108a92b50>"
|
||
|
]
|
||
|
}
|
||
|
],
|
||
|
"prompt_number": 14
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"The transfer function of a filter in (discrete time) state space formulation is simple, and very similar to\n",
|
||
|
"the continuous time version:\n",
|
||
|
"\n",
|
||
|
"$$ H(z) = D + C(zI-A)^{-1}B $$\n",
|
||
|
"\n",
|
||
|
"So similar, in fact, we can reuse the existing transfer() function for both continuous and discrete time.\n",
|
||
|
"\n",
|
||
|
"See https://ccrma.stanford.edu/~jos/filters/Transfer_Function_State_Space.html for the derivation and more discussion."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Now we'll plot the bilinear and step-invariant transforms for the Moog filter, against the analog prototype,\n",
|
||
|
"for a single frequency. Both are quite close, although the frequency warping of the bilinear version causes\n",
|
||
|
"its rolloff to accelerate at high frequencies."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"def freqz_ss(sys):\n",
|
||
|
" return array([transfer(sys, exp(1j * pi * x)) for x in xs])\n",
|
||
|
"\n",
|
||
|
"axis([1e-2, 1, -60, 10])\n",
|
||
|
"f = 0.05\n",
|
||
|
"sysd = bilinear(sys, f)\n",
|
||
|
"semilogx(xs, db(freqz_ss(sysd)))\n",
|
||
|
"sysd = step_invariant(sys, f)\n",
|
||
|
"semilogx(xs, db(freqz_ss(sysd)))\n",
|
||
|
"\n",
|
||
|
"# Analog prototype\n",
|
||
|
"semilogx(xs, db(freqz_ss_continuous(sys, 2 * f)))\n",
|
||
|
"show()"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data",
|
||
|
"png": "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\nsc1rqdK4pdFxxIMkJyeTnJxc
|
||
|
"text": [
|
||
|
"<matplotlib.figure.Figure at 0x108ac8510>"
|
||
|
]
|
||
|
}
|
||
|
],
|
||
|
"prompt_number": 15
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"The bilinear transform shows the characteristic warping of frequency response, including a zero at the\n",
|
||
|
"Nyquist frequency."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"axis([1e-2, 1, -60, 10])\n",
|
||
|
"for i in range(5):\n",
|
||
|
" f = 0.4 * .5 ** i\n",
|
||
|
" sysd = bilinear(sys, f)\n",
|
||
|
" semilogx(xs, db(freqz_ss(sysd)))\n",
|
||
|
" semilogx(xs, db(freqz_ss_continuous(sys, 2 * f)))\n",
|
||
|
"show()"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data",
|
||
|
"png": "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\nnbHaaQ/iNjH6ZxvwUzl/KHtn
|
||
|
"text": [
|
||
|
"<matplotlib.figure.Figure at 0x108acba50>"
|
||
|
]
|
||
|
}
|
||
|
],
|
||
|
"prompt_number": 16
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"For this particular filter response, the step invariant transform is more accurate."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"axis([1e-2, 1, -60, 10])\n",
|
||
|
"for i in range(5):\n",
|
||
|
" f = 0.4 * .5 ** i\n",
|
||
|
" sysd = step_invariant(sys, f)\n",
|
||
|
" semilogx(xs, db(freqz_ss(sysd)))\n",
|
||
|
" semilogx(xs, db(freqz_ss_continuous(sys, 2 * f)))\n",
|
||
|
"show()"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data",
|
||
|
"png": "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\nhYOFhQ0vzkTWXV6HKR2mQKlk
|
||
|
"text": [
|
||
|
"<matplotlib.figure.Figure at 0x108aaef10>"
|
||
|
]
|
||
|
}
|
||
|
],
|
||
|
"prompt_number": 17
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"For highpass filters, the bilinear transform performs fairly well, albeit with noticeable effects\n",
|
||
|
"from frequency warping at higher corner frequencies."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"sys = svf_hp_system(0.5)\n",
|
||
|
"axis([1e-2, 1, -60, 10])\n",
|
||
|
"for i in range(5):\n",
|
||
|
" f = 0.4 * .5 ** i\n",
|
||
|
" sysd = bilinear(sys, f)\n",
|
||
|
" semilogx(xs, db(freqz_ss(sysd)))\n",
|
||
|
"show()"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data",
|
||
|
"png": "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\nXwgJEXTsCHa7HXuxFaxWhNUK
|
||
|
"text": [
|
||
|
"<matplotlib.figure.Figure at 0x108a7ec50>"
|
||
|
]
|
||
|
}
|
||
|
],
|
||
|
"prompt_number": 18
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"However, step-invariant is not a good choice for high-pass filter responses, as expected."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"axis([1e-2, 1, -60, 10])\n",
|
||
|
"for i in range(5):\n",
|
||
|
" f = 0.4 * .5 ** i\n",
|
||
|
" sysd = step_invariant(sys, f)\n",
|
||
|
" semilogx(xs, db(freqz_ss(sysd)))\n",
|
||
|
"show()"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data",
|
||
|
"png": "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\nN2OvRCnFDxE/MHXbVPp17McH
|
||
|
"text": [
|
||
|
"<matplotlib.figure.Figure at 0x108a9c910>"
|
||
|
]
|
||
|
}
|
||
|
],
|
||
|
"prompt_number": 19
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 3,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"A bit of simplification"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"It's certainly possible to just use matrix primitives to design the filter. However, for modulating the filter parameters\n",
|
||
|
"at a high rate, it might make sense to simplify the math symbolically. For the state variable filter, the following\n",
|
||
|
"code goes straight to the bilinear transformed state space representation, using a by-hand symbolic solution to the matrix\n",
|
||
|
"inverse:"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"def svf_lp(f, res):\n",
|
||
|
" g = tan(pi * f)\n",
|
||
|
" k = 2 - 2 * res\n",
|
||
|
" a1 = 1/(1 + g * (g + k))\n",
|
||
|
" a2 = g * a1\n",
|
||
|
" a3 = g * a2\n",
|
||
|
" A = array([[2*a1 - 1, -2*a2],\n",
|
||
|
" [2*a2, 1 - 2*a3]])\n",
|
||
|
" B = array([2 * a2, 2 * a3])\n",
|
||
|
" C = array([a2, 1 - a3])\n",
|
||
|
" D = a3\n",
|
||
|
" return A, B, C, D"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [],
|
||
|
"prompt_number": 20
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Then we can check that this really does compute the same system."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [
|
||
|
"f = 0.1\n",
|
||
|
"res = 0.2\n",
|
||
|
"print svf_lp(f, res)\n",
|
||
|
"print bilinear(svf_lp_system(res), f)"
|
||
|
],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"output_type": "stream",
|
||
|
"stream": "stdout",
|
||
|
"text": [
|
||
|
"(array([[ 0.23043279, -0.39979185],\n",
|
||
|
" [ 0.39979185, 0.87009975]]), array([ 0.39979185, 0.12990025]), array([ 0.19989592, 0.93504988]), 0.064950123180475744)\n",
|
||
|
"(array([[ 0.23043279, -0.39979185],\n",
|
||
|
" [ 0.39979185, 0.87009975]]), array([ 0.39979185, 0.12990025]), array([ 0.19989592, 0.93504988]), 0.064950123180475744)\n"
|
||
|
]
|
||
|
}
|
||
|
],
|
||
|
"prompt_number": 21
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Simplified code for the rest of the [Audio EQ cookbook](http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt) designs are also at the [Second order sections in matrix form](Second%20order%20sections%20in%20matrix%20form.ipynb) notebook.\n",
|
||
|
"\n",
|
||
|
"Similarly, for the Moog filter, it's not too difficult to invert the matrix symbolically. Both Vadim's book and\n",
|
||
|
"the KVR thread on cheap zero delay filters contain these derivations.\n",
|
||
|
"\n",
|
||
|
"TODO: adapt code and put here."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 2,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"A note on nonlinearity"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Real analog filters have nonlinearities, and true \"virtual analog\" simulations attempt to model those as\n",
|
||
|
"well as the purely linear components.\n",
|
||
|
"Antti's paper is excellent at presenting differential equations representing\n",
|
||
|
"an only slightly idealized version of the Moog filter.\n",
|
||
|
"\n",
|
||
|
"Really emulating the non-linearity is beyond the scope of this cookbook, but I will note that keeping the\n",
|
||
|
"state space variables matching physical quantities (voltages across capacitors, etc.) is a huge help. There\n",
|
||
|
"are a number of ways of simply adding the nonlinearity back in. These are covered in more detail in the\n",
|
||
|
"\"cheap zero delay\" KVR thread and Vadim's book."
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "heading",
|
||
|
"level": 2,
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"Conclusion"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "markdown",
|
||
|
"metadata": {},
|
||
|
"source": [
|
||
|
"We have shown how to design digital IIR filters in a state space framework, starting from analog prototypes\n",
|
||
|
"and giving some choices regarding discretization. All these techniques are cookbook and don't require solving\n",
|
||
|
"systems of equations by hand, or doing symbolic manipulation.\n",
|
||
|
"\n",
|
||
|
"These techniques work on a wide range of source filters and allow both easy, fast implementation and analysis, for example plotting the frequency response as a precise function of frequency, not requiring Fourier transforms\n",
|
||
|
"of impulse responses.\n",
|
||
|
"\n",
|
||
|
"In addition, the literature is rich in techniques for state space systems. As an example, the poles of the resulting\n",
|
||
|
"filter are exactly the eigenvalues of the $A$ matrix. In analog form, state space matrices can be composed arbitrarily,\n",
|
||
|
"including with feedback paths, making the basic technique powerful for circuit design and analysis.\n",
|
||
|
"\n",
|
||
|
"It is my hope that these matrix formulations will make the process of designing digital filters less mysterious,\n",
|
||
|
"and accessible to a broader audience, much in the way that Dan Lancaster's Active Filter Cookbook popularized analog\n",
|
||
|
"filter designs, and no doubt contributed to a great many home-built designs.\n"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"collapsed": false,
|
||
|
"input": [],
|
||
|
"language": "python",
|
||
|
"metadata": {},
|
||
|
"outputs": []
|
||
|
}
|
||
|
],
|
||
|
"metadata": {}
|
||
|
}
|
||
|
]
|
||
|
}
|