Filter Tracking with Resonance issue

Status
Not open for further replies.

Rolfdegen

Well-known member
Hallo friends

I'm using the Teensy Audio Lib for a state variable filter. I control the filter frequency for key tracking via the frequency control input.

The filter's corner frequency is set via filter.frequency()

The control of the filter frequency works correctly. But the resonance frequency shifts downwards and no longer matches the filter frequency.

I programmed the Filter.octave control to a fixed value of 7 (7 octaves).



Code:
//*************************************************************************
// play Voices
//*************************************************************************
void voice1On(byte note, byte velocity, float level) {
  keytrackingValue = KEYTRACKINGAMT[note] * keytrackingAmount;  // keytrackingAmount = 0.0 - 1.0  (0-100%)
  filterModMixer1.gain(2, keytrackingValue); 
  voices[0].note = note;
  voices[0].timeOn = millis();
  // Amp velocity
  float velo = (0.5 - myAmpVelocity) + (VELOCITY[velocitySens][velocity] * myAmpVelocity);
  voiceMixer1.gain(0, velo);
  //voiceMixer1.gain(0, VELOCITY[velocitySens][velocity] * VOICEMIXERLEVEL);
  // Filter velocity
  velo = ((0.5 - myFilVelocity) + (VELOCITY[velocitySens][velocity] * myFilVelocity));
  //FilterVelo1.amplitude(velo);
  filterEnvelope1.noteOn();
  ampEnvelope1.noteOn();
  voices[0].voiceOn = 1;
  if (glideSpeed > 0 && note != prevNote) {
    glide1.amplitude((prevNote - note) * DIV24);   //Set glide to previous note frequency (limited to 1 octave max)
    glide1.amplitude(0, glideSpeed * GLIDEFACTOR); //Glide to current note
  }
  if (unison == 0)prevNote = note;
}


  filterOctave = 7.0f;
  filter1.octaveControl(filterOctave);
  filter2.octaveControl(filterOctave);
  filter3.octaveControl(filterOctave);
  filter4.octaveControl(filterOctave);
  filter5.octaveControl(filterOctave);
  filter6.octaveControl(filterOctave);
  filter7.octaveControl(filterOctave);
  filter8.octaveControl(filterOctave);

void updateFilterFreq() {
  filter1.frequency(filterFreq);
  filter2.frequency(filterFreq);
  filter3.frequency(filterFreq);
  filter4.frequency(filterFreq);
  filter5.frequency(filterFreq);
  filter6.frequency(filterFreq);
  filter7.frequency(filterFreq);
  filter8.frequency(filterFreq);
}
  
  filterOctave = 7.0f;
  filter1.octaveControl(filterOctave);
  filter2.octaveControl(filterOctave);
  filter3.octaveControl(filterOctave);
  filter4.octaveControl(filterOctave);
  filter5.octaveControl(filterOctave);
  filter6.octaveControl(filterOctave);
  filter7.octaveControl(filterOctave);
  filter8.octaveControl(filterOctave);

// KeyTracking Amt
const static float KEYTRACKINGAMT[128] = {
    0, 0.008, 0.016, 0.024, 0.031,
    0.039, 0.047, 0.055, 0.063, 0.071, 0.079, 0.087, 0.094, 0.102, 0.110,
    0.118, 0.126, 0.134, 0.142, 0.150, 0.157, 0.165, 0.173, 0.181, 0.189,
    0.197, 0.205, 0.213, 0.220, 0.228, 0.236, 0.244, 0.252, 0.260, 0.268,
    0.276, 0.283, 0.291, 0.299, 0.307, 0.315, 0.323, 0.331, 0.339, 0.346,
    0.354, 0.362, 0.370, 0.378, 0.386, 0.394, 0.402, 0.409, 0.417, 0.425,
    0.433, 0.441, 0.449, 0.457, 0.465, 0.472, 0.480, 0.488, 0.496, 0.504,
    0.512, 0.520, 0.528, 0.535, 0.543, 0.551, 0.559, 0.567, 0.575, 0.583,
    0.591, 0.598, 0.606, 0.614, 0.622, 0.630, 0.638, 0.646, 0.654, 0.661,
    0.669, 0.677, 0.685, 0.693, 0.701, 0.709, 0.717, 0.724, 0.732, 0.740,
    0.748, 0.756, 0.764, 0.772, 0.780, 0.787, 0.795, 0.803, 0.811, 0.819,
    0.827, 0.835, 0.843, 0.850, 0.858, 0.866, 0.874, 0.882, 0.890, 0.898,
    0.906, 0.913, 0.921, 0.929, 0.937, 0.945, 0.953, 0.961, 0.969, 0.976,
    0.984, 0.992, 1
};


Jeannie-one-Voice-V103m.jpg


Thanks for help. Greetings Rolf
 
You need to set filterOctave lower (1.0f) and it'll track properly. This is why my code has this odd feature where filterOctave is changed with cut off freq, to maintain deep basses at low cut off and better sharper, resonance at higher cut off. This is how TSynth gets sub-bass and percussive sounds, because the resonance peak is wider.

Code:
        float filterOctave = 0.0;
        //Altering filterOctave to give more cutoff width for deeper bass, but sharper cutoff at higher frequencies
        if (value <= 2000) {
            filterOctave = 4.0f + ((2000.0f - value) / 710.0f);//More bass
        } else if (value > 2000 && value <= 3500) {
            filterOctave = 3.0f + ((3500.0f - value) / 1500.0f);//Sharper cutoff
        } else if (value > 3500 && value <= 7000) {
            filterOctave = 2.0f + ((7000.0f - value) / 4000.0f);//Sharper cutoff
        } else {
            filterOctave = 1.0f + ((12000.0f - value) / 5100.0f);//Sharper cutoff
        }
 
The calculation must work according to this formula.

Signal is Filter.tracking value. For frequence shift with 1.octav is signal value 0.14285 (7 octaves)

Tracking.jpg

I can't find that in your code.
 
I want to control the filter tracking over seven octaves with midi notes.

These are the keytracking values (Signal) for the FilterModMixer input.


Signal-Value.jpg


Greetings Rolf
 
In the TSynth the key tracking is monophonic. I made it polyphonic in the Voices


polyphone KeyTracking
Code:
//*************************************************************************
// play Voices
//*************************************************************************
  void voice1On(byte note, byte velocity, float level) {
  keytrackingValue = KEYTRACKINGAMT[note] * keytrackingAmount;
  filterModMixer1.gain(2, keytrackingValue); 
  voices[0].note = note;
  voices[0].timeOn = millis();
  filterEnvelope1.noteOn();
  ampEnvelope1.noteOn();
  voices[0].voiceOn = 1;
  if (glideSpeed > 0 && note != prevNote) {
    glide1.amplitude((prevNote - note) * DIV24);   //Set glide to previous note frequency (limited to 1 octave max)
    glide1.amplitude(0, glideSpeed * GLIDEFACTOR); //Glide to current note
  }
  if (unison == 0)prevNote = note;
}
 
This is the table for the filter tracking values. The middle frequency is 440Hz.

The filter corner frequency and resonance is shifted 100% with keytracking.

Code:
const static float KEYTRACKINGAMT[128] = {
    
    -0.53571551f, -0.52380850f, -0.51190530f, -0.49999932f, -0.48809746f, -0.47618825f, -0.46428706f, -0.45238275f,
    -0.44047653f, -0.42857142f, -0.41666832f, -0.40476328f, -0.39285837f, -0.38095136f, -0.36904815f, -0.35714218f, 
    -0.34523781f, -0.33333346f, -0.32142769f, -0.30952351f, -0.29761939f, -0.28571428f, -0.27380941f, -0.26190447f,
    -0.24999965f, -0.23809570f, -0.22619101f, -0.21428636f, -0.20238067f, -0.19047632f, -0.17857166f, -0.16666637f,
    -0.15476225f, -0.14285714f, -0.13095227f, -0.11904732f, -0.10714251f, -0.09523782f, -0.08333317f, -0.07142856f,
    -0.05952353f, -0.04761918f, -0.03571452f, -0.02380975f, -0.01190461f,  0.00000000f,  0.01190486f,  0.02380939f,
     0.03571423f,  0.04761895f,  0.05952397f,  0.07142858f,  0.08333329f,  0.09523795f,  0.10714290f,  0.11904765f,
     0.13095228f,  0.14285714f,  0.15476201f,  0.16666674f,  0.17857137f,  0.19047627f,  0.20238093f,  0.21428572f,
     0.22619044f,  0.23809524f,  0.25000004f,  0.26190479f,  0.27380954f,  0.28571428f,  0.29761904f,  0.30952378f,
     0.32142861f,  0.33333332f,  0.34523808f,  0.35714286f,  0.36904758f,  0.38095239f,  0.39285711f,  0.40476187f,
     0.41666669f,  0.42857142f,  0.44047618f,  0.45238093f,  0.46428571f,  0.47619047f,  0.48809522f,  0.50000001f,
     0.51190476f,  0.52380953f,  0.53571429f,  0.54761904f,  0.55952380f,  0.57142857f,  0.58333332f,  0.59523809f,
     0.60714285f,  0.61904761f,  0.63095239f,  0.64285713f,  0.65476190f,  0.66666665f,  0.67857143f,  0.69047619f,
     0.70238094f,  0.71428571f,  0.72619047f,  0.73809524f,  0.74999999f

Code:
//*************************************************************************
// play Voices
//*************************************************************************
  void voice1On(byte note, byte velocity, float level) {
  keytrackingValue = KEYTRACKINGAMT[note] * keytrackingAmount;   // keytrackingAmount is 0.0 - 1.0 (0-100%)
  filterModMixer1.gain(2, keytrackingValue); 
  voices[0].note = note;
  voices[0].timeOn = millis();
  float velo = (0.5 - myAmpVelocity) + (VELOCITY[velocitySens][velocity] * myAmpVelocity);
  voiceMixer1.gain(0, velo);
  velo = ((0.5 - myFilVelocity) + (VELOCITY[velocitySens][velocity] * myFilVelocity));
  filterModMixer1.gain(3, velo); 
  filterEnvelope1.noteOn();
  ampEnvelope1.noteOn();
  voices[0].voiceOn = 1;
  if (glideSpeed > 0 && note != prevNote) {
    glide1.amplitude((prevNote - note) * DIV24);   //Set glide to previous note frequency (limited to 1 octave max)
    glide1.amplitude(0, glideSpeed * GLIDEFACTOR); //Glide to current note
  }
  if (unison == 0)prevNote = note;
}


void voice2On(byte note, byte velocity, float level) {
  keytrackingValue = KEYTRACKINGAMT[note] * keytrackingAmount;

Greetings from germany. Rolf
 
The octave option on the filter is nothing to do with the resonance, it determines how far the cut-off frequency can be modulated, so if it is set to 2, a full scale lfo waveform will modulate the cut-off frequency over a range of two octaves. if it is set to 5, then the filter cutoff is modulated over a range of five octaves. It is designed for limiting the size of filter sweeps, or as a means of adjusting modulation depth.

Sometimes, if you have the octave control set to the full 7 octaves, then you may well run into some problems at the highest frequencies, possibly even that horrible screeching noise when the filter gives up (which is why its sometimes necessary to limit the upper frequency of the filter using octave control).
 
TBH you are overcomplicating what should be a simple procedure.

You just need an array of note frequencies (such as you have in your spreadsheet).
then feed those frequencies directly in your code to the oscillators / filters, with the filter cutoff doubled if needed (experiment to see what works best for you).

Then use the audio design tool to feed modulation signals to the filter frequency input (same as you have done, but without the key tracking input).
You could utilise the octave control to adjust the modulation level, as in the lower the octave control, the less octaves the filter will be modulated over.
However in general I have always found just using a mixer to mix modulation levels together much easier and more versatile (using octave control adjusts all the input signals equally)

Below is a simplified example of how it is done, complete with a fifteen octave note frequency reference array.

Code:
const float freqs[] =
{
  8.1758,    8.6620,    9.1770,    9.7227,    10.3009,    10.9134,    11.5623,    12.2499,  12.9783,   13.7500,   14.5676,   15.4339,
  16.3516,   17.3239,    18.3540,    19.4454,  20.6017,   21.8268,   23.1247,   24.4997,   25.9565,    27.5000,    29.1352,    30.8677,
  32.7032,   34.6478,   36.7081,   38.8909,   41.2034,    43.6535,    46.2493,    48.9994,  51.9131,   55.0000,   58.2705,   61.7354,
  65.4064,   69.2957,    73.4162,    77.7817,  82.4069,   87.3071,   92.4986,   97.9989,   103.8262,   110.0000,   116.5409,   123.4708,
  130.8128,  138.5913,  146.8324,  155.5635,  164.8138,   174.6141,   184.9972,   195.9977,   207.6523,  220.0000,  233.0819,  246.9417,
  261.6256,  277.1826,   293.6648,   311.1270, 329.6276,  349.2282,  369.9944,  391.9954,  415.3047,   440.0000,   466.1638,   493.8833,
  523.2511,  554.3653,  587.3295,  622.2540,  659.2551,   698.4565,   739.9888,   783.9909,   830.6094,  880.0000,  932.3275,  987.7666,
  1046.5023, 1108.7305,  1174.6591,  1244.5079,    1318.5102, 1396.9129, 1479.9777, 1567.9817, 1661.2188,  1760.0000,  1864.6550,  1975.5332,
  2093.0045, 2217.4610, 2349.3181, 2489.0159, 2637.0205,  2793.8259,  2959.9554,  3135.9635,  3322.4376, 3520.0000, 3729.3101, 3951.0664,
  4186.0090, 4434.9221,  4698.6363,  4978.0317,  5274.0409, 5587.6517, 5919.9108, 6271.9270, 6644.8752,  7040.0000,  7458.6202,  7902.1328,
  8372.0181, 8869.8442, 9397.2726, 9956.0635, 10548.0818, 11175.3034, 11839.8215, 12543.8540,  13289.7504, 14080.0000,  14917.2404,  15804.2656,
  16744.0362, 17739.6884, 18794.5452, 19912.127, 21096.1636, 22350.6068, 23679.643, 25087.7080, 26579.5008, 28160.0000, 29834.4808, 31608.5312,
  33488.0724, 35479.3768, 37589.0904, 39824.254, 42192.3272, 44701.2136, 47359.286, 50175.416, 53159.0016, 56320.0000, 59668.9616, 63217.0624,
  66976.1448, 70958.7536, 75178.1808, 79648.508, 84384.6544, 89402.4272, 94718.572, 100350.832, 106318.003, 112640.0000, 119337.923, 126434.125,
  133952.29, 141917.507, 150356.362, 159297.016, 168769.309, 178804.854, 189437.144, 200701.664, 212636.006, 225280.0000, 238675.846, 252868.25
};

float notefreq1 = freqs[insert note number here];

waveform1.frequency(notefreq1);
filter1.frequency(notefreq1 * 2);
 
Yes. Thank you for the information.

The cutoff frequency now follows the midi note. It wasn't exactly before.

For filter velocity to 7 octaves, we have limited the resonance to 4.0.

Direct input of the filter frequency cannot be used for filter tracking because it changes the Modulation values (LFO, Velocity, Envelope and Cutoff).

Code:
//*************************************************************************
// play Voices
//*************************************************************************
  void voice1On(byte note, byte velocity, float level) {
  keytrackingValue = KEYTRACKINGAMT[note] * keytrackingAmount;
  filterModMixer1.gain(2, keytrackingValue); 
  voices[0].note = note;
  voices[0].timeOn = millis();
  float velo = (0.5 - myAmpVelocity) + (VELOCITY[velocitySens][velocity] * myAmpVelocity);
  voiceMixer1.gain(0, velo);
  velo = ((VELOCITY[velocitySens][velocity] * myFilVelocity) * 4.5f);
  filterModMixer1.gain(3, velo); 
  filterEnvelope1.noteOn();
  ampEnvelope1.noteOn();
  voices[0].voiceOn = 1;
  if (glideSpeed > 0 && note != prevNote) {
    glide1.amplitude((prevNote - note) * DIV24);   //Set glide to previous note frequency (limited to 1 octave max)
    glide1.amplitude(0, glideSpeed * GLIDEFACTOR); //Glide to current note
  }
  if (unison == 0)prevNote = note;
}

Jeannie-one-Voice-V103m.jpg
 
Direct input of the filter frequency cannot be used for filter tracking because it changes the Modulation values (LFO, Velocity, Envelope and Cutoff).

I think you still misunderstand the filter design.
the base frequency set in code does not change as a result of whatever signals appear at the Fc/in1 input, nor will the modulation signals at Fc/in1 be affected by the frequency set in code, they are simply added together.

All keyboard tracking does is to make sure the cutoff frequency follows the note frequency (on a self oscillating filter that means the oscillation should perfectly track the note frequency).

if you set the frequency to 440hz in code:
(filter1.frequency(440))

Then the filters cutoff frequency will be 440hz - perfect tracking.

from the audio design tool documentation:
"frequency(freq);
Set the filter's corner frequency. When a signal is connected to the control input, the filter will implement this frequency when the signal is zero."

the zero signal referred to here is the signal to Fc/In1, the frequency control input (which should really be more accurately labelled as the modulation input).

Any signal sent to the Fc/in1 input is exponentially added to (or subtracted from) the base frequency by an amount defined by the octave control .
if you input a control signal to Fc of 1.0, and the octave control is set to 1 (filter.octaveControl(1)) then the filter frequency will increase by exactly one octave above that set in the code (cutoff = 880hz)
if octave control is set to 2, then the signal will be shifted by exactly two octaves (cutoff = 1760hz).

From the audio design tool documentation (underline mine):

"octaveControl(octaves);
Set how much (in octaves) the control signal can alter the filter's corner freqency. Range is 0 to 7 octaves. For example, when set to 2.5, a full scale positive signal (1.0) will shift the filter frequency up 2.5 octaves, and a full scale negative signal will shift it down 2.5 octaves."

So you can have a frequency set to 440hz in code, then add another +/- 7 octaves on top of that via the Fc/in1 input.

Paul stoffgrens filter design is actually very good, you can send linear control signals to the Fc/in1 input, and they are automatically converted into the correct frequency, like a very precise v/oct converter.
This conversion works over 14 octaves (+/- 7 octaves).

This is why I always set note frequency (tracking) for the filter in code, then add modulation, cutoff, envelope and velocity signals via the Fc/in1 connection.

Of course if you prefer the more complicated way of doing things then that is fine, it's your design not mine.
I'm just trying to help out by correcting some misconceptions you appear to have about how the filter object works, and to offer a simpler way of using the filter object.

TBH part of the problem is unclear documentation that can be easily misleading if you don't have English as your first language.
 
Hallo Graham

Thank you for your very good explanation. I will think about it for my project. :confused:

Greetings from germany. Rolf

My little "Jeannie" Synthesizer Prototyp

20210518-111652.jpg
 
Last edited:
Status
Not open for further replies.
Back
Top