/* 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 "limits.h"
# include "effect_modulated_delay.h"
# include "spline.h"
# include "config.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 ;
_circ_idx = 0 ;
if ( delayline = = NULL ) {
return ( false ) ;
}
if ( d_length < 10 ) {
return ( false ) ;
}
_delayline = delayline ;
_delay_length = _max_delay_length = d_length ;
return ( true ) ;
}
void AudioEffectModulatedDelay : : update ( void )
{
audio_block_t * block ;
audio_block_t * modulation ;
int16_t * bp ;
int16_t * mp ;
float mod_idx ;
if ( _delayline = = NULL )
return ;
block = receiveWritable ( 0 ) ;
modulation = receiveReadOnly ( 1 ) ;
# ifdef INTERPOLATE_MODE
int8_t j ;
float x [ INTERPOLATION_WINDOW_SIZE ] ;
float y [ INTERPOLATION_WINDOW_SIZE ] ;
Spline s ( x , y , INTERPOLATION_WINDOW_SIZE , INTERPOLATE_MODE ) ;
# endif
bp = block - > data ;
mp = modulation - > data ;
if ( block & & modulation )
{
float mod_number ;
float mod_fraction ;
for ( uint16_t i = 0 ; i < AUDIO_BLOCK_SAMPLES ; i + + )
{
// write data into circular buffer
if ( _circ_idx > = _delay_length )
_circ_idx = 0 ;
_delayline [ _circ_idx ] = * bp ;
// Calculate modulation index as a float, for interpolation later.
// The index is located around the half of the delay length multiplied by the current amount of the modulator
/**************************************************************************************************************
Use an IIR filter on the modulation for avoiding aliasing ( http : //www-users.cs.york.ac.uk/~fisher/mkfilter/) */
# if defined(FILTER_IIR_ORDER_1)
/* Digital filter designed by mkfilter/mkshape/gencode A.J. Fisher
Command line : / www / usr / fisher / helpers / mkfilter - Bu - Lp - o 1 - a 1.1333499558e-01 0.0000000000e+00 - l
Parameters : filtertype = Butterworth
passtype = Lowpass
ripple =
order = 1
samplerate = 44117.64706
corner1 = 5000
corner2 =
adzero =
logmin =
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# define NZEROS 1
# define NPOLES 1
# define GAIN 3.688918967e+00
static float xv [ NZEROS + 1 ] , yv [ NPOLES + 1 ] ;
xv [ 0 ] = xv [ 1 ] ;
xv [ 1 ] = ( float ( * mp ) / SHRT_MAX ) / GAIN ;
yv [ 0 ] = yv [ 1 ] ;
yv [ 1 ] = ( xv [ 0 ] + xv [ 1 ] ) + ( 0.4578357460 * yv [ 0 ] ) ;
//new_value = yv[1];
mod_idx = yv [ 1 ] * float ( _delay_length > > 1 ) ;
# elif defined(FILTER_IIR_ORDER_3)
/* Digital filter designed by mkfilter/mkshape/gencode A.J. Fisher
Command line : / www / usr / fisher / helpers / mkfilter - Bu - Lp - o 3 - a 1.1333333333e-01 0.0000000000e+00 - l
raw alpha1 = 0.1133333333
raw alpha2 = 0.1133333333
warped alpha1 = 0.1183783855
warped alpha2 = 0.1183783855
gain at dc : mag = 4.028005941e+01 phase = 0.0000000000 pi
gain at centre : mag = 2.848230315e+01 phase = - 0.7500000000 pi
gain at hf : mag = 0.000000000e+00
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
# define NZEROS 3
# define NPOLES 3
# define GAIN 4.028005941e+01
static float xv [ NZEROS + 1 ] , yv [ NPOLES + 1 ] ;
xv [ 0 ] = xv [ 1 ] ; xv [ 1 ] = xv [ 2 ] ; xv [ 2 ] = xv [ 3 ] ;
xv [ 3 ] = ( float ( * mp ) / SHRT_MAX ) / GAIN ;
yv [ 0 ] = yv [ 1 ] ; yv [ 1 ] = yv [ 2 ] ; yv [ 2 ] = yv [ 3 ] ;
yv [ 3 ] = ( xv [ 0 ] + xv [ 3 ] ) + 3 * ( xv [ 1 ] + xv [ 2 ] )
+ ( 0.2323461955 * yv [ 0 ] ) + ( - 1.0299524229 * yv [ 1 ] )
+ ( 1.5989967885 * yv [ 2 ] ) ;
//new_value = yv[3];
mod_idx = yv [ 3 ] * float ( _delay_length > > 1 ) ;
/************************************************************************/
# endif
mod_fraction = modff ( mod_idx , & mod_number ) ;
# ifdef INTERPOLATE_MODE
// Generate a an array with the size of INTERPOLATION_WINDOW_SIZE of x/y values around mod_idx for interpolation
uint8_t c ;
int16_t c_mod_idx = _circ_idx - int ( round ( mod_idx ) ) ; // This is the pointer to the value in the circular buffer at the current modulation index
for ( j = ~ ( INTERPOLATION_WINDOW_SIZE > > 1 ) | 0x01 , c = 0 ; j < = INTERPOLATION_WINDOW_SIZE > > 1 ; j + + , c + + ) // only another way to say: from -INTERPOLATION_WINDOW_SIZE/2 to INTERPOLATION_WINDOW_SIZE/2
{
int16_t jc_mod_idx = ( c_mod_idx + j ) % _delay_length ; // The modulation index pointer plus the value of the current window pointer
if ( jc_mod_idx < 0 )
y [ c ] = float ( _delayline [ _delay_length + jc_mod_idx ] ) ;
else
y [ c ] = float ( _delayline [ jc_mod_idx ] ) ;
x [ c ] = float ( j ) ;
}
* bp = int ( round ( s . value ( mod_fraction ) ) ) ;
# else
// Simple interpolation
int16_t c_mod_idx = ( _circ_idx - int ( round ( mod_idx ) ) ) % _delay_length ;
float value1 , value2 ;
if ( c_mod_idx < 0 )
{
value1 = _delayline [ _delay_length + c_mod_idx ] ;
value2 = _delayline [ _delay_length + c_mod_idx - 1 ] ;
}
else
{
value1 = _delayline [ c_mod_idx ] ;
value2 = _delayline [ c_mod_idx - 1 ] ;
}
* bp = mod_fraction * value1 + ( 1.0 - mod_fraction ) * value2 ;
# endif
bp + + ; // next audio data
mp + + ; // next modulation data
_circ_idx + + ; // next circular buffer index
}
}
if ( block )
{
transmit ( block , 0 ) ;
release ( block ) ;
}
if ( modulation )
release ( modulation ) ;
}
void AudioEffectModulatedDelay : : setDelay ( float milliseconds )
{
_delay_length = min ( AUDIO_SAMPLE_RATE * milliseconds / 500 , _max_delay_length ) ;
}