Filteredspeakertoolkit
Filteredspeakertoolkit
Filteredspeakertoolkit
The Filter
The BiQuad
BiQuad 0 2 + 1 + 2 0 = 2 1 2
At the heart of every Filter Object in the toolkit is the basic BiQuad filter. It is uniquely defined by 5 constants - 0 , 1 , 2 , 1 , 2 . Some basic constructors are provided to determine these constants. If not explicitly constructed, it defaults to a NULL BiQuad with Zero Gain and unit Transfer. [0 = 1, 1 = 2 = 1 = 2 = 0] = 2/ is a complex number at frequency "" (in Hz) and is the filters sampling frequency (also in Hz). = 1 , = 3.14159
The transfer function, 0 , defines how the BiQuad filter scales the input response at frequency f.
See MiniDSP's All Digital Coefficients Sheet See MiniDSP's All Digital Coefficients Sheet See MiniDSP's All Digital Coefficients Sheet Defined using coeffs b0,b1,b2,a1,a2
These are the currently builtin constructors aka FilterTypes. Look at the Sample sheets for the parameters corresponding to each FilterType. Alternate constructors can be used if you know how to derive the 5 coefficients just use the BiQuadCoeffs FilterType to send in your values. The set of built-in constructors may evolve over time these were available in Version 1.0
HighPass
HighShelf Linkwitz_Filter Linkwitz_Transform LowPass LowShelf Notch Peaking PoleAndZero PoleAndZeroFreqs RIAA NULL
The Filter
BiQuad
BiQuad
Embedded Filters
BiQuad BiQuad 0 = 0 2 + 1 + 2 2 1 2
BiQuad
BiQuad
BiQuad
BiQuad
BiQuad
A Filter object has embedded within it a single BiQuad and an array of other Filters. Filters could contain Filters that themselves contain Filters and so on. There are no explicit limits on either the number of embedded Filters nor on the depth of the nesting.
The embedded BiQuads could each be constructed using any of the available constructors. Each BiQuad has its own Sampling Frequency so in principle a single Filter could contain components with differing Sampling Frequencies. All Filters are defined using the FilterDefine function. Only IIR filters are available currently. All Filters have unique names (aka handles) and can be referenced using their names.
Filter Operations
= 0 .
=1
are the transfer functions of the embedded Filters N = Number of embedded filters The Filter scales signals that are input to it by
Helper Functions are available to: - dump Coefficients in MiniDSP format - List all Poles and Zeros - Get Transfer function - Stream Contents
Look at the sample sheets for more details on how to define and use Filters in the toolkit
Creating a Filter
A Filter can be created/defined using the function FilterDefine whose signature is as follows: =FilterDefine(MyName,BuildOptions0,FiltersToEmbed,BuildOptions1,BuildOptions2,BuildOptions3) MyName : This is a string that can be used to reference the Filter in memory. It is a handle. If the Filter is built successfully, the function will return this string. This is a 2-column range that contain Options/Parameters for constructing the primary BiQuad. The first column should contain the Name of the option and the second column its Value. As an example:
FilterType SamplingFrequency dbOffset Polarity AllPass 96000 5 1
BuildOptions0 :
The block can be of any length. Option Names that are not understood and/or those that have blank values will be ignored. STRICT STRING COMPARISONS are done internally in checking for Option Names. Try not to type them by hand use the helper functions. Look at the Main tab in the Sample workbook for cells with Data Validation to get pull-down lists. FiltersToEmbed : BuildOptions1-3 : This is an optional array of FilterNames that were created previously. These Filters will be embedded. These are additional blocks. Internally, BuildOptions0 through BuildOptions3 will be stacked together and processed like a single block. Separating them in the signature makes it easier to collect information from different parts of the Excel sheet.
Valid BuildOptions can be obtained using the FilterGetBuildOptions and FilterGetTypes functions.
Look at the Filter Examples worksheet in the sample workbook for more details on how to use these.
These are all the available BuildOptions. Not all of them may be relevant for all Filters. Look at the output from the function FilterGetBuildOptions for details on what options are relevant for each FilterType. Look at the Basic Filter building block in the sample sheet.
This is a listing of some additional filter related functions. Look at the sample sheet Available Functions tab for more details. Check out the embedded comments in the cell with the Calling Sequence
The Speaker-Driver
FRD file
OR
Freq_Hz 10 30 200 3325 SPL_dB Phase_deg 65 80 69 65 76 60 54 24
At least one basic speaker-driver lurks within all Speakers in the Toolkit. Unless explicitly defined, it has a flat frequency response set at 0db with a phase of 0 degrees. The driver gets its frequency response from either a standard FRD file or from a range containing similar data. Note: (a) If Phase information is omitted it will be assumed to be 0. (b) Minimum Phase can be extracted from the SPL data and used for all response computations in the complex plane. Once constructed, the driver has a well defined Response function that feeds the Speaker(s) it is used in.
20 180 0 = 10 Where the SPL _dB and Phase_deg correspond to the appropriate interpolated values at frequency f. Additional options such as Polarity, Delays, PhaseShifts, dbOffsets, etc. are available and can be applied. _ . _ deg .
The function SpeakerResponse can be used to stream response information out to an FRD file and/or a range.
Time delay : can be specified as cms, feet, inches, meters, milliseconds or seconds. Spline Tension : For interpolating between data points Polarity : Positive ( .ge. 0) or Negative ( .lt. 0) MinPhase : variety of parameters related to Minimum Phase
Parameters to control slope beyond first and last point Parameters to control octave-width smoothing.
Alternately if a text string is specified for the SPLData field, the function will assume that it is the name of a file and attempt to open+read data using a standard FRD format. As of this writing, I do not handle tabs as separators correctly. USE SPACES AS SEPARATORS IN THE FILE, NOT TABS. A helper function is provided in the toolkit that will open a file selection window and return the full path/filename of the selected file as a string. The function is =SpeakerGetFRDfilename(CachedName,startDir) Notes:
If the field CachedName is omitted (blank ) or if a file with the specified name cannot be located, then a system File-SelectionWindow will be opened. The starting directory for the search will be in startDir (Default = the system folder MyDocuments). The output of the function is a string containing the full pathname of the file you selected. It would be a royal PITA to have the file selection window pop up every time this function is called so If CachedName is the name of a file that exists in your system, then the function just returns this name and the file selection window will NOT be displayed. Thus this field can be used to prevent the File-Selection-Window being opened each time the cell is recalculated. Check out the sample sheets for examples where (using a circular reference) a recursive approach is incorporated to do this automatically across Excel sessions. You manually get the function to open the window, select your file, tell the function to remember the filename. No more pop ups even if you reboot your machine and load the sheet again as long as a file with that name exists on your system, the File-Selection-Window will not open unless you manually ask it to do so again! Sit down dog! ;-) Btw, you do NOT need to use the function (SpeakerGetFRDfilename) to specify a speaker file you can type in the full filename manually. The function just makes the file selection process a bit easier than having to manually type it in thats all.
Once the SPL and Phase data have been imported, a driver will need to interpolate between the specified points and extrapolate beyond the discrete range (especially for Minimum Phase calculations) if it is to be useful. A tension spline is used to do this interpolation. The SplineTension parameter used in the spline is normalized - a value of 0 converges to a cubic spline while a tension of 25 (or greater) degenerates to linear interpolation (I actually switch to linear interpolation internally if the specified tension exceeds 25). A tension of 1.0 provides a nice tight fit that is differentiable everywhere. A tension of 10.0 yields something pretty close to linear interpolation but one that is still differentiable at the data input points (aka knot points). Separate splines are used for Amplitude and Phase but they share a common tension parameter. Phase is unwrapped to be a strictly decreasing function before any interpolation is done. This avoids any spurious effects induced from interpolating a wrapped phase (which can have large discontinuities) using a smooth interpolant.
Extrapolation is allowed beyond the range specified by the data - the Optional parameters SlopeLo_dB_Octave and SlopeHi_dB_Octave are used to determine how the response function decays beyond the two ends. These are proportional gains/cuts applied to Amplitude and/or Phase per octave of the frequencies at the low and high ends. The default values are SlopeLo_dB_Octave = 48 and SlopeHi_dB_Octave =-48. This provides a slope off to 0 at 48dB/octave. To summarize,
= + SlopeLo_dB_Octave. log 2 , + SlopeHi_dB_Octave. log 2 , + SlopeLo_dB_Octave. log 2 , + SlopeHi_dB_Octave. log 2 ,
I confess that Im not too excited by the above definition for the Phase component. But in the absence of a more appealing alternative I figured that for the most part well be interpolating in the data and not extrapolating so this is not hyper critical.
The default values are MinPhase_OctaveSlopeLo = 12 and MinPhase_OctaveSlopeHi = 12 (i.e. 12 dB/octave). Over a span of 5 octaves this translates to a reduction of 60dB or equivalently a reduction in the amplitude by a factor of 1000 = 1020 . Finally, the Group Delay corresponding to the Minimum Phase is computed numerically using a Five-point stencil. At the endpoints the algorithm degenerates to either a central or one-sided derivative.
The Speaker
Summing Speakers
Speaker Speaker
Embedded Filters
Speaker BiQuad Primary Driver BiQuad Speaker
Scaling Speakers
BiQuad BiQuad Speaker Speaker
Speaker
Speaker
A Speaker in the toolkit contains a Primary Driver, an optional array of embedded Filters, an optional array of Summing Speakers and an optional array of Scaling Speakers. They collectively contribute to the frequency response function of the Speaker. Each embedded Speaker could be a complex Speaker object Speakers could contain Speakers that could in turn contain other Speakers. And again there is no explicit limit on either the number or depth of nesting of Speakers or Filters. The limit comes from the amount of memory available. To prevent run-away effects of self referential connections, all embedded speakers are copied over at define time (i.e. it is NOT a reference so a full new copy is stored in memory). Ill give you one guess on how I found out about this ;-)
= 0 +
=1
() .
=1
() .
=1
[ ]
0 is the response of the primary driver within A (using any SPL data that was input) are the responses of the Summing speakers whose responses will be added (each scaled by a weighting factor ). are the transfer functions of the embedded Filters. are the responses of the Scaling speakers. They scale the final response like a filter. Each Scaling speaker has its contribution scaled by . So if =0.1 and the scaling speaker had a response of 50dB at some frequency, then the scaling factor will be 5dB (since Amplitude = 10^(dB/20)) at that frequency. Additionally, a Delay or PhaseShift can be specified (which will be applied to the summed and filtered response, ) as well as a Polarity switch (which again will be applied to the final response and return . Finally, smoothing algorithms (which essentially average across a set of neighboring points) are also provided these will be applied at the end and serve as visual aids in calibration etc. I expect that this representation should cover most of the cases we encounter normally in a calibration. If a need arises, Ill consider extending this further.
Output-Stage Smoothing: In this case the smoothing is applied at the final stage *after* all filters etc. have been applied to the speakers response. i.e. the smoothing is done to the finally computed response curve, . One way is to think of this as the smoothing that the human ear/brain may apply to a choppy response it is done after the Speaker produces its output. However it opens another wrinkle in the calibration process. Since the filter is also smoothed (actually the product of the speaker and the filter are smoothed), it now becomes harder to isolate/visualize the impact that any particular filter has. For example if you are trying to optimally locate a notch filter, its impact may get completely smoothed out in the output plots! Fortunately, smoothing can be turned off or on easily by switching the option, TurnOffAllSmoothing_bool, to be True or False.
By default, smoothing will only be applied at either the input or output stage. If a smoothing model is specified at both stages, then any stored input level smoothing that was relevant for that speaker will be turned off. You can turn both on using the option AllowBothSmoothing_bool. Be warned though turning smoothing on at both ends will increase the computation time significantly since each point will now require far more calculations. Think about it - using just 20 steps at each end (which is not a lot) implies 400 (20*20) evaluations for each computed response. Given the above, one needs to exercise care when using smoothing. Smoothing can be a powerful ally in the calibration process, but by its very nature, smoothing discards some information (e.g. high frequency oscillations) so that we can focus on others (e.g. broad trends and shapes). Being aware of what it does and judicious use is a key to its usefulness.
. .
Where is the unsmoothed response, is the weighting function, and < < are the lower and upper limits of the window within which smoothing is done. In general the wider the window, the smoother the response but well miss out on the detail s. Similarly, the flatter the weight function the smoother will be the response. An extreme weight function that is peaked at the center frequency will be similar to no smoothing at all. In the toolkit the integral is computed using Gauss-Legendre Quadrature as =
. . =
=1
( )
The weights and locations are uniquely determined by the number of points, N. The greater the number of points, the more accurate the integral but it will take more time to compute. I find 21 to 31 points to work well. 101 points gives a very clean result (but each response is computed an extra 100 times!). Speed will be the determining factor. If N=1, then no smoothing occurs. Note: If N is an odd number then the value at f is included in the averaging with (N-1)/2 samples on either side. If N is even, then the value at f is not included in the averaging. The integration can further view the frequency axes as being linear or proportional. A linear model will set the window symmetrically around the center frequency and the points are also equally spaced. The window is = < < = + . In the proportional model the spacing is symmetric using the logarithm of the frequency axis = / < < = . The window width is specified in terms of the number of octaves of the center frequency. Specifying a window that is 1 proportional model implies that = . 21/(2) ; = . 21/(2) The points at which the summation is done are proportionally spaced in this interval. With the linear model, = . (1 21/(2) ) ; = . (1 + 21/(2) ) and the N measurement points are equally spaced.
In the current implementation there are two different weighting functions available. The first is a Uniform Distribution over [ , ]. = 1 , 0,
The second one is a truncated Normal distribution function centered in the middle. The volatility parameter of the normal is chosen so that the boundaries are "" standard deviations away. Thus if we define the density function by (, , ) and the cumulative distribution function as , , , then the weight function can be expressed as + 2 2 , , , = + , 2 , + , 2 , 2 2 0,
When "" is small, this function approaches the uniform. As "" increases in value the bell shape of the normal becomes increasingly apparent and the weight given to the extreme values declines. Id like to add Savitsky-Golay one of these days ;-)
Streaming
Functions are available to stream the internal representations of Filters and Speakers to/from ranges on the sheet as well as files on the hard disk.
Directives
There are a few options that I ended up keeping separate from those that are listed only with Filters or Speakers I may combine them with those relevant for Speakers in a future revision. Similar to other options, these are name/value pairs with the name in the first column and the value in the second. I treat them in exactly the same manner as Options when called from Excel . They are just separate within the code. DoNotReCompute_bool : TRUE/FALSE. Default=FALSE. Certain functions (SpeakerDefine, FilterDefine, SpeakerResponse, and FilterTransfer) store a copy of the computed value in an internal cache. If this option is set to TRUE, then subsequent calls will return the cached value. This can speed up the functions SpeakerResponse and FilterTransfer since all computations are bypassed. In the case of SpeakerDefine and FilterDefine, the object is not recreated and thus any changes made to component speakers are ignored (e.g. a Summing speaker may have been changed but we do not wish to update this define with the new instance of the object). If this is set to TRUE in any function call, then that cell will only be computed once in any workbook that it resides in. QuickBuild_bool removed. : TRUE/FALSE. Default=FALSE. This was used during debugging. Does not affect any valuation. Will be
WrapPhase_bool : TRUE/FALSE. Default=FALSE. If set to TRUE, then Phase that is output will be wrapped in ( -180,+180) if in degrees and in (-,+) if in radians. PhaseInRadians_bool : TRUE/FALSE. Default=FALSE. If set to TRUE, then the Phase output is in Radians, else in Degrees.
TurnOffAllSmoothing_bool : TRUE/FALSE. Default=FALSE. If set to TRUE, then all smoothing is turned off. Only relevant for the SpeakerResponse function AllowBothSmoothing_bool : TRUE/FALSE. Default=FALSE. If set to TRUE , then Both Inner and Outer Smoothing algorithms will be applied simultaneously (assuming that both Inner and Outer Smoothing have been specified). If FALSE, Inner Smoothing will be turned off whenever Outer Smoothing has been specified. WriteToFRDFilename_string : A string with a valid file name. Default=. If included with the SpeakerResponse function, the speaker response will be sent out in the FRD format to the file specified. Note: If DoNotReCompute_bool has been specified as TRUE and a cached copy of the response is available, then nothing will be written out.
Whenever one of the computationally intensive functions are called, the results are stored internally in a memory cache. The key/name that is used to identify these cached values is the actual address of the calling range. When the Directive, DoNotReCompute_bool , is set to TRUE the cached value is returned for subsequent function calls and no additional computations are done. The functions that cache their results are: FilterDefine FilterTransfer SpeakerDefine SpeakerResponse SpeakerGetFRDfilename : This caches the name of the Filter : This caches the array of numbers that were computed and sent out. : This caches the name of the Speaker : This caches the array of numbers that were computed and sent out. : This caches the full name of the FRD file that was selected.
In all cases, the key that is associated with the value in the cache is the address of the range that called the function in the first place. A few functions are provided to manage this cache. RangeList(substring,ReturnCountOnly):
Returns a list of the available keys that begin with the requested substring (Default=)
ReturnCountOnly is True/False (Default=False). If True it returns the count of keys in memory. RangeDump(name) RangeRemoveFromCollection(substring) : This will return the contents of the named range (if it exists). : This will remove all caches starting with substring from memory. If the argument is blank, everything is cleared