Forum Rule: Always post complete source code & details to reproduce any issue!
Page 1 of 2 1 2 LastLast
Results 1 to 25 of 26

Thread: Teensy based multistage distortion modeling pedal

  1. #1
    Junior Member
    Join Date
    Apr 2017
    Posts
    17

    Teensy based multistage distortion modeling pedal

    Hello guys,

    I have been working since the arrival of my Teensy 3.5/3.6 on a guitar distortion/amp modeling code that is now sufficiently mature to share with you.
    The hardware implementation is not the greatest as it is just a bunch of components hooked together but it works pretty well in a typical hacker fashion...:

    - An high impedance input buffer for guitar signal: here DIY "Klon" type buffer = transparent opamp buffer with a little less than unity gain (see here : http://tagboardeffects.blogspot.ch/2...on-buffer.html ).
    - a MicroE WM8731 audi codec board but PJRC Audio board should work as well with a little bit more noise though.
    - A Teensy 3.5 (the one I use now) or 3.6 (better, but I fried mine...).
    - A 2.2'' 320x240 ILI9341 TFT for GUI.
    - Two latching foot switches: one for True bypass, the other for preset switching.
    - One simple switch to trigger preset or editing mode
    - 4x 100k linear rotary potentiometers for master volume, gain, treble and bass controls
    - one rotary encoder + switch for menu navigation and editing (type EC11 or similar).

    My quick and dirty development prototype pedal looks like this:
    Click image for larger version. 

Name:	TeensyDistPicture.jpg 
Views:	615 
Size:	73.7 KB 
ID:	10610

    My code is based on Teensy audio library and its 32bit floating point extension by Chipaudette (Openaudio arduino library).
    The distortion modeling implements the following elements :
    - a pre-eq & pre-impulse (FIR) filter for tone shaping before distortion
    - a compressor and noise gate (pre distortion)
    - up to 4 distortion stages (single or push-pull tube stage or waveshaper based stage) each with their own pre- and post gains as well as bias, low and high pass filters. All distortion and pre filters are implemented in 32bit floating point for maximum dynamics and distortion can run up to 2x oversampling with Hermite or cubic interpolation of the waveshaping tables on Teensy 3.5 in real time (86% peak cpu load with everything on). The code is based on the concept of independent waveshapers with their own filtering and coupled through adjustable high pass filters which allows to capture the dynamic excursion of input voltage (dynamic shift of bias/operating point).
    - a post FIR and post EQ for modeling a tone stack or final shaping of the sound.
    - optional support for USB audio input and output to act as a sound card (both mixed with physical inputs and outputs) and an option to "plot" the input/out (Arduino plotter tool) for debugging.
    - 4 banks of 4 dual (A/B) presets, for a total of 32 presets stored in Teensy's EEPROM

    The signal flow is shown in the picture below:
    Click image for larger version. 

Name:	TeensyDistSignalFlow.png 
Views:	355 
Size:	57.8 KB 
ID:	10611

    In terms of Audio processing objects, I have implemented those new elements (in floating point):
    - a single stage waveshaper distortion block with variable oversampling and different interpolation types (my first steps, unused in latest version)
    - a multistage distortion object with many waveshaping tables for tubes (12AX7, 12AT7, 12AU7, 6V6, KT88), and other classic mathematical waveshaping functions (hyperbolic tangent, cubic and asymmetric laws of Doidic et al, power or exponential laws, soft clipping). Each stage can be eitehr single ended or push-pull and bias can be adjusted on the fly to generate different levels of odd/even harmonics. The DSP code performance can be adjusted to select different types of table interpolation (linear, quadratic, cubic spline of Hermite), change the number of point of the wave tables or choose between 1x or 2x oversampling. All the code is floating point 32bit.
    - a simple compressor/noise gate block (noise gate needs to be fixed though).
    - an easy to use floating point IIR equalizer block with automatic calculation of coefficients for high or low pass, peak or shelving filters.

    The GUI code is relatively well separated from the processing and it implements a simple menu system with "bar" representation of parameter values for interactive parameter editing and a preset management system (not the cleanest code so might be difficult to port).

    The code is available here with some simple docs: https://github.com/jcugnoni/TeensyDist

    My short term plan is to add more waveshaping table for solid state stages and improve/add other impulse for improved tone shaping, and also implement a sag model for a more dynamic feel (you can already get some sag by using the compressor though: gain 2.5, threshold 0.4, attack 250ms, release 250ms). Another point could be to add a simple cabinet simulation and tone stack module as a cascade of IIR filters. At the moment, I prefer to use a Raspberry Pi 3 with jconvolver for high resolution (3k sample stereo) impulse response modeling of cabs... another project that I need to document.

    Longer term, I would like to further develop my Octave amp/pedal profiling & identification code to allow anyone to capture the impulse response and match the distortion characteristics of his own gear as other commercial products already do. A simple approach based on swept sine and harmonic content analysis seems to already work in some cases but I would like to be able to automate it and maybe implement it in the Teensy.

    Anyway, i hope that you will like it.
    Feel free to test, improve this code as you wish, or maybe if you want to to port this code to another architecture...
    I have not much time to maintain it but I will appreciate any feedback.

  2. #2
    Senior Member Blackaddr's Avatar
    Join Date
    Mar 2017
    Location
    Canada
    Posts
    203
    Looks pretty cool!

  3. #3
    Junior Member
    Join Date
    Apr 2017
    Posts
    17
    Thank you.

    Your guitar pedal board looks cool too. By the way, do you have any plan to sell PCB, kits or ready-made ones when it is finalized?

    In this case, i would be interested to buy one as it would be a much cleaner hardware implementation as my rough prototype and the code should be pretty straightforward to port as it uses the same codec.

    By the way, you mentioned in some of your posts that you managed to disable some on-board filters in the WM8731 and that it reduced noise significantly.
    Could you share the code to do that?
    As distortion uses very large gain factors, I am always looking for improvements to reduce the noise floor on the input..

  4. #4
    Senior Member Blackaddr's Avatar
    Join Date
    Mar 2017
    Location
    Canada
    Posts
    203
    When I buy PCBs they come in quantities of 10 so I'll likely build up a few and put them on Tindie in case anyone is interested.

    If you're into software guitar modelling you might enjoy a couple articles and blog posts I've thrown up on my website this year.

    www.blackaddr.com

  5. #5
    Senior Member Blackaddr's Avatar
    Join Date
    Mar 2017
    Location
    Canada
    Posts
    203
    Here's a code snippet to turn off the HP filter which was causing a lot of harmonic noise, just like the STGTL5000.

    Code:
    #define WM8731_REG_LLINEIN  0
    #define WM8731_REG_RLINEIN  1
    #define WM8731_REG_ANALOG 4
    #define WM8731_REG_DIGITAL  5
    
    // extend the the WM8731 base class to give our user code direct write access to the codec.
    class AudioControlWM8731User : public AudioControlWM8731
    {
      public:
        void writeI2C(unsigned int addr, unsigned int val);
    };
    
    void AudioControlWM8731User::writeI2C(unsigned int addr, unsigned int val)
    {
      write(addr, val);
    }
    
    // ...
    AudioControlWM8731User       wm8731_1; // instantiate the user class
    // ...
    
    void setup() {
        // ...
        wm8731_1.writeI2C(WM8731_REG_LLINEIN, 0x17); // set line input gain to 0db.
        wm8731_1.writeI2C(WM8731_REG_RLINEIN, 0x17);
        wm8731_1.writeI2C(WM8731_REG_ANALOG, 0x10); // enable DAC
        wm8731_1.writeI2C(WM8731_REG_DIGITAL, 0x1);    // disable HP filter
    }

  6. #6
    Member
    Join Date
    Nov 2015
    Location
    Norway
    Posts
    70
    Good work! Seems like you did a very good job with this. From where did you get the tube data?

  7. #7
    Junior Member
    Join Date
    Apr 2017
    Posts
    17
    @Blackaddr: Thanks for your answer and code. I will try it ASAP to see if it also improves the noise level in my case.

    @Omjanger: in fact, the current tube tables are adapted from those found in Guitarix source code, but for the future, I plan to add other tube types from Koren model parameters by either adapting Guitarix tool tube_transfer.py (https://github.com/unclechu/guitarix...be_transfer.py ) or by directly tabulating a tube stage in LTSpice ( something like http://www.duncanamps.com/technical/ltspice.html ).

  8. #8
    Member
    Join Date
    Nov 2015
    Location
    Norway
    Posts
    70
    ohh! Thats some nice code=) This gives a lot of opportunities indeed! And how does it sound compared to a real tube amp?

  9. #9
    Junior Member
    Join Date
    Apr 2017
    Posts
    17
    I will try to upload a few clips to give you an idea when I can find some time back home.

    I am not a great guitar player so if anyone has some free DI tracks to demonstrate some presets it could sound much better than me playing :-)

    In my opinion it does the Marshall crunch to high gain pretty well and can sound close to some of my pedals. One of the key to that is to properly capture the tone shaping / eq of the pedal/amp.

    For example I have measured the frequency response of my small Donner Morpher (Suhr Riot clone) and implemented as FIR in the code. With that , the modeled sound is pretty close to the original.
    Same thing with a Marshall tone stack FIR and 2 to 3 12ax7 stages + 1 push pull KT88 power stage, you can get rather close to a Marshall Plexi. Adding a Klon or Tube screamer FIR in front gives it a typical mid boost as expecfed.

    However it is currently more difficult to match a clean sparkly Fender sound or spongy Tweed .. to do that I should implement a bright cap (high pass filter) model as an option and implement a few other addition such as a tube rectifier sag model.

    Anyway the tones I get are definitely more dynamic and responsive than my Line 6 modeler and through a good set of impulse response it sounds pretty good to me. At least as good or better than my Blackstar Ht1 and close to the sound of my Marshall 18w clone recorded through a reactive load box and cabinet IR.

    The main drawback at the moment is that the noise floor remains a bit too high and that some inharmonic aliasing noise can still be heard at at the very end of the note decay.

  10. #10
    Member
    Join Date
    Nov 2015
    Location
    Norway
    Posts
    70
    sounds very promising yes! To be able to measure the impulse response is the key to any "black box" my teacher used to say=) Will try to play around with your code when I got some spare time...
    Didi you build the codec circuit yourself or is it on a pre fabricated board?

  11. #11
    Junior Member
    Join Date
    Apr 2017
    Posts
    17
    Actually, I used the MikroE WM8731 codec board (https://shop.mikroe.com/add-on-board...io-codec-proto) but it should also work with PJRC audio board (more noisy though).
    As I don't use any special features of the codec, using another board supported by the Teensy Audio lib would only require to adapt a few lines of code.
    The only custom module is a TL072 based guitar buffer (high input impedance) similar to http://tagboardeffects.blogspot.ch/2...on-buffer.html

  12. #12
    Junior Member
    Join Date
    Apr 2017
    Posts
    17
    Just a small update: I have uploaded on Github a first draft of a user manual as well as two example recordings... (sorry for my limited skills in terms of playing ;-)

  13. #13
    Senior Member Blackaddr's Avatar
    Join Date
    Mar 2017
    Location
    Canada
    Posts
    203
    I downloaded your git hub a couple days ago and ripped out just the Audio Library classes to try out on my custom board. It would be helpful if you arranged github to separate everything into a library that contains all the audio classes you write, and an example/application that is custom to your board. This makes it easier for stuff to be ported to other projects. Obviously the switch/knob/GUI processing only works as-is on your pedal/setup.

    Also, if you could provide some basic usage examples for your classes it would be helpful. Your manual looked to be a manual for your pedal, but what others may want is a manual for your 'library'. At the moment, I had to go trying to figure out how each classes API works by reading the code itself.

    I'm eager to try out the distortion effects as part of my VST rig.

  14. #14
    Junior Member
    Join Date
    Apr 2017
    Posts
    17
    I fully understand.
    Until now I developed all this for my own experimentation but it would certainly be more usefull as a library. I will try to do what you suggest and document each library class.

    Is it ok it if I develop a single example involving the whole process chain but without any gui (hard coded settings) ?

    The user's manual remain relevant to understand the underlying parameters and their typical usage / value range.

    You mentionned VST, I am also interested in that, but have no experience so far. Could you recommend a simple example that I could follow to begin in that domain.

    My idea would be, if time allows, to port that code and further extend it to a Raspberry Pi 3, and merge it with my cabinet simulator Picabsim.

    PS: I still need to try your trick to reduce noise. But thanks again for sharing the code

  15. #15
    Junior Member
    Join Date
    Apr 2017
    Posts
    17
    Hi again,
    just a quick update: I have added a first version of TeensyDist library with an implementation example showing how to model a Tube Screamer type pedal.
    The example is a first draft, it compiles well but I did not have the opportunity to do sound-check as I am away from my pedal.
    The code is on GitHub: https://github.com/jcugnoni/TeensyDi...master/Library

  16. #16
    Senior Member Blackaddr's Avatar
    Join Date
    Mar 2017
    Location
    Canada
    Posts
    203
    Quote Originally Posted by jcugnoni View Post
    Hi again,
    just a quick update: I have added a first version of TeensyDist library with an implementation example showing how to model a Tube Screamer type pedal.
    The example is a first draft, it compiles well but I did not have the opportunity to do sound-check as I am away from my pedal.
    The code is on GitHub: https://github.com/jcugnoni/TeensyDi...master/Library
    Sweet! I'll give it a go sometime in the next day or two.

  17. #17
    Junior Member
    Join Date
    Apr 2017
    Posts
    17
    Hi, all the codes, docs and examples are on https://github.com/jcugnoni/TeensyDist

  18. #18
    Member
    Join Date
    Mar 2014
    Location
    Übersee, Germany
    Posts
    40
    Hi.
    Great project. I found some really new effects in a pedal called Freqout Natural Feedback Creator.
    My first association was that it might be worthwile implementing something of this kind in a teensy guitar pedal.

  19. #19
    Senior Member
    Join Date
    Oct 2016
    Posts
    170
    It's possible to convert the tubes array for a standard waveshape object of the original paul's audio library?

    The values and the length aren't compatible to standard audio library and waveshape object... how can i do?

    Thank you

  20. #20
    Junior Member
    Join Date
    Apr 2017
    Posts
    17
    Hi
    The tube waveshaper table in TeensyDist library are float32 array with 2001 entries. Each entry is in the interval -/+32767.
    To use those tables in the regular waveshaper object you will need to:
    1) divide the table by 32767 to normalize the waveform within -/+1.0
    2) interpolate the table to one of the supported length used in the waveshaper filter i.e 1025 or 2049.
    I would recommend doing it in Octave with the function interp1(). Linear interpolation would probably be sufficient.

    However as Teensy audio lib waveshaper uses linear interpolation with 16bit arithmetic nor oversampling at runtime, you might get a lot of digital noise and artificial harmonics if you stack multiple waveshapers or use a lot of pre gains. This is the main reason why I developed my own code.

  21. #21
    Senior Member
    Join Date
    Oct 2016
    Posts
    170
    Quote Originally Posted by jcugnoni View Post
    Hi
    The tube waveshaper table in TeensyDist library are float32 array with 2001 entries. Each entry is in the interval -/+32767.
    To use those tables in the regular waveshaper object you will need to:
    1) divide the table by 32767 to normalize the waveform within -/+1.0
    2) interpolate the table to one of the supported length used in the waveshaper filter i.e 1025 or 2049.
    I would recommend doing it in Octave with the function interp1(). Linear interpolation would probably be sufficient.

    However as Teensy audio lib waveshaper uses linear interpolation with 16bit arithmetic nor oversampling at runtime, you might get a lot of digital noise and artificial harmonics if you stack multiple waveshapers or use a lot of pre gains. This is the main reason why I developed my own code.
    Thank you...

    Do you think that you code of waveshaper is compatible with the standard teensy dist audio library?

  22. #22
    @jcugnoni

    How did you fry your Teensy 3.6?

  23. #23
    Junior Member
    Join Date
    Apr 2017
    Posts
    17
    @daperl
    I had it soldered directly to Teensy audio board via header pins but then wanted to try other audio boards...
    ... it is actually very challenging to desolder about 40 pins two rows and I have probably overheated some components while trying do desolder it :-((

    @danixdj
    Actually Teensy Dist library is an addition to the Teensy Audio lib and to Chipaudette's OpenAudio extension.

    Some object like the single stage waveshaper in my lib are regular 16bit audio processing blocks and can be directly linked to Teensy audio lib objects. Some other like the multistage waveshaper filter use 32bit floating point and can be linked to regular Teensy audio object via type conversion filters (see Chipaudette's OpenAudio extension).

  24. #24
    Senior Member
    Join Date
    Oct 2016
    Posts
    170
    Quote Originally Posted by jcugnoni View Post
    1) divide the table by 32767 to normalize the waveform within -/+1.0
    2) interpolate the table to one of the supported length used in the waveshaper filter i.e 1025 or 2049.
    I would recommend doing it in Octave with the function interp1(). Linear interpolation would probably be sufficient.
    i have a table within -/+ 1 but i don't know what i must to do with interp1() function to crete a new 2049 array (now is 2001)

    Can you help me?

    Thank you

    Dani
    Last edited by danixdj; 09-17-2017 at 06:53 PM.

  25. #25
    Junior Member
    Join Date
    Apr 2017
    Posts
    17
    Hi

    First you need to copy/paste the desired column of the tube table in a text file without the {}. Then load the array in Matlab or Octave. I assume that the tube table is stored in array y of size 2001.
    You can generate a new array of size 2049 in the following way by interpolation using the commands:

    x=linspace(-1,1,2001);
    xnew=linspace(-1,1,2049);
    ynew=interp1(x,y,xnew)/32767;
    disp(ynew);

    The first two lines define the input values corresponding to the entries of the waveshaper.
    The 3rd line interpolate the y(x) relation for the new discretization ynew(xnew).
    The new table is also normalized to -1,1 range and then displayed in terminal.

    I hope it helps you!

    Cheers

Posting Permissions

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