I'm working on a (very simple) EQ-matching plugin and wanted to check my
understanding/approach and I also have a question. Here's what I do:
1. Analyze spectrum 1 using 50% overlapping blocks each windowed with a
Hann window followed by an FFT and then using the absolute value to get
the magnitude response of each block. Then finally I calculate the
average of all those magnitude responses.
2. Analyze spectrum 2 using the same approach as for spectrum 1
3. Calculate the bin-wise ratio of spectrum 2 / spectrum 1
4. Calculate a linear phase filter response by using the IFFT of the
aforementioned magnitude response ratio and then doing the ifftshift dance
5. Calculate a minimum phase filter response from the magnitude response
taking the complex log followed by an IFFT, then folding the negative
time components onto the positive ones, followed by an FFT, complex
eponentiation and a final IFFT. In code:
f = IFFT(exp(FFT(fold(IFFT(log(s))))))
where s is the magnitude response ratio.
The code implementing this is found here:
The plugin using it is not done yet.
So here are my questions:
1. Are there any glaring oversights with this approach?
2. What is a sensible approach to regularize this approach. If spectrum
1 has any components near zero magnitude then the division is ill
suited. It works fine for my experiments with distorted guitars, but I
wonder whether I should e.g. just clamp the values of spectrum 1 to
something like -70 dB?
3. If the user wants to smooth the spectrum before calculating the
responses what would be sensible approaches? I thought about smoothing s
with a filter that has a varying bandwidth when expressed in FFT bins or
Hz, but constant, when expressed in e.g. octaves or decades. I'm not
sure how to do that though. Any thoughts?
To finish this email, here is one example (you can play them e.g. by
using mplayer on the console):
1. A guitar recording played through some distortion:
2. The same guitar recording played through some distortion and then a
3. The same guitar played through the recovered filter after matching 1
spectmorph-0.6.0 has been released.
The new main features are:
- SpectMorph now provides a CLAP plugin.
- A filter with different filter modes was added.
- A new, more flexible modulation system was added.
- SpectMorph now provides visual feedback for modulated parameters.
Video Tutorial for 0.6.0: https://youtu.be/watch?v=scKrf0o6e6k
What is SpectMorph?
SpectMorph is a free software project which allows to analyze samples of
musical instruments, and to combine them (morphing). It can be used to
construct hybrid sounds, for instance a sound between a trumpet and a
flute; or smooth transitions, for instance a sound that starts as a
trumpet and then gradually changes to a flute.
SpectMorph ships with many ready-to-use instruments which can be
combined using morphing.
SpectMorph is implemented in C++ and licensed under the GNU LGPL version
2.1 or later
Integrating SpectMorph into your Work
SpectMorph is currently available for Linux, Windows and macOS (Intel
and Apple Silicon), with CLAP/LV2/VST plugins. Under Linux, there is
also JACK Support.
There are many audio demos on the website, which demonstrate morphing
List of Changes in SpectMorph 0.6.0:
#### New features
* New, more flexible modulation system
* Added filter with different filter modes
* Provide visual feedback for modulated properties
* Provide signed .pkg installers for macOS (Intel and ARM)
#### CLAP Plugin
* Provide CLAP Plugin
* Support for per-voice modulation
* Support timestamped modulation/automation events
#### LV2 Plugin
* Support LV2 on all platforms
* Fix crashes triggered by Carla (absolute_path/abstract_path returning
* Support newer LV2 development headers
#### Minor Changes
* Support "Velocity" as modulation source
* Make pitch bend range configurable
* New Presets with filter: "Cheese Cake Bass", "Liquid Silver"
* Sort midi events by timestamp to workaround Bitwig bug
* Sliders now support shift+drag for fine editing
* Support for Apple Silicon
* Avoid crashes if XOpenIM / XCreateIC return NULL (#15).
* Fix statically linked plugin data directory location (works in flatpak
* Add dockerized MXE builds for windows, bump compiler version to gcc-12.
* Bump minimum C++ standard to C++17
* Fix build on RISC-V (#13)
* Use GitHub CI for Linux, macOS and Windows
* Change license from "LGPL v3 or later" to "LGPL v2.1 or later".
* Minor fixes and cleanups
#### Internals: Properties
* Add generic property handling
* Simplify load/save/gui for properties
* Support modulatable properties using ModulationList
* Add gui for editing property value and ModulationList
* MorphPlan is no longer ref-counted, just one instance per Project
* Introduced MorphOperatorConfig objects for cleaner/faster parameter
#### Internals: Filter
* Add two filter types: "Ladder" and "Sallen-Key" filter to output operator
* Integrated PandaResampler for SIMD 4x filter oversampling
* Support modulation with high time resolution for filter
#### Internals: UI Toolkit
* Support multiple update regions in UI toolkit
* Optimize drawing for UI toolkit
* Support "software sprites" for efficient visual feedback
* Map Ctrl+Left Click to Right Click on macOS
#### Internals: Optimizations
* Pass wav set pointers (instead of strings) to morph linear/grid/source.
* Avoid fmod() for phase truncation.
* Build using -ffast-math
* NotifyBuffer: fast dsp thread -> ui thread notifications (no malloc in
* Avoid allocations in dsp thread in many cases (retrigger, noise
* Support optimized SIMD code on ARM (Apple Silicon), code from Peter
Stefan Westerfeld, http://space.twc.de/~stefan
I wrote a small plugin for spreading mono content to stereo and I would
like to receive some feedback on it from people more knowledgable in DSP
matters than me.
It has one nice property: It spreads the signal over the spectrum by way
of a conjugate pair of random phase all-pass filters, and since it's a
conjugate pair it sums back to unity when down-mixing to mono. So, no
ugly comb filter effects like when downmixing a Haas-expanded signal.
The page has an example sound..
How it works:
It's actually rather simple:
1. create a vector of random phases (matlab notation):
filter_length = 1000;
spread = pi;
hfft1 = exp(-1i*(spread*rand(filter_length,1) - spread/2));
And for the second filter just take the complex conjugate:
hfft2 = conj(hfft1);
This ensures that what is a phase theta in the first filter becomes a
phase of -theta in the second filter, and summed that just gives a phase
2. Then assemble the coefficients such that it corresponds to a fft of a
real signal and do the inverse fft (possibly I have a small error here
which i needed to fix with the 'symmetric' flag in matlab):
f1 = ifft([1; hfft1; conj(hfft1((end-1):-1:1))], 'symmetric');
f2 = ifft([1; hfft2; conj(hfft2((end-1):-1:1))], 'symmetric');
3. The two IRs f1 and f2 implement the pair of filters and can be
applied via convolution (which the above plugin does).
It seems to be possible with little ill effect to reduce the length of
the filter down to a size of 50 samples or so by just cutting it off
before the convolution.
What do you think?
I'm a bit of a noob to linux audio programming, but I'm stuck with alsa lib and wondering if someone could help.
When I use the function snd_dtl_open I get the error:
Cannot open shared library libasound_module_conf_pulse.so (/usr/lib/alsa-lib/libasound_module_conf_pulse.so
The library exists in /usr/lib/x86_64-linux-gnu/alsa-lib/l, but not /usr/lib/alsa-lib which doesn't exist.
I suppose I could create a symlinked folder, but I'm wondering why alsa looks in that directory and what I can do get it working correctly?
Thanks for any advice.