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

Thread: Modulated Delay: Chorus/Flanger

  1. #1

    Modulated Delay: Chorus/Flanger

    Hello,

    i am trying to build a modulated delay for the audio library. The whole thing should work as a chorus/flanger.

    As base I used effect_delay.*. I added an input as modulation source. The audio data is written into a circular buffer (like the delay) and an index is calculated by means of the modulation signal, which gets a value from the circular buffer. I added a spline interpolation (found on the net), so that values between two index-values from the circular buffer can be adjusted by interpolation.

    In principle this works. But I have strange artifacts (cracking) always when the sign of the (sinus or triangle) wave of the modulation signal (with or without interpolation!) changes.

    After searching for hours, I can't find a cause. Maybe someone from the pros has an idea and can help?

    TIA, Holger

    P.S.: Complete test code for a Teensy with SGTL5000: Test_ModulatedDelay.zip (outputs a modulated 1000Hz sine)

  2. #2
    Senior Member Blackaddr's Avatar
    Join Date
    Mar 2017
    Location
    Canada
    Posts
    242

    Post

    Quote Originally Posted by C0d3man View Post
    Hello,

    i am trying to build a modulated delay for the audio library. The whole thing should work as a chorus/flanger.

    As base I used effect_delay.*. I added an input as modulation source. The audio data is written into a circular buffer (like the delay) and an index is calculated by means of the modulation signal, which gets a value from the circular buffer. I added a spline interpolation (found on the net), so that values between two index-values from the circular buffer can be adjusted by interpolation.

    In principle this works. But I have strange artifacts (cracking) always when the sign of the (sinus or triangle) wave of the modulation signal (with or without interpolation!) changes.

    After searching for hours, I can't find a cause. Maybe someone from the pros has an idea and can help?

    TIA, Holger

    P.S.: Complete test code for a Teensy with SGTL5000: Test_ModulatedDelay.zip (outputs a modulated 1000Hz sine)
    You can't use a raw triangle wave as a modulation source directly, it will cause glitching because a perfect triangle has infinite bandwidth due to discontinuities at the peaks/troughs. This infinite bandwidth causes you aliasing due to violating Nyquist. Your triangle wave must be band limited, i.e. rounded edges. You can manually create a smoothed triangle Waveform, or you can pass your LFO output through an IIR filtering processing. Lots of different ways to do it, you just need to always stay bandlimited in audio, everything very smooth, no sudden jumps of any kind! :-)

  3. #3

    Lightbulb

    Quote Originally Posted by Blackaddr View Post
    You can't use a raw triangle wave as a modulation source directly, it will cause glitching because a perfect triangle has infinite bandwidth due to discontinuities at the peaks/troughs. This infinite bandwidth causes you aliasing due to violating Nyquist. Your triangle wave must be band limited, i.e. rounded edges. You can manually create a smoothed triangle Waveform, or you can pass your LFO output through an IIR filtering processing. Lots of different ways to do it, you just need to always stay bandlimited in audio, everything very smooth, no sudden jumps of any kind! :-)
    Ok, you told me that a few months ago, but I don't understand it until now. I usually have to try something like this by myself and try to solve the individual problems one after the other.

    I have now put a simple IIR filter behind the modulation input and before calculating the modulation-index. Unfortunately I could not try it out or hear it yet. To calculate the coefficients for the IIR filter (and even to get simple C-code for it) the following page seemed to be very helpful: http://www-users.cs.york.ac.uk/~fish...lter/trad.html

    I'll post my results here then.

    Thanks, Holger

  4. #4
    Senior Member DD4WH's Avatar
    Join Date
    Oct 2015
    Location
    Central Europe
    Posts
    488
    Hi Holger,

    give this a try also, its a very nice filter designer and also plots the response and gives you the coefficients. Also helpful is the possibility to test the filters with different accuracies of your calculations and scalings (fixed point vs. floating point, 16bit vs 24bit etc.).

    http://www.iowahills.com/

    IIR code can be found there also, but maybe you would like to give the CMSIS library a try, which has highly optimized code for many types of filters:

    https://www.keil.com/pack/doc/CMSIS/...ascadeDF1.html

    And finally you could look at the code in the audio library, which also contains highly optimized code for fixed point implementation of IIR biquad filters. You can also find formulae for calculating coefficients on-the-fly for IIR biquads in that code.

    Have fun with the Teensy,

    Frank DD4WH

  5. #5
    Senior Member Blackaddr's Avatar
    Join Date
    Mar 2017
    Location
    Canada
    Posts
    242
    Quote Originally Posted by C0d3man View Post
    Ok, you told me that a few months ago, but I don't understand it until now. I usually have to try something like this by myself and try to solve the individual problems one after the other.
    Don't worry! We're hear to help.

    You might also want to try something dead simple first. Rather than designing a proper BiQuad IIR filter, what about just a 1st order IIR where you have

    Code:
    // Simple 1st order IIR, mix the new value with the old value
    float alpha = 0.9f; // 90% new value, 10% feedback
    filteredOutput = (newValue * alpha) + filteredOutput*(1 - alpha);
    When alpha = 1.0f, no filtering is performed, as you turn alpha down you are increasing the filter feedback making it more and more of a low pass filter. This will obviously modify the shape of the Triangle in an assymetric way, but it might provide enough smoothing to eliminate aliasing caused by this particular issue. Since the triangle shape has a large impact on how it will sound, you may want to replace it later with something more complex like a precomputed waveform table or apply higher order IIR filtering using BiQuads, etc.

  6. #6

    Quote Originally Posted by DD4WH View Post
    Hi Holger,

    give this a try also, its a very nice filter designer and also plots the response and gives you the coefficients. Also helpful is the possibility to test the filters with different accuracies of your calculations and scalings (fixed point vs. floating point, 16bit vs 24bit etc.).

    http://www.iowahills.com/

    IIR code can be found there also, but maybe you would like to give the CMSIS library a try, which has highly optimized code for many types of filters:

    https://www.keil.com/pack/doc/CMSIS/...ascadeDF1.html

    And finally you could look at the code in the audio library, which also contains highly optimized code for fixed point implementation of IIR biquad filters. You can also find formulae for calculating coefficients on-the-fly for IIR biquads in that code.

    Have fun with the Teensy,

    Frank DD4WH
    Many thanks @Frank! Very good information. Just tried a little bit around and learned much new things about this topic!

    Regards, Holger

  7. #7
    Quote Originally Posted by Blackaddr View Post
    Code:
    // Simple 1st order IIR, mix the new value with the old value
    float alpha = 0.9f; // 90% new value, 10% feedback
    filteredOutput = (newValue * alpha) + filteredOutput*(1 - alpha);
    When alpha = 1.0f, no filtering is performed, as you turn alpha down you are increasing the filter feedback making it more and more of a low pass filter. This will obviously modify the shape of the Triangle in an assymetric way, but it might provide enough smoothing to eliminate aliasing caused by this particular issue. Since the triangle shape has a large impact on how it will sound, you may want to replace it later with something more complex like a precomputed waveform table or apply higher order IIR filtering using BiQuads, etc.
    Many thanks!

    I have tried a few things and hope to have learned a lot in the meantime. Currently I use filter coefficents for a lowpass calculated with the Iowa-Hills-IIR-Filter-Designer, which I use with
    Code:
    arm_biquad_cascade_df1_f32(&filter, modulation_f32, modulation_f32, AUDIO_BLOCK_SAMPLES);
    for the modulation data (f=1000Hz)

    Strangely enough, I still have "click" noises. I think I have a problem accessing the ring buffer somewhere.

    One more question: If I should make a triangle waveform a bit smoother by lowpassing it, why not using a sine wave for modulation?

    Regards, Holger

  8. #8
    Senior Member Blackaddr's Avatar
    Join Date
    Mar 2017
    Location
    Canada
    Posts
    242
    Yes, use a sine wave. When I made my chorus I got it working glitch free with a sinewave first, then moved on to investigating ways of bandlimiting the other waveforms like triangle.

  9. #9
    Hi all,

    after weeks of testing I have no idea anymore for getting my modulated_delay working without aliases and I hope someone of the audio-pro's can help.

    First of all: My code is located at Codeberg inside the branch dev-modulated-delay. I have taken the chorus code with only one tap and added a modulation input ([1]). The rest of the code is a port of the MDA-EPiano, so you only need effect_modulated_delay.cpp and effect_modulated_delay.h:

    effect_modulated_delay.cpp:
    Code:
    /* Audio Library for Teensy 3.X
       Copyright (c) 2014, Pete (El Supremo)
       Copyright (c) 2019, Holger Wirtz
    
       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 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.
    */
    
    #include <Arduino.h>
    #include <Audio.h>
    #include "arm_math.h"
    #include "effect_modulated_delay.h"
    
    /******************************************************************/
    
    // Based on;      A u d i o E f f e c t D e l a y
    // Written by Pete (El Supremo) Jan 2014
    // 140529 - change to handle mono stream - change modify() to voices()
    // 140219 - correct storage class (not static)
    // 190527 - added modulation input (by Holger Wirtz)
    
    boolean AudioEffectModulatedDelay::begin(short *delayline, int d_length)
    {
    #if 0
      Serial.print(F("AudioEffectModulatedDelay.begin(Chorus delay line length = "));
      Serial.print(d_length);
      Serial.println(F(")"));
    #endif
    
      _delayline = NULL;
      _delay_length = 0;
      _delay_offset = 0.0;
      _cb_index = 0;
    
      if (delayline == NULL) {
        return (false);
      }
      if (d_length < 10) {
        return (false);
      }
    
      _delayline = delayline;
      _delay_length = d_length;
    
      set_modulator_filter_coeffs();
      modulator_filter_data = {1, modulator_filter_state, modulator_filter_coeffs};
    
      return (true);
    }
    
    void AudioEffectModulatedDelay::set_modulator_filter_coeffs(void)
    {
      // modulator filter
      // "IOWA Hills IIR Filter Designer 6.5", http://www.iowahills.com/8DownloadPage.html
      // Example: https://web.fhnw.ch/technik/projekte/eit/Fruehling2016/MuelZum/html/parametric_equalizer_example_8c-example.html
      // Coeeficients calculated with https://arachnoid.com/BiQuadDesigner/index.html
    
      // SR = 44110, Fc = 20 Hz; Q=0.707
      modulator_filter_coeffs[0] = 5.06973332e-7;  // b0
      modulator_filter_coeffs[1] = 1.01394666e-6;  // b1
      modulator_filter_coeffs[2] = modulator_filter_coeffs[0];  // b2
      modulator_filter_coeffs[3] = 1.99798478;  // -a1
      modulator_filter_coeffs[4] = -0.99798681;  // -a2
    }
    
    void AudioEffectModulatedDelay::update(void)
    {
      audio_block_t *block;
      audio_block_t *modulation;
    
      if (_delayline == NULL)
        return;
    
      block = receiveWritable(0);
      modulation = receiveReadOnly(1);
    
      if (block && modulation)
      {
        int16_t *bp;
        int16_t cb_mod_index_neighbor;
        float *mp;
        float mod_index;
        float mod_number;
        float mod_fraction;
        float modulation_f32[AUDIO_BLOCK_SAMPLES];
    
        bp = block->data;
        arm_q15_to_float(modulation->data, modulation_f32, AUDIO_BLOCK_SAMPLES);
        arm_biquad_cascade_df1_f32(&modulator_filter_data, modulation_f32, modulation_f32, AUDIO_BLOCK_SAMPLES);
        mp = modulation_f32;
    
        for (uint16_t i = 0; i < AUDIO_BLOCK_SAMPLES; i++)
        {
          // write data into circular buffer (delayline)
          if (_cb_index >= _delay_length)
            _cb_index = 0;
          _delayline[_cb_index] = *bp;
    
          // Calculate the modulation-index as a floating point number for interpolation
          mod_index = *mp * (1 - MODULATION_MAX_FACTOR) * _delay_length; // "(1 - MODULATION_MAX_FACTOR) * _delay_length" means: maximum bytes of modulation allowed by given delay length
          mod_fraction = modff(mod_index, &mod_number); // split float of mod_index into integer (= mod_number) and fraction part
    
          // calculate modulation index into circular buffer
          cb_mod_index = (_cb_index - (_delay_offset + mod_number));
          if (cb_mod_index < 0) // check for negative offsets and correct them
            cb_mod_index += _delay_length;
    
          if (cb_mod_index == 0)
            cb_mod_index_neighbor = _delay_length;
          else
            cb_mod_index_neighbor = cb_mod_index - 1;
    
          *bp = round(float(_delayline[cb_mod_index]) * mod_fraction + float(_delayline[cb_mod_index_neighbor]) * (1.0 - mod_fraction));
    
          // push the pointers forward
          bp++; // next audio data
          mp++; // next modulation data
          _cb_index++; // next circular buffer index
        }
      }
    
      if (modulation)
        release(modulation);
    
      if (block)
      {
        transmit(block, 0);
        release(block);
      }
    }
    
    float AudioEffectModulatedDelay::offset(float offset_value) // in ms
    {
      uint16_t offset_frames = (offset_value / 1000) * AUDIO_SAMPLE_RATE;
      if (offset_frames > _delay_length * MODULATION_MAX_FACTOR)
        _delay_offset = _delay_length * MODULATION_MAX_FACTOR;
      else if (offset_frames <= _delay_length * (1 - MODULATION_MAX_FACTOR))
        _delay_offset = _delay_length * (1 - MODULATION_MAX_FACTOR);
      else
        _delay_offset = offset_frames;
    
      return (offset_frames / AUDIO_SAMPLE_RATE * 1000);
    }
    effect_modulated_delay.h:
    Code:
    /* Audio Library for Teensy 3.X
       Copyright (c) 2014, Pete (El Supremo)
       Copyright (c) 2019, Holger Wirtz
    
       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 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.
    */
    
    #ifndef effect_modulated_chorus_h_
    #define effect_modulated_chorus_h_
    
    #include "Arduino.h"
    #include "AudioStream.h"
    
    #define MODULATION_MAX_FACTOR 0.5
    
    /*************************************************************************/
    //                A u d i o E f f e c t M o d u l a t e d D e l a y
    // Written by Pete (El Supremo) Jan 2014
    // 140219 - correct storage class (not static)
    // 190527 - added modulation input handling (Aug 2019 by Holger Wirtz)
    
    class AudioEffectModulatedDelay :
      public AudioStream
    {
      public:
        AudioEffectModulatedDelay(void):
          AudioStream(2, inputQueueArray)
        { }
    
        boolean begin(short *delayline, int delay_length);
        virtual void update(void);
        virtual float offset(float offset_value);
    
      private:
        void set_modulator_filter_coeffs(void);
        audio_block_t *inputQueueArray[2];
        int16_t *_delayline;  // pointer for the circular buffer
        uint16_t _cb_index;   // current write pointer of the circular buffer
        uint16_t _delay_offset; // number of samples for the read offset of the modulation inside the circular buffer
        uint16_t _delay_length; // calculated number of samples of the delay
        int16_t cb_mod_index; // current read pointer with modulation for the circular buffer
        arm_biquad_casd_df1_inst_f32 modulator_filter_data;
        float32_t modulator_filter_state[4];
        float32_t modulator_filter_coeffs[5];
    };
    #endif
    My first problem is: I am using "my" MIDI-EPiano for testing. The code is complex, but I have no idea what to use instead as sound generator for testing - a simple waveform generator?

    Next problem: clicking noise im the background: I tried to use a sine and a triangle wave with slow frequency (0.7 Hz) and a biquad filter behind, as modulation source Both waveforms are generating noise (clicking) in the background (hear demo sounds on SoundCloud).

    I tried to set the corner frequency for the modulator biquad to 20 Hz. It was getting better but not good - especially in the lower frequencies. I also took a look at other chorus implementations. They were only using a simple DC blocker or low-pass for the output. I also tried to filter the output with an additional biquad, but this won't help also. So I think I am doing something fundamental wrong - but after hours of trying and debugging I have absolute no idea anymore - maybe because I have less idea about the topic than is possibly necessary.

    Has anyone an idea what I am doing wrong? It would be so nice to have a cool chorus effect for the EPiano... it is the last piece of code what is missing for going onto stage with my Teensy :-/

    TIA, Holger

  10. #10
    I've got a modulated chorus, it may help to have a look: https://github.com/quarterturn/teensy3-ensemble-chorus. My screwup was failing to wrap around both ways for the index offset of the delay tap readouts.

    I'd love to have feedback on how to improve mine btw. I need to make it stereo-capable and convert the LFO from a pre-computed wavetable to floating point trig.

  11. #11
    Quote Originally Posted by quarterturn View Post
    I've got a modulated chorus, it may help to have a look: https://github.com/quarterturn/teensy3-ensemble-chorus. My screwup was failing to wrap around both ways for the index offset of the delay tap readouts.

    I'd love to have feedback on how to improve mine btw. I need to make it stereo-capable and convert the LFO from a pre-computed wavetable to floating point trig.
    Thanks for answering! I took a look into your code and have some (stupid?) questions:

    1.) Why do you use a zeroblock? Wouldn't it be easier to use memset() to fill the block with zeros?
    2.) Why do you have two loops for handling an audio block? You could use the audio block receiveWritable(0); and then put the modulated signal on it.
    3.) What does the magic 16384 in the following line do? outblock->data[i] = 16384 + (delayBuffer[offsetIndex1] >> 2) + (delayBuffer[offsetIndex2] >> 2) + (delayBuffer[offsetIndex3] >> 2);


    Regards, Holger

  12. #12
    Quote Originally Posted by C0d3man View Post
    Thanks for answering! I took a look into your code and have some (stupid?) questions:

    1.) Why do you use a zeroblock? Wouldn't it be easier to use memset() to fill the block with zeros?
    2.) Why do you have two loops for handling an audio block? You could use the audio block receiveWritable(0); and then put the modulated signal on it.
    3.) What does the magic 16384 in the following line do? outblock->data[i] = 16384 + (delayBuffer[offsetIndex1] >> 2) + (delayBuffer[offsetIndex2] >> 2) + (delayBuffer[offsetIndex3] >> 2);


    Regards, Holger
    1. I'm new to this, saw it elsewhere in an audio lib and it seemed to work. I will look into memset though.
    2. Maybe, though I need 512 bytes for the effect and a block is only 128, right?
    3. I push each sample readout two bits to the right (divide by four essentially) to make sure they don't clip when added; 16384 is just to fill in for the 'missing' fourth and bring the volume up.

  13. #13
    I think I have fixed my code (have to test it the next days) so my crackling noise in the background is away...

    Quote Originally Posted by quarterturn View Post
    1. I'm new to this, saw it elsewhere in an audio lib and it seemed to work. I will look into memset though.
    2. Maybe, though I need 512 bytes for the effect and a block is only 128, right?
    3. I push each sample readout two bits to the right (divide by four essentially) to make sure they don't clip when added; 16384 is just to fill in for the 'missing' fourth and bring the volume up.
    1.) I am also not an audio-programming-professional, so your solution might be a good (and faster) one instead of memset. It uses RAM, but I think it is faster.
    2.) Yes. But I think you can avoid allocating an extra audio block when using block = receiveWritable(0); and running only once through AUDIO_BLOCK_SAMPLES - I am doing this in my testcode and I am using input 1 as external modulation input.

    Code:
     void AudioEffectModulatedDelay::update(void)
    {
      audio_block_t *block;
      audio_block_t *modulation;
    
      if (_delayline == NULL)
        return;
    
      block = receiveWritable(0);
      modulation = receiveReadOnly(1);
    
      if (block && modulation)
      {
        int16_t *bp;
        int16_t cb_mod_index_neighbor;
        float *mp;
        float mod_index;
        float mod_number;
        float mod_fraction;
        float modulation_f32[AUDIO_BLOCK_SAMPLES];
    
        bp = block->data;
        arm_q15_to_float(modulation->data, modulation_f32, AUDIO_BLOCK_SAMPLES);
        mp = modulation_f32;
    
        for (uint16_t i = 0; i < AUDIO_BLOCK_SAMPLES; i++)
        {
          // write data into circular buffer (delayline)
          if (_cb_index >= _delay_length)
            _cb_index = 0;
          _delayline[_cb_index] = *bp;
    
          // Calculate the modulation-index as a floating point number for interpolation
          mod_index = *mp * (_delay_length >> 1);
          mod_fraction = modff(mod_index, &mod_number); // split float of mod_index into integer (= mod_number) and fraction part
    
          // calculate modulation index into circular buffer
          cb_mod_index = (_cb_index - ((_delay_length >> 1) + mod_number));
          if (cb_mod_index < 0) // check for negative offsets and correct them
            cb_mod_index += _delay_length;
    
            if (cb_mod_index == _delay_length - 1)
              cb_mod_index_neighbor = 0;
            else
              cb_mod_index_neighbor = cb_mod_index + 1;
    
          *bp = round(float(_delayline[cb_mod_index]) * mod_fraction + float(_delayline[cb_mod_index_neighbor]) * (1.0 - mod_fraction));
    
          // push the pointers forward
          bp++; // next audio data
          mp++; // next modulation data
          _cb_index++; // next circular buffer index
        }
      }
    
      if (modulation)
        release(modulation);
    
      if (block)
      {
        transmit(block, 0);
        release(block);
      }
    3.) If you add a constant you (IMHO) only add a DC part. I think this is wrong. I think you should do the following:

    Code:
             int32_t tmp=round(delayBuffer[offsetIndex1] + delayBuffer[offsetIndex2] + delayBuffer[offsetIndex3])/3.0);
             outblock->data[i] = int16_t(tmp);
    I am sure that there are much better ways with CMSIS functions, but the above way is more understanable for me :-)

    Regards, Holger

  14. #14
    I tried writing back to the block (having declared it as writeable) but couldn't get it to work, but I'll revisit it.

    I'm pretty sure it sounded louder with the constant in testing but logically I think you're right about it just creating a DC offset. I will try your suggestion on the audio level since 3.6 has floating point hardware.

  15. #15
    OK I have replaced the offset with the float divide by three and tweaked the LFO. I also added "stereo" by shifting the other channel LFO phase by 90 degrees.

    It sounds VERY lush now! I'm still going to tweak it a bit to see if I can get that last bit of springy phase shift you can hear in a real Lowrey "Symphonic Strings" BBD unit, but otherwise I very much like it.

  16. #16

    Quote Originally Posted by quarterturn View Post
    OK I have replaced the offset with the float divide by three and tweaked the LFO. I also added "stereo" by shifting the other channel LFO phase by 90 degrees.

    It sounds VERY lush now! I'm still going to tweak it a bit to see if I can get that last bit of springy phase shift you can hear in a real Lowrey "Symphonic Strings" BBD unit, but otherwise I very much like it.
    Hey, that's pretty cool!!!

    As a solution for the block = receiveWritable(0); you have to add your 3 signals to the original signal, perhaps like this:

    Code:
        int16_t *bp;
    ...
        block = receiveWritable(0)
        bp = block->data;
    ...
    <audio block loop>
             *bp=(*bp>>2)+(delayBuffer[offsetIndex1]>>2) + (delayBuffer[offsetIndex2]>>2) + (delayBuffer[offsetIndex3]>>2);
    ...
             bp++;
    </audio block loop>
    This sums up a quarter of the four signals. But perhaps it is better to not adding equal signal components. Maybe a factor for the original signal and the modulated signals will be better (but than the dividing by bit-shifting will not work anymore).

    Regards, Holger

  17. #17
    Hi @ll,

    after reading, testing and changing much of my code I am proud to say that I have a running modulated delay effect

    The current code with a simple example is located at https://codeberg.org/dcoredump/effect_modulated_delay. There may be some smaller problems but inside MicroMDAEPiano it works nicely.

    I hope someone has time to test and perhaps (if it is fully tested) Paul may put it into the Teensy audio-lib?

    Regards, Holger
    Last edited by C0d3man; 09-09-2019 at 10:33 AM.

  18. #18
    I did a quick test and it seemed to work for me. T4, WM8731, custom hardware. It sounded smooth without any of the artifacting of the stock delay effect.

    Great job, I say.

  19. #19

    Cool

    Quote Originally Posted by wcalvert View Post
    I did a quick test and it seemed to work for me. T4, WM8731, custom hardware. It sounded smooth without any of the artifacting of the stock delay effect.

    Great job, I say.
    Thanks for testing! I will try to add more taps and optimize a little bit the code. Because it fits into the Teensy audio stack you can try to add filters behind the modulated signal or use any waveform as modulator (perhaps also filtered).

    Regards, Holger

  20. #20
    Quote Originally Posted by C0d3man View Post
    Thanks for testing! I will try to add more taps and optimize a little bit the code. Because it fits into the Teensy audio stack you can try to add filters behind the modulated signal or use any waveform as modulator (perhaps also filtered).

    Regards, Holger

    I just forgot to mention: The code is using CMSIS (arm_q15_to_float()) for getting the modulator factor as a float. IMHO this will only work with T_3.5/T_3.6/T_4.0. I think I have to convert this to integer calculation and remove all floats for getting this effect into the Teensy audio library...

    Regards, Holger

Posting Permissions

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