Modulated Delay: Chorus/Flanger

Status
Not open for further replies.

C0d3man

Well-known member
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: View attachment Test_ModulatedDelay.zip (outputs a modulated 1000Hz sine)
 
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: View attachment 16745 (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! :)
 
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/~fisher/mkfilter/trad.html

I'll post my results here then.

Thanks, Holger
 
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/DSP/html/group__BiquadCascadeDF1.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
 
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.
 
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/DSP/html/group__BiquadCascadeDF1.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
 
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
 
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.
 
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
 
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 = 16384 + (delayBuffer[offsetIndex1] >> 2) + (delayBuffer[offsetIndex2] >> 2) + (delayBuffer[offsetIndex3] >> 2);


Regards, Holger
 
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 = 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.
 
I think I have fixed my code (have to test it the next days) so my crackling noise in the background is away...

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
 
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.
 
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.
 
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
 
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:
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.
 
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
 
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
 
Status
Not open for further replies.
Back
Top