Programming
Formula / C Code
For about seven years I have had a lot of fun manipulating sound, or more specifically sound generated from a software synthesizer I am programming, with DSP – Digital Signal Processing. For my synthesizer I have programmed not just tone generation via multiple synthesis methods, but also modulation, effects, and filtering of those tones.
In the past I have tried out new DSP code ideas directly in Visual Studio, compiling the C/C++ code, and then open my VST3 plugin in the Reaper DAW software., However, I recently came upon “Formula” and VST or stand-alone program that allow one to quickly create and test DSP C code within the App. You may find information about “Formula”, including a download link here; https://soundspear.com/product/formula
The App itself contains, at the time of writing, about formulas, pieces of C code, anywhere from filters, to delay, chorus, reverb, and distortion. One of these included formula’s, are “Formant vowel” which is a formant filter that can make a waveform such as a sawtooth, sound like a person speaking vowels. I incorporated this into my synthesizer, and quickly expanded the code from being able to “only” generate 5 vowels via a cutoff parameter ranging from 0 to 1, to code that could smoothly mix between two adjacent vowels. It sounds cool when applying an envelope, and/or LFO to the cutoff frequency. As I was unable to submit my enhanced version to the developer, I am publishing it here on my website for anyone to use as they wish.
Note if you’re not using the “Fomula” app to test this, you need to replace “KNOB_1” with your variable holding the current cutoff frequency, from 0.01f to 0.99f.
/*
Author or source: Alex
Public source code by alex@smartelectronix.com
Simple example of implementation of formant filter
Vowelnum can be 0,1,2,3,4 <=> A,E,I,O,U
Good for spectral rich input like saw or square
Expanded by René W. Feuerlein on June 25th, 2026
to smoothly mix between two adjacent vowels, although
mixing from 5th to 1st has issues, so note I'm mixing
from 5th to second. Sounds great with an envelope or
LFO modulating the cutoff frequency. DKDiveDude.com
*/
const double coeff[5][11]= {
{ 8.11044e-06,
8.943665402, -36.83889529, 92.01697887, -154.337906, 181.6233289,
-151.8651235, 89.09614114, -35.10298511, 8.388101016, -0.923313471 ///A
},
{4.36215e-06,
8.90438318, -36.55179099, 91.05750846, -152.422234, 179.1170248, ///E
-149.6496211,87.78352223, -34.60687431, 8.282228154, -0.914150747
},
{ 3.33819e-06,
8.893102966, -36.49532826, 90.96543286, -152.4545478, 179.4835618,
-150.315433, 88.43409371, -34.98612086, 8.407803364, -0.932568035 ///I
},
{1.13572e-06,
8.994734087, -37.2084849, 93.22900521, -156.6929844, 184.596544, ///O
-154.3755513, 90.49663749, -35.58964535, 8.478996281, -0.929252233
},
{4.09431e-07,
8.997322763, -37.20218544, 93.11385476, -156.2530937, 183.7080141, ///U
-153.2631681, 89.59539726, -35.12454591, 8.338655623, -0.910251753
}
};
//---------------------------------------------------------------------------------
static double memory[10]={0,0,0,0,0,0,0,0,0,0};
//---------------------------------------------------------------------------------
formula_main
{
int vowelNum1 = fmin(4, 5 * KNOB_1);
int vowelNum2 = vowelNum1 + 1;
if (vowelNum2 > 4) vowelNum2 = 1;
// Mix Ratio
float cutoffMin = 0.01f;
float cutoffMax = 0.99f;
float cutoffSectionRange = (cutoffMax - cutoffMin) / 5;
float mix1 = (vowelNum1 + 1) - (KNOB_1 - cutoffMin) / cutoffSectionRange;
float mix2 = 1.0f - mix1;
double res1 = (float) ( coeff[vowelNum1][0] * input +
coeff[vowelNum1][1] *memory[0] +
coeff[vowelNum1][2] *memory[1] +
coeff[vowelNum1][3] *memory[2] +
coeff[vowelNum1][4] *memory[3] +
coeff[vowelNum1][5] *memory[4] +
coeff[vowelNum1][6] *memory[5] +
coeff[vowelNum1][7] *memory[6] +
coeff[vowelNum1][8] *memory[7] +
coeff[vowelNum1][9] *memory[8] +
coeff[vowelNum1][10] *memory[9] );
double res2 = (float) ( coeff[vowelNum2][0] * input +
coeff[vowelNum2][1] *memory[0] +
coeff[vowelNum2][2] *memory[1] +
coeff[vowelNum2][3] *memory[2] +
coeff[vowelNum2][4] *memory[3] +
coeff[vowelNum2][5] *memory[4] +
coeff[vowelNum2][6] *memory[5] +
coeff[vowelNum2][7] *memory[6] +
coeff[vowelNum2][8] *memory[7] +
coeff[vowelNum2][9] *memory[8] +
coeff[vowelNum2][10] *memory[9] );
float resCombined = res1 * mix1 + res2 * mix2;
memory[9]= memory[8];
memory[8]= memory[7];
memory[7]= memory[6];
memory[6]= memory[5];
memory[5]= memory[4];
memory[4]= memory[3];
memory[3]= memory[2];
memory[2]= memory[1];
memory[1]= memory[0];
memory[0]=(double) resCombined;
return resCombined;
}