Forum Rule: Always post complete source code & details to reproduce any issue!
Page 9 of 9 FirstFirst ... 7 8 9
Results 201 to 212 of 212

Thread: Porting moog ladder filters to audio objects?

  1. #201
    Junior Member
    Join Date
    Mar 2021
    Posts
    18
    Hello all, I'm super excited to use the filter. Flo mentioned it's in the "official" audio library now, but I don't see it in the GUI tool. Any tips on where to find it?

  2. #202
    Quote Originally Posted by rvh View Post
    Hi Florian, good to hear you're enjoying the filter. On my system, I get 7% for POLY_FIR, and 5% for LINEAR. To reduce it further I suggest going to 2x oversampling. However, that requires a different set of FIR coefficients. The ones from the newer filter that already runs at 2x should be fine, but they are 32pt rather than 36 so the changes to the .h file include changing both INTERPOLATION from 4 to 2, and interpolation_taps form 36 to 32.
    Hi Richard,
    thank you, I changed the coefficients and corrected the bug you found in the poly update routine and now it works great with relatively low CPU usage (also 4% for 2x oversampling and linear interpolation on my system).

    I will also have a look at the full model, but unfortunately, I need to economise on my CPU ressources.
    Last edited by flo; 03-08-2021 at 05:47 AM. Reason: Quoted too much text

  3. #203
    Quote Originally Posted by TigerBalm2 View Post
    Hello all, I'm super excited to use the filter. Flo mentioned it's in the "official" audio library now, but I don't see it in the GUI tool. Any tips on where to find it?
    Hello TigerBalm,

    weird, I noticed too that somehow, there are different versions of the gui tool. (with different indexes? )
    This here -> https://www.pjrc.com/teensy/gui/ does not include the filter, whereas
    this here -> https://www.pjrc.com/teensy/gui/inde...ioFilterLadder
    has it in.

    Greetings, flo

  4. #204
    Senior Member
    Join Date
    Feb 2021
    Posts
    172
    Here is an update of the new full-model filter_ladder2, which corrects a few minor issues, and adds linear interpolation as an option in the same way as the existing ladder filter.

    filter_ladder2.cpp
    Code:
    /* Audio Library for Teensy, Ladder Filter
     * Copyright (c) 2021, Richard van Hoesel
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice, development funding notice, and this permission
     * notice shall be included in all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    
    //-----------------------------------------------------------
    // AudioFilterLadder2 for Teensy Audio Library
    // Based on Huovilainen's full model, presented at DAFX 2004
    // Richard van Hoesel, March. 11, 2021
    // v.1.02 includes linear interpolation option
    // v.1.01 Adds a "resonanceLoudness" parameter (1.0 - 10.0)
    //        to control resonance loudness 
    // v.1.0 Uses 2x oversampling. Host resonance range is 0-1.0. 
    //
    // please retain this header if you use this code.
    //-----------------------------------------------------------
    
    #include <Arduino.h>
    #include "filter_ladder2.h"
    #include "arm_math.h"
    #include <math.h>
    #include <stdint.h>
    
    #define MAX_RESONANCE ((float)1.015)
    #define MAX_FREQUENCY ((float)(AUDIO_SAMPLE_RATE_EXACT * 0.425f))
    #define fI_NUM_SAMPLES  AUDIO_BLOCK_SAMPLES * INTERPOLATION
    
    //=== 2 xOS, 32taps (88.2kHz)
    static float AudioFilterLadder2 ::finterpolation_coeffs[AudioFilterLadder2::finterpolation_taps] = {    
    -129.6800658538650740E-6, 0.001562843504973615, 0.004914813078250063, 0.007186231102125209, 0.002728024844356039,-0.007973931496234599,-0.013255882861166815,-841.1646993462468340E-6,
     0.022309967341058432, 0.027240071522569135,-0.007073857882406733,-0.056523975383091875,-0.055929860712812876, 0.042278471323346570, 0.207056768528768836, 0.335633514003638445,
     0.335633514003638445, 0.207056768528768836, 0.042278471323346570,-0.055929860712812876,-0.056523975383091875,-0.007073857882406733, 0.027240071522569135, 0.022309967341058432,
    -841.1646993462468340E-6,-0.013255882861166815,-0.007973931496234599, 0.002728024844356039, 0.007186231102125209, 0.004914813078250063, 0.001562843504973615,-129.6800658538650740E-6
    };
    
    void AudioFilterLadder2::initpoly()
    {
      if (arm_fir_interpolate_init_f32(&interpolation, INTERPOLATION, finterpolation_taps,
         finterpolation_coeffs, finterpolation_state, AUDIO_BLOCK_SAMPLES)) {
        polyCapable = false;
        return;
      }
      if (arm_fir_decimate_init_f32(&decimation, finterpolation_taps, INTERPOLATION,
         finterpolation_coeffs, fdecimation_state, fI_NUM_SAMPLES)) {
        polyCapable = false;
        return;
      }
      // TODO: should we fill interpolation_state & decimation_state with zeros?
      polyCapable = true;
      polyOn = true;
    }
    
    void AudioFilterLadder2::interpolationMethod(AudioFilterLadderInterpolation imethod)
    {
      if (imethod == LADDER_FILTER_INTERPOLATION_FIR_POLY && polyCapable == true) {
        // TODO: if polyOn == false, clear interpolation_state & decimation_state ??
        polyOn = true;
      } else {
        polyOn = false;
      }
    }
    
    void AudioFilterLadder2::resonanceLoudness(float v)  // input 1-10
    {
      if(v<1) v=1;
      if(v>10) v=10;
      VTx2 =  v;   
      inv2VT = 1.0/VTx2;
    }
    
    static inline float fast_exp2f(float x)
    {
      float i;
      float f = modff(x, &i);
      f *= 0.693147f / 256.0f;
      f += 1.0f;
      f *= f;
      f *= f;
      f *= f;
      f *= f;
      f *= f;
      f *= f;
      f *= f;
      f *= f;
      f = ldexpf(f, i);
      return f;
    }
    
    void AudioFilterLadder2::inputDrive(float drive) 
    {
      if(drive<1)
          overdrive = 1.0f;
      else 
      if(drive>3)
         overdrive =  3.0f;  
      else
        overdrive = drive;   
    }
    
    void AudioFilterLadder2::portamento(float pc)
    {
      FcPorta = pc; 
    }
    
    void AudioFilterLadder2::compute_coeffs(float freq, float res) // tuned for 2x os
    {
        float fc, freq2, freq3;          
        if (res > MAX_RESONANCE) res = MAX_RESONANCE ;
        if (res < 0.0f) res = 0.0f;    if (freq < 5) freq = 5;
        if (freq > MAX_FREQUENCY) freq = MAX_FREQUENCY;       
        fc = freq *  FC_SCALER;                             
        freq2 = freq*freq;
        freq3 = freq2 * freq;      
        float qa = 0.998f + 3.5e-5f * freq - 8e-10f * freq2 - 4e-14f * freq3; 
        K = 4.0f * res * qa;       
        float fa = 1.0036f - 2e-5f *freq + 1e-9f*freq2   - 1.75e-14f * freq3;  
        Gx2Vt = float((1.0f - exp(-fc * fa)) * VTx2);                                      
    }
    
    void AudioFilterLadder2::frequency(float c)
    {
        if (c < 1) c = 1;
        if (c > MAX_FREQUENCY) c = MAX_FREQUENCY;  
        targetFbase = c; 
        //Fbase = c;   
    }
    
    void AudioFilterLadder2::resonance(float res)
    {  
        res *= MAX_RESONANCE;                                 // normalize so host specifies 0-1 rather than 0-1.015
        if (res > MAX_RESONANCE) res = MAX_RESONANCE ;
        if (res < 0.0f) res = 0.0f;
        Qbase = res;
    }
    
    void AudioFilterLadder2::octaveControl(float octaves)
    {
      if (octaves > 10.0f) {             // increased range compared to previous filters to span full audio range with 0-1 envelope
        octaves = 10.0f;
      } else if (octaves < 0.0f) {
        octaves = 0.0f;
      }
      octaveScale = octaves / 32768.0f;
    }
    
    static inline float fast_tanh(float x)
    {
      if(x>3) return 1;
      if(x<-3) return -1;
      float x2 = x * x;
      return x * (27.0f + x2) / (27.0f + 9.0f * x2);
    }
    
    bool AudioFilterLadder2::resonating()
    {
      for (int i=0; i < 4; i++) {
        if (fabsf(z0[i]) > 0.0001f) return true;
        if (fabsf(z1[i]) > 0.0001f) return true;
      }
      return false;
    }
    
    void AudioFilterLadder2::update(void)
    {
      audio_block_t *blocka, *blockb, *blockc;
      float ftot, qtot;
      bool FCmodActive = true;
      bool QmodActive = true;
    
          blocka = receiveWritable(0);
          blockb = receiveReadOnly(1);
          blockc = receiveReadOnly(2);
          if (!blocka) {
            if (resonating()) {
              // When no data arrives but the filter is still
              // resonating, we must continue computing the filter
              // with zero input to sustain the resonance
              blocka = allocate();
            }
            if (!blocka) {
              if (blockb) release(blockb);
              if (blockc) release(blockc);
              return;
            }
            for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) {
              blocka->data[i] = 0;
            }
          }
          if (!blockb) {
            FCmodActive = false;
            ftot = Fbase;
          }
          if (!blockc) {
            QmodActive = false;
            qtot = Qbase;
          }
          float blockOut[AUDIO_BLOCK_SAMPLES]; 
          
          if (polyOn == true) {
            float blockIn[AUDIO_BLOCK_SAMPLES];   
            float blockOS[fI_NUM_SAMPLES], blockOutOS[fI_NUM_SAMPLES];                
            for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++)
              blockIn[i] = blocka->data[i]  * overdrive *  float(INTERPOLATION /32768.0f);
            arm_fir_interpolate_f32(&interpolation, blockIn, blockOS, AUDIO_BLOCK_SAMPLES);             
            for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) {                   
              Fbase = FcPorta * Fbase + (1-FcPorta) * targetFbase; 
              ftot = Fbase;            
              if (FCmodActive) {
                float FCmod = blockb->data[i] * octaveScale;
                ftot = Fbase * fast_exp2f(FCmod);                    
              }
              if (QmodActive) {
                float Qmod = blockc->data[i] * (1.0f/32768.0f);
                qtot = Qbase + Qmod;
              }            
              compute_coeffs(ftot,qtot) ;                          
              for(int os = 0; os < INTERPOLATION; os++)  
              {      
                float input = blockOS[i * INTERPOLATION + os];                            
                filter_y1 = filter_y1 + Gx2Vt * (fast_tanh((input - K * filter_out) * inv2VT) - save_tan1);
                save_tan1=  fast_tanh(filter_y1 * inv2VT);                  
                filter_y2 = filter_y2 + Gx2Vt * (save_tan1 - save_tan2);
                save_tan2 = fast_tanh(filter_y2 * inv2VT);                  
                filter_y3 = filter_y3 + Gx2Vt * (save_tan2 - save_tan3);  
                save_tan3 = fast_tanh(filter_y3 * inv2VT);                   
                filter_y4 = filter_y4 + Gx2Vt * (save_tan3 - fast_tanh(filter_y4 * inv2VT));                             
                filter_out = (filter_y4 + filter_y5) * 0.5f;
                filter_y5 = filter_y4;   
                blockOutOS[i*INTERPOLATION + os] = filter_out;
              }      
            }
            arm_fir_decimate_f32(&decimation, blockOutOS, blockOut, fI_NUM_SAMPLES);  
          }     
          else {         
            for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) {                  
              Fbase = FcPorta * Fbase + (1-FcPorta) * targetFbase;   
              if (FCmodActive) {
                float FCmod = blockb->data[i] * octaveScale;
                ftot = Fbase * fast_exp2f(FCmod);                    
              }
              if (QmodActive) {
                float Qmod = blockc->data[i] * (1.0f/32768.0f);
                qtot = Qbase + Qmod;
              }            
              compute_coeffs(ftot,qtot) ;                        
              float total = 0.0f;
              float interp = 0.0f;
              float input = blocka->data[i] * overdrive * (1.0f/32768.0f);
              for (int os = 0; os < INTERPOLATION; os++) {
                float inputOS = (interp * oldinput + (1.0f - interp) * input); 
                filter_y1 = filter_y1 + Gx2Vt * (fast_tanh((inputOS - K * filter_out) * inv2VT) - save_tan1);
                save_tan1=  fast_tanh(filter_y1 * inv2VT);                  
                filter_y2 = filter_y2 + Gx2Vt * (save_tan1 - save_tan2);
                save_tan2 = fast_tanh(filter_y2 * inv2VT);                  
                filter_y3 = filter_y3 + Gx2Vt * (save_tan2 - save_tan3);  
                save_tan3 = fast_tanh(filter_y3 * inv2VT);                   
                filter_y4 = filter_y4 + Gx2Vt * (save_tan3 - fast_tanh(filter_y4 * inv2VT));                             
                filter_out = (filter_y4 + filter_y5) * 0.5f;
                filter_y5 = filter_y4;   
                total += filter_out * (1.0f / (float)INTERPOLATION);
                interp += (1.0f / (float)INTERPOLATION);            
              } 
              blockOut[i] = total;  
              oldinput = input;
            }      
          }                 
          float absblk;
          for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) {  
            absblk = 1.25f * blockOut[i];
            if(absblk<0) absblk=-absblk;        
            if(absblk > 1)
               if(absblk > peak)
                  peak = absblk;
          }     
          for (int i=0; i < AUDIO_BLOCK_SAMPLES; i++) {         
            if(peak>1){ 
              pgain = 0.99f * pgain + 0.01f/peak;    // fast attack    
              peak *=0.99995f;
            }   
            blocka->data[i] = blockOut[i]  * pgain * float(0.85f * float(32767.0));  
          } 
          transmit(blocka);
          release(blocka);
          if (blockb) release(blockb);
          if (blockc) release(blockc);
    }
    filter_ladder2.h
    Code:
    /* Audio Library for Teensy, Ladder Filter
     * Copyright (c) 2021, Richard van Hoesel
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy
     * of this software and associated documentation files (the "Software"), to deal
     * in the Software without restriction, including without limitation the rights
     * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     * copies of the Software, and to permit persons to whom the Software is
     * furnished to do so, subject to the following conditions:
     *
     * The above copyright notice, development funding notice, and this permission
     * notice shall be included in all copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
     * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
     * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
     * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
     * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
     * THE SOFTWARE.
     */
    
    //-----------------------------------------------------------
    // AudioFilterLadder2 for Teensy Audio Library
    // Based on Huovilainen's full model, presented at DAFX 2004
    // Richard van Hoesel, March. 11, 2021
    // v.1.02 includes linear interpolation option
    // v.1.01 Adds a "resonanceLoudness" parameter (1.0 - 10.0)
    //        to control resonance loudness 
    // v.1.0 Uses 2x oversampling. Host resonance range is 0-1.0. 
    //
    // please retain this header if you use this code.
    //-----------------------------------------------------------
    
    // https://forum.pjrc.com/threads/60488?p=269609&viewfull=1#post269609
    
    #ifndef filter_ladder2_h_
    #define filter_ladder2_h_
    
    #include "Arduino.h"
    #include "AudioStream.h"
    #include "arm_math.h"
    #include <Audio.h>
    #define MOOG_PI ((float)3.14159265358979323846264338327950288)  
    
    class AudioFilterLadder2: public AudioStream
    {
    public:
      AudioFilterLadder2() : AudioStream(3, inputQueueArray) { initpoly(); };
      void frequency(float FC);
      void resonance(float reson);
      void octaveControl(float octaves);
      void inputDrive(float drive);
      void portamento(float pcf);   
      void compute_coeffs(float freq, float res);  
      void initpoly(); 
      void interpolationMethod(AudioFilterLadderInterpolation im);
      void resonanceLoudness(float v);  
      virtual void update(void);
      
    private:
      static const int INTERPOLATION = 2;
      static const int finterpolation_taps = 32;
      static const float FC_SCALER = 2.0f * MOOG_PI / (INTERPOLATION * AUDIO_SAMPLE_RATE_EXACT);
      float finterpolation_state[(AUDIO_BLOCK_SAMPLES-1) + finterpolation_taps / INTERPOLATION];
      arm_fir_interpolate_instance_f32 interpolation;
      float fdecimation_state[(AUDIO_BLOCK_SAMPLES*INTERPOLATION-1) + finterpolation_taps];
      arm_fir_decimate_instance_f32 decimation;   
      static float finterpolation_coeffs[finterpolation_taps];
      
      float Gx2Vt, K, filter_res, filter_y1, filter_y2, filter_y3, filter_y4, filter_y5, filter_out; 
      float VTx2 = 5; 
      float inv2VT = 1.0/VTx2;
      float Fbase=1000, Qbase=0.5;
      float overdrive=1;
      float ftot=Fbase, qtot=Qbase;
      bool  resonating();
      bool  polyCapable = false;
      bool  polyOn = false;    // FIR is default after initpoly()
      float octaveScale = 1.0f/32768.0f;
      float z0[4] = {0.0, 0.0, 0.0, 0.0};
      float z1[4] = {0.0, 0.0, 0.0, 0.0};
      float pbg=0;
      float save_tan1=0;
      float save_tan2=0;
      float save_tan4=0;
      float save_tan3=0;
      float peak=1.0;
      float pgain=1.0;
      float targetFbase = 1000;
      float FcPorta = 0;   
      float oldinput=0;  
      audio_block_t *inputQueueArray[3];
    };
    
    #endif

  5. #205
    I want to experiment with the Audio Library version of this filter. As suggested by Paul in message #50 of this thread, I downloaded a zip version of https://github.com/PaulStoffregen/Audio and unzipped it to {Documents}/Arduino/libraries but it wasn't successful. I tried to run the example in message #50 and it didn't recognize the new filter class.

    Is there a better way to get the ladder filter into my Arduino 1.8.13 environment?

  6. #206
    Senior Member
    Join Date
    Jul 2020
    Posts
    1,862
    I'd put it in your sketchbook/libraries - check the compiler versbose output as this should confirm which libraries are
    being used.

  7. #207
    The sketches on my windows 10 machine are located in {Documents}/Arduino/libraries. That is where I put it. The compiler said it found two Audio.h files and it automatically chose the original Audio.h.

    I did finally got it to work by temporarily changing the name of the ladder filter Audio.h to temp_Audio,h and used #include temp_Audio.h in the sketch. I thought that if I put it in {Documents}/Arduino/libraries it was supposed to take precedence, but I guess that was an incorrect assumption.

  8. #208
    I am running my project at 96000 Hz. Before I proceed I want to check an assumption I am making.

    If, for example, I run the four stage ladder filter AudioFilterLadder::update() at a 88000 Hz rate and if I modify the ladder filter code to eliminate any interpolation, is that equivalent to running the ladder filter at 44000 Hz with INTERPOLATION = 2?

  9. #209
    Junior Member Khorus's Avatar
    Join Date
    Mar 2022
    Location
    Montreal, Quebec
    Posts
    1
    Hello good people,

    I'm from the Axoloti world and just received my Teensy 4.0 board with the Audio Shield. Having fun testing out the capabilities and I'm impressed so far. I'd like to "port" a project of mine that's a Moog Taurus Bass Pedal emulator. I started this project as a Pure-Data model a few years back and I did use a model of Antti Huovilainen's Moog filter. It is excellent! Now, imagine my excitement when I discovered your thread!

    Now, I've played with the "Ladder" filter that's included in the Audio library but I can't seem to figure out how can I use the updated (full model) version of this filter. Maybe I'm using the wrong dev tools? I'm using the basic Arduino/Teensyduino IDE. Thanks in advance guys and sorry for reviving an old thread.

    Marc aka Khorus

    PS: For the curious mind, here's a video of my current Taurus emulator that runs on Axoloti Core. https://www.facebook.com/727063099/v...0096457328671/

  10. #210
    Senior Member
    Join Date
    Feb 2021
    Posts
    172
    My full model version of the filter isn't available through the standard audio library. You will have to copy the cpp and h files from my post #204 above and include them manually, either as stand alone files in your project directory (probably the easiest solution), or else incorporate them into your local version of the audio library.

    Cheers,
    Richard

  11. #211
    Richard, I'm using your filter and it sounds amazing. It growls gracefully, my respect!

    Have you ever looked into the following paper, written by
    Stefan D'Angelo (Arturia's VA plugins & Neural DSP) and the legendary Vesa Välimäki?

    An improved virtual analog model of the Moog ladder filter
    Abstract:
    In this paper we derive a novel circuit-based model for the Moog filter and discretize it using the bilinear transform. The proposed nonlinear digital filter compares favorably against Huovilainen's model, which is the best previous white-box model for the Moog filter. The harmonic distortion characteristics of the proposed model match closely with those of a SPICE simulation. Furthermore, the novel model realistically enters the self-oscillation mode and maintains it. The proposed model requires only 12 more basic operations per output sample than Huovilainen's model, but includes the same number of nonlinear functions, which dominate the computational load. The novel nonlinear digital filter is applicable in virtual analog music synthesis and in musical audio effects processing.

  12. #212
    Senior Member
    Join Date
    Feb 2021
    Posts
    172
    Quote Originally Posted by deenigewouter View Post
    Richard, I'm using your filter and it sounds amazing. It growls gracefully, my respect!

    Have you ever looked into the following paper, written by
    Stefan D'Angelo (Arturia's VA plugins & Neural DSP) and the legendary Vesa Välimäki?

    An improved virtual analog model of the Moog ladder filter
    Abstract:
    Yes, I've looked at Stefan's papers in the past. I was hoping to get around to trying out some of his ideas but haven't yet, and they're not trivial to implement if I remember correctly.

Posting Permissions

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