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

Thread: Stereo Plate Reverb for Teensy4.x

  1. #1
    Junior Member
    Join Date
    Jul 2017
    Posts
    18

    Stereo Plate Reverb for Teensy4.x

    Here is something i have been working recently and already used it in a few projects:
    a new implementation of a plate type reverb sound for Teensy4.X.
    It's an allpass based reverb using two LFOs to modulate the delay lines inside the reverb chain. The result is more spread, less ringing reverb tail.
    Internally all the calculations are done on float32_t, the i/o runs on int16, so it's compatible with a standard audio library.
    You can hear a sound sample here:

    https://soundcloud.com/hexeguitar/t40gfx-plate-reverb

    And the code can be found here:

    https://github.com/hexeguitar/t40fx

    I will be putting more new audio components there in the future.

    Reverb comes with a set of controls:
    size(float n) - will set the reverb time
    lowpass(float n) - controls the output lowpass filter. Often it is convenient to limit the high band in the reverb sound to make it sit better in the whole mix.
    hidamp(float n) - treble dampening control
    lodamp(float n) - bass dampening control

    I have written a simple example project following a typical mixing console scheme with reverb placed in an AUX loop. Each sound source can go into "Reverb Send" mixers and then the output of the reverb is mixed with the dry signals using final mixer4 stages:

    Click image for larger version. 

Name:	StereoPlateReverb.png 
Views:	63 
Size:	26.5 KB 
ID:	23042

    However, since i don't own the new audio adaptor rev D, but use my own audio board i wasn't able to test the example project on a real hardware. I'd appreciate letting me know if there are any problems.
    Otherwise, you can use the component as any other, just include the header, use AudioConnection to route the input and output stereo signals.

    One note about ram usage: as default (reverb is part of a larger project) i've put the buffers into DMAMEM to leave more DTCM ram for other things. In that case the buffers are declared outside the reverb class, so only one instance of reverb is allowed. Simply comment out the #REVERB_USE_DMAMEM inside the header file to move the variables back to DTCM.

    Hope you like it!
    Piotr

  2. #2
    Senior Member Blackaddr's Avatar
    Join Date
    Mar 2017
    Location
    Canada
    Posts
    300
    Looks pretty cool! I'll have to give this a try. What is the processor usage % on the T4?

  3. #3
    Senior Member
    Join Date
    Jun 2018
    Location
    USA
    Posts
    165
    Thank you for posting this Piotr! I loaded it into my project and it sounds great.

    I uploaded a sample vocal recording of it in action. To avoid copyright issues I found a vocal recording on archive.org.


  4. #4
    Junior Member
    Join Date
    Jul 2017
    Posts
    18
    Jay, that's a nice sample, thanks for posting.
    I noticed i should maybe reverse the "lowpass" operation. Right now the parameter controls the cutoff frequency, so lower settings give more darker sound. Which is actually consistent with "Tone" knob found on many FX. However the name could be understood as "amount of lowpass" and increasing the control should result in darkening the sound. What do you think?

    Currently i have the reverb working together with another new component, the IR convolution based guitar cabinet simulator:
    (reverb part starts at 1:38 https://youtu.be/0fVbkKVKO9M?t=98


    Looks pretty cool! I'll have to give this a try. What is the processor usage % on the T4?
    The processorUsageMax function reports about 5% for the reverb object. The included example prints out the RAM usage and the CPU load in 1 second intervals.

  5. #5
    Senior Member
    Join Date
    Jun 2018
    Location
    USA
    Posts
    165
    I noticed i should maybe reverse the "lowpass" operation. Right now the parameter controls the cutoff frequency, so lower settings give more darker sound. Which is actually consistent with "Tone" knob found on many FX. However the name could be understood as "amount of lowpass" and increasing the control should result in darkening the sound. What do you think?
    I'm not in tune with the standard here, but it felt right to me. Turning up the lowpass knob "did something". Turning it down seemed to "do less". lol.

  6. #6
    Junior Member
    Join Date
    Jul 2017
    Posts
    18
    The difference between the hidamp and lowpass is:
    - hidamp is a lowpass filter placed inside the reverb tank, causing an increasing treble loss as the reverb tail fades out,
    - lowpass is a static filter applied at the end of the algorithm, controls overall amount of treble in the reverb sound.

    I have just pushed an update changing the taper of the lowpass control to semi-log (n^3 approximated). It improves the control response, linear taper is not the best one for controlling the filter cutoff frequency.

  7. #7
    Senior Member
    Join Date
    Jun 2018
    Location
    USA
    Posts
    165
    Hello Piotr,

    Awesome work. Is it safe to say that the lodamp "minimizes" the effect of the room size and hidamp "minimizes the effect of the lowpass knob? In my video you see I turn up the room size and because it was getting loud I went over to the lodamp to turn that up (thus minimizing the "loudness"). Then the lopass and hidamp also work similarly - one is the antithesis of the other. Is that right?

    I guess it makes sense? When you turn the lowpass knob it opens up the highs, and then you can dampen the highs with the hidamp... Just my 2 cents, no problems here. I just might swap the order of my encoders. Reverb, Room Size, lodamp, lowpass, hidamp.

    Jay

  8. #8
    Member
    Join Date
    Sep 2019
    Location
    Sevilla, Spain
    Posts
    50
    Hi Pio, I'm trying to use this reverb on a project, but got an error, any idea about what is it? using vscode with platformio:
    Code:
    .pio/build/teensy41/src/Tukra_1.41.ino.cpp.o: In function `_GLOBAL__sub_I_myusb':
    Tukra_1.41.ino.cpp:(.text.startup._GLOBAL__sub_I_myusb+0x2450): undefined reference to `AudioEffectPlateReverb::AudioEffectPlateReverb()'
    collect2: error: ld returned 1 exit status
    *** [.pio/build/teensy41/firmware.elf] Error 1

  9. #9
    Senior Member Blackaddr's Avatar
    Join Date
    Mar 2017
    Location
    Canada
    Posts
    300
    Sounds like you aren't linking the proper object files in your project and it can't find the constructor function. I would recommend you try to use the Arduino IDE first, and when you get that working, you can figure out how to get it going in another IDE. If it works in Arduino IDE and doesn't work in another IDE, it's unlikely the author will be able to help much.

  10. #10
    Member
    Join Date
    Sep 2019
    Location
    Sevilla, Spain
    Posts
    50
    thanks for the advice

  11. #11
    Junior Member
    Join Date
    Jul 2017
    Posts
    18
    M4ngu
    make sure both of the files, effect_platervbstereo.h and effect_platervbstereo.cpp are in the same directory as your main file.
    And of course, there header is included properly:
    Code:
    #include "Audio.h"
    #include "effect_platervbstereo.h"
    If you can post the code, or a part of it maybe we can help further.

  12. #12
    Member
    Join Date
    Sep 2019
    Location
    Sevilla, Spain
    Posts
    50
    Quote Originally Posted by Pio View Post
    M4ngu
    make sure both of the files, effect_platervbstereo.h and effect_platervbstereo.cpp are in the same directory as your main file.
    And of course, there header is included properly:
    Code:
    #include "Audio.h"
    #include "effect_platervbstereo.h"
    If you can post the code, or a part of it maybe we can help further.
    thx dude, placed the .h and .cpp files in the 'src' folder (instead of the 'include' one) and compiled fine ;-)

  13. #13
    Member
    Join Date
    Sep 2019
    Location
    Sevilla, Spain
    Posts
    50
    Trying to reduce the minimum reverb size (because it'll be almost for percussion) I've modified those lines in the effect_platervbstereo.h, but maybe is not the right way to proceed, any advice would be appreciated
    Code:
        void size(float n)
        {
            n = constrain(n, 0.0, 1.0);
    
            //n = map (n, 0.0, 1.0, 0.2, rv_time_k_max);
            n = map (n, 0.0, 1.0, 0.02, rv_time_k_max);
    
            //float32_t attn = 0.5 * map(n, 0.0, rv_time_k_max, 0.5, 1.0);
            float32_t attn = 0.5 * map(n, 0.0, rv_time_k_max, 0.05, 1.0);
    
            AudioNoInterrupts();
            rv_time_k = n;
            input_attn = attn;
            AudioInterrupts();
        }

  14. #14
    Junior Member
    Join Date
    Jul 2017
    Posts
    18
    I'd leave the attn as it is. It is used to attenuate the input signal depending on the reverb time, creates more attenuation for long reverb tail to avoid clipping.
    The n calculation looks fine.
    However, for shorter "room" type reverbs, more often used for drums a different reverb algorithm would be better. I'd like to code a few more different reverbs (room, spring reverb emulation), just not sure when i'll have time for this.

  15. #15
    Member
    Join Date
    Sep 2019
    Location
    Sevilla, Spain
    Posts
    50
    thanks PIO, yes I know plate is not be the most appropriate type of reverb for drum sounds, but I've testing yours and it really sounds much better than the Teensy reverb object that was using before, nice stereo image and better 'integration' in the mix.

  16. #16
    Member
    Join Date
    Sep 2019
    Location
    Sevilla, Spain
    Posts
    50
    Hi again PIO, would like to know if is there a reason to use AudioNoInterrupts(); in the header file instead of __disable_irq();
    I though was not recommended to use it because of this:
    "The special AudioNoInterrupts() and AudioInterrupts() functions should NOT within your object's functions. These are meant to allow the Arduino sketch to disable audio library updates across multiple function calls, perhaps to several different objects. They work separate from __disable_irq()."
    https://www.pjrc.com/teensy/td_libs_...ewObjects.html

  17. #17
    Senior Member
    Join Date
    Oct 2016
    Posts
    185
    can it work for T 3.6 also?

  18. #18
    Junior Member
    Join Date
    Jul 2017
    Posts
    18
    M4ngu
    Hi again PIO, would like to know if is there a reason to use AudioNoInterrupts(); in the header file instead of __disable_irq();
    I might be wrong with my assumptions, but the thinking behind this was:
    __disable_irq() disables all interrupts, everything including USB, timers etc.
    AudioNoInterrupts(); disables the software interrupt only, where all the audio processing happens. The variables i'm updating within the disable/enable block are accessed in the AudioInterrupt only. So, that option seemed to be more correct. But i might be wrong here. It happens only in functions updating the parameters which are called from the main loop, never inside or called from the update function.

    danixdj
    can it work for T 3.6 also?
    I can't test it with T3.6 on hardware, but i was able to compile a basic sketch using the reverb object. Reverb requires about 125KB of ram, T3.6 has enough. Not sure about the load.
    To make it compile for T3.6 make sure the
    Code:
    //#define REVERB_USE_DMAMEM
    in the effect_platervbstereo.h is commented out.

  19. #19
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    24,005
    Should I merge this into the audio library? I see it already has the MIT license header.

    Would be nice to get it into 1.54-beta7 so more people can get access and test it... before a 1.54 release.


    On the matter of interrupts, within audio objects generally you should use __disable_irq() and of course disable for as short a time as possible. Even if you're only disabling the audio software interrupt, generally you should save AudioNoInterrupts() for users to employ within their Arduino sketch code rather than use it inside your audio object.

    The intended model is Arduino sketch code might wish to make changes to multiple audio objects and have all take effect on the next 128 sample update. For example, maybe the Arduino code turns on a pink noise source, configures an envelope to shape it into a sharp percussive sound, and configures your reverb to make it more "full" sounding, and sends it to a mixer which needs its gain setting updated so the sound is heard at the intended level (maybe based on a recent analogRead indicating how hard a piezo disc or other sensor was struck by a drum stick). That code would call AudioNoInterrupts() before making any of these changes. As it calls functions from the noise synth, envelope effect, your reverb, and the mixer, those function calls might use __disable_irq(), but the intended model is they do not call AudioNoInterrupts() and AudioInterrupts(). Only the user's Arduino sketch code is intended to call AudioInterrupts(), after it has finished configuring a group of audio objects. If your reverb calls AudioInterrupts() after it's done updating the setting, you might mistakenly cause the audio library to begin a 128 sample update before the user's intended mixer gain change is made. Then the user's scuplted-noise percussive sound could be perceived very differently if the first 2.9 ms are at the wrong level. This usage model is only guaranteed to let Arduino sketch code update multiple objects in unison if the objects use __disable_irq() and save AudioNoInterrupts() for the higher level Arduino sketch code.

    There are some exceptions to this general guideline, mostly revolving around objects which need to use other non-audio libraries to access storage media like the SD card. But for a "pure math" type object, definitely use __disable_irq() and save AudioNoInterrupts() for your user's discretion.

  20. #20
    Junior Member
    Join Date
    Jul 2017
    Posts
    18
    Thanks for the explanation Paul.
    I have fixed the code using the __disable/__enable macros. Also changed all the float constants to use "f" postfix to ensure they are single precision floats.

    And there is a new function, which imho might come handy in more complex projects: reverb bypass.
    When the bypass is enabled, the reverb will clear its buffers at the 1st iteration to avoid continuing the previous reverb tail when the engine is turned on again.
    When done it just returns from the update function saving the cycles for something else. This way there might be more CPU heavy processes used in alternate fashion, if RAM/FLASH space allows it, of course.
    Using a mixer to bypass the reverb when not in use still makes it crunch all the zeros. It is also a way to immediately reset the sound without waiting for the tail to fade away.

    Feel free to merge the code.

    I have been working on a spring reverb simulation lately. Still requires some work and for now it's mono only. Here is a sample:
    https://soundcloud.com/hexeguitar/te...-spring-reverb

  21. #21
    Junior Member
    Join Date
    Mar 2021
    Posts
    15
    Pio I just got your reverb working on my synth! It sounds absolutely delightful. Thank you very much.

Posting Permissions

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