Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 13 of 13

Thread: Filter Tracking with Resonance issue

  1. #1
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    209

    Filter Tracking with Resonance issue

    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
    };



    Thanks for help. Greetings Rolf

  2. #2
    Senior Member
    Join Date
    Apr 2019
    Posts
    153
    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
            }

  3. #3
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    209
    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)

    Click image for larger version. 

Name:	Tracking.jpg 
Views:	13 
Size:	20.3 KB 
ID:	24783

    I can't find that in your code.

  4. #4
    Senior Member
    Join Date
    Apr 2019
    Posts
    153
    It's in here: https://github.com/PaulStoffregen/Au...ter_variable.h

    Perhaps you're misunderstanding what filterOctave does? And I am too? Perhaps someone can explain, I though it was to do with width of the cutoff freq, although that should be 12dB?!?

  5. #5
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    209
    I want to control the filter tracking over seven octaves with midi notes.

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




    Greetings Rolf

  6. #6
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    209
    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;
    }

  7. #7
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    209
    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

  8. #8
    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).

  9. #9
    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);

  10. #10
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    209
    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;
    }

  11. #11
    Quote Originally Posted by Rolfdegen View Post

    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.

  12. #12
    Senior Member Rolfdegen's Avatar
    Join Date
    Sep 2020
    Location
    Germany
    Posts
    209
    Hallo Graham

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

    Greetings from germany. Rolf

    My little "Jeannie" Synthesizer Prototyp

    Last edited by Rolfdegen; 05-18-2021 at 09:32 AM.

  13. #13
    Quote Originally Posted by Rolfdegen View Post
    Hallo Graham

    Thank you for your very good explanation. I will think about it.
    You are welcome, always happy to help out fellow synthesiser designers if I can

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •