n.b. Replaces Thread posted in other group !!

Although my guitar processor/effects unit is not complete, i have posted it here to solicit
helpful comments. I have been helped in the past by Houston, Paul at PJRC,
MichaelMeissner, Defragster et. al.
The 'front end' is an op amp circuit which i have used successfully many times before.
It is from an old book by R A Penfold on preamp an filter circuits.
I use the circuit as shown except with a single 9V battery and a more modern op amp
an LM 324 i think. The resistors set a gain of 10x. The output of this circuit feeds the
L & R line inputs of the Teensy Rev D audio shield. It works well but 3 questions :
1.Should a load resistor be placed across its output after the decoupling cap ?
2.Should there be any other capacitors in the circuit for noise removal or are
these unnecessary because the DC source is a battery ?
3.What is the low cutoff frequency of the input signal from the guitar and should there
be a high cut off filter to prevent aliasing at 44.1 kHz sampling when the FFT program is activated ?

The signal handling by the library routines is wonderful, CD quality, and there
are no timing issues.

I give the preamp circuit, an image of the unit, the sketch and the .cpp and .h
files for the 'enhance' object.
In all 5 pots are used. a button could be used to step through the effects
(0 to 4 are implemented - others TO DO will follow) or a single effect can be locked in.
The chorus algorithm is from the wiki explanation of chorus effects and for anyone
unfamiliar with the Savitsky-Golay least squares smoothing algorithm i would
recommend it to them. I have used it in DSP since the 60's - principally in
gas chromatography signal handling.

Pleased with the quality of the unit i would now like to build an onboard unit for my guitar
which transmits wirelessly from a preamp-audioshield-tennsy4 lineup to a remote
teensy receiver. i have ordered an EMB1061BLE bluetooth transmitter and several NRF24l01 2.4 GHZ
transceivers and would welcome advice on which to use and how to make the connection to the teensy 4.0
which will already have an audio shield connected to it.



Click image for larger version. 

Name:	effect unit.jpg 
Views:	13 
Size:	165.9 KB 
ID:	18489


Click image for larger version. 

Name:	Guitar preamp.jpg 
Views:	7 
Size:	150.5 KB 
ID:	18490

Savitsky-Golay coefficients
=================

Click image for larger version. 

Name:	SavitskyGolay.jpg 
Views:	21 
Size:	104.3 KB 
ID:	18491

SKETCH
=====


#include <Audio.h>
#include <Wire.h>
#include <SerialFlash.h>
#include "enhanced.h" // local onject


AudioInputI2S i2s1;

AudioMixer4 mixer1;
AudioEffectEnhanced enhanced1;
AudioOutputI2S i2s2;
AudioConnection patchCord1(i2s1, 0, mixer1, 0);
AudioConnection patchCord2(i2s1, 1, mixer1, 1);
AudioConnection patchCord3(mixer1, enhanced1);
AudioConnection patchCord4(enhanced1, 0, i2s2, 0);
AudioConnection patchCord5(enhanced1, 0, i2s2, 1);
AudioControlSGTL5000 sgtl5000_1;

// implemented effects
const int passthro=0;
const int fixedDuet=1;
const int varDuet=2;
const int echo=3;
const int clip=4;

// partially implemented effects TO DO
const int harmonic=5;
const int multichorus=6;
const int effectFFT=7;

const int npins=7; // at least pins 0 to 6 avail for digout
const int nanins=3,maxanins=5; // 5; when totally populated with pots
int pincount=0;

// these are the available analog input pin nos
int anins[maxanins]={15,22,16,14,17};
int pots[maxanins]={-100, // always volume
-100, // always mix
-100, // eg LFO
-100, // eg middelay
-100}; // eg depth
const int tol=2; // alloweable noise on pots before change recognised
const int quantise=25; // fsd for all pots

void setup()
{ Serial.begin(9600);
analogReadResolution(10);
//for (int pin=0;pin<npins;pin++) pinMode(pin, INPUT_PULLUP);
//for (int anin=0;anin<anins;anin++) pinMode(nanins[anin],analogInput);
AudioMemory(20);
sgtl5000_1.enable();
sgtl5000_1.volume(0.7);

mixer1.gain(0, 0.5);
mixer1.gain(1, 0.5);
// start the effect
enhanced1.begin();
for (int pin=0;pin<npins;pin++) pinMode(pin, OUTPUT);
}

void opPotVal( int potno,int value)
{
if (value>=0) {Serial.print(" Pot ["); Serial.print(potno);
Serial.print("] =>");Serial.print(value);}
}

void hint(char *heading, int pot1, int pot2, int pot3, int pot4)
{ Serial.print(heading); Serial.print(" suggest : " );
Serial.println("");
opPotVal(1,pot1); opPotVal(2,pot2);
opPotVal(3,pot3); opPotVal(4,pot4);
Serial.println("");
}

// Return effect no - either programmed in, eg 2=echo,
// or index incremented from contact closure of button.

int getnexteffect()
{return 2;} // 0=passthro 1=fixed duet 2=var duet 3=echo 4=clip

void loop() // cycle every 0.01 second
{
uint16_t mymaxsig;
const uint16_t fsd=0x7FFF;
int potval,nexteffect,lasteffect=-1;

while (true) // inner loop
{
for (int npot=0; npot<maxanins; npot++)
{ potval=analogRead(anins[npot])*quantise/1024;
if (abs(potval-pots[npot])>tol) pots[npot]=potval;
}

nexteffect=getnexteffect();
if (nexteffect!=lasteffect)
{lasteffect=nexteffect;
mymaxsig=enhanced1.readmaxsig(); // peak vumeter
Serial.print(" Peak Signal = ");Serial.print(mymaxsig); Serial.print(" => [");
Serial.print(mymaxsig*100/fsd);Serial.println(" %] ");
Serial.println(" = init");
enhanced1.initPots(pots,maxanins,tol); // also 3 & 4

switch (nexteffect)
{
case passthro : // effect=0 checked ok
hint(" => passthro",-1,-1,-1,-1);
enhanced1.passthrough();
break;

case fixedDuet : // effect=1
hint(" => duet fixed",64,-1,-1,12);
enhanced1.configureFixedDuet(2,0.05,8); // LFOsweep (secs),float midlag (secs),float LFOsweep, amplitudereduction of sine wave)
break;

case varDuet : // effect=2
hint(" => duet variable",68,4480,72,15);
enhanced1.configureVarDuet(2,0.05,8); // LFOsweep (secs),float midlag (secs),float LFOsweep, amplitudereduction of sine wave)
break;

case echo : // effect=3 done
hint(" => echo",-1,2,148,-1);
enhanced1.configureEcho(1,0.25,50); // n echos, tap delay secs, % retention
break;

case clip : // effect=4
hint(" => clip",10,-1,-1,-1);
enhanced1.clipat(30);
break;

case harmonic : // effect=5
hint(" => octave",0,-1,-1,-1);
enhanced1.setOctave(pots[2]); // mix=0 to quantise %
for (int pin=0;pin<npins;pin++) digitalWrite(pin,LOW);
break;

case multichorus : // effect=6 TO DO
Serial.println(" => multi chorus");
// set all 5 voices as for duet
for (int voice=0; voice<5; voice++) enhanced1.editvoice(voice,2,0.05,8);
enhanced1.editvoice(1,2.0,0.05,8);
enhanced1.editvoice(2,1.5,0.10,8);
enhanced1.editvoice(3,0.5,0.07,8);
enhanced1.editvoice(4,0.4,0.06,8);
//enhanced1.editvoice(5,1.7,0.15,2);
enhanced1.initMultichorus();
break;

case effectFFT : // effect=7
(" => FFT",-1,-1,-1,-1);
enhanced1.goFFT();
break;

} // end switch statement
} // end if new effect

enhanced1.refreshPots(pots,maxanins,tol); // copy 3 & 4 also
delay(10);
} // end true loop

}

.cpp file
=====

// effect_enhanced.cpp

#include <Arduino.h>
#include "enhanced.h"

bool sim = false; // false=read input from line inputs, true=read simulated input from sine table
int hop = 3;
const int quantise=25; // qhantisation of pot readings

bool SavitskyGolay = false, flip = false;
const uint fsd=0x7FFF; // full scale deflection
const int heartbeat = 13; // inbuilt LED pin no
const int qsize = 1 << 15; // length of main q which has to be modulo 2
const int qmask = qsize - 1; // mask for cyclic q

long q[qsize];
const int preludeblocks = (qsize / AUDIO_BLOCK_SAMPLES) + 1;
int qinpt = 0, lastqpt, lastsample, q2inpt = 0, q2outpt = 0;

int fillblockcount;
bool pulse = true;
int blockcount = 0;


// array for signal smoothing by Savitsky-Golay convolution
int x[7]; // 7 points for auto correlation
// x[3] is current point // 5 points x[1] -x[5] for Savitsky Golay smooth and 1st derivative
int ndelays, sampledelta;
bool delaysactive;

// pot parameters // lag in 1/quantise sec
volatile int vol=quantise,mix=quantise,clipamp=quantise,depth=q uantise,LFO=quantise;
volatile int lag=quantise,nechos=1,echoattenuation=quantise;
volatile int maxpots=3;
volatile int mystartpots[5];
volatile int mypots[5];
int mytol;

// octave data
bool isOctave = false, xpt = false;
int deadtime = 7, way, poolmatchindex, trypoolstart, threshold;
long long maxOverlapIntegral, OverlapIntegral;
int poolbasept, octpt, poolpt, slope, oldpoolpt, oldval, ototal, cycles;
int startwave, wavelength, peakwide,pmatchindex;
const int wavemin = 11;
const int poolpower = 13;
const int pooldim = 1 << poolpower; // pool for mixing of LFO in chorus effect
const int poolmask = pooldim - 1;
const int himargin = pooldim / 16, lomargin = pooldim / 16;

// isPassThrough data
boolean isPassThro=false;

// clip data
int cliplimit;
boolean isClip = false;

// echo data // enhanced1.configureecho(2,0.1,100);
bool isEcho = false;
long long total, delaypt;
int sustain = 100; //%

// chorus data //enhanced1.configurechorus(2,0.05,4);
bool isVarDuet = false;
long long _LFOmodulationfrequency = 88200; // 2 secs LFO
long long _middelay = 2205; // 1/20 sec average lag
int _chorusreduction = 4; // sine amplitude -250 to +250

// var chorus data //enhanced1.configurechorus(2,0.05,4);
bool isFixedDuet = false;
long long LFOmodulationfrequency = 88200; // 2 secs LFO
long long middelay = 2205; // 1/20 sec average lag
int chorusreduction = 4; // sine amplitude -250 to +250


// multi chorus data //enhanced1.configurechorus(2,0.05,4);
bool isMulti = false;
int mLFOmodulationfrequency[] = {88200, 88200, 88200, 88200, 88200}; // 2 secs LFO
int mmiddelay[] = {2205, 2205, 2205, 2205, 2205}; // 1/20 sec average lag
int mchorusreduction[] = {4, 4, 4, 4, 4}; // sine amplitude -250 to +250
int mLFOcount[] = {0, 0, 0, 0, 0};
// chorus working variables
int sintab[360];
int sinept = 0;
int LFOcount = 0;

// FFT data // goFFT
bool isFFTactive = false;
int nblocks = 0;
const int maxblocks = 6;
long long rFFTq[3][AUDIO_BLOCK_SAMPLES * 2];
long long iFFTq[3][AUDIO_BLOCK_SAMPLES * 2];
bool FFTqready[3];
const int power = 8;
const int n = 1 << power;
int bitrev[n];
int offset; // 0 or AUDIO_BLOCK_SAMPLES
int nblocksby2, outblocksby2;
int pulsecount = 0;

void muteEffects()
{
isEcho = false; isFixedDuet = false; isVarDuet=false; isMulti = false; isFFTactive = false;
isOctave = false; SavitskyGolay = false; isClip = false; isPassThro=false;
}

void AudioEffectEnhanced::initPots(int pots[],int npots,int tol)
{ // all pots[....] returning 0 to quantise vol,mix,clipamp,depth,LFO,nechos,echodelay,echoatt enuation
mytol=tol;
for (int p=0;p<npots;p++) {mypots[p]=pots[p]; mystartpots[p]=pots[p];}
}

void AudioEffectEnhanced::refreshPots(int pots[],int npots,int tol)
{ for (int p=0;p<npots;p++) {mypots[p]=pots[p];}
mytol=tol; maxpots=npots;

vol=mypots[0];

mix=mypots[1];

clipamp=mypots[2];
LFO=mypots[2];
nechos=mypots[2]/5;

lag=mypots[3]*qsize/quantise;
middelay=mypots[3];

depth=mypots[4];
}

boolean hasChanged(char *name,int potno,int value, char *units)
{boolean change=false;
if (abs(mypots[potno]-mystartpots[potno])>mytol)
{change=true;
Serial.print("[");Serial.print(name); Serial.print("] pot (");
Serial.print(potno);Serial.print(") = ");Serial.print(value);
Serial.print(" ");Serial.println(units);
mystartpots[potno]=mypots[potno];
}
return change;
}

void AudioEffectEnhanced::setOctave(int mix0toquantise)
{ float secs;
mix=mix0toquantise;
deadtime = 5; // for SG buffer flush;
muteEffects();
SavitskyGolay = true; oldval = 0; ototal = 0;
Serial.println("");
Serial.print("**** qsize = "); Serial.print(qsize);
Serial.print(" (millisecs) = "); Serial.println((float)qsize / 44.1);
Serial.print("**** pool = "); Serial.print(pooldim);
Serial.print(" (millisecs) = "); Serial.println((float)pooldim / 44.1);
for (int i = 0; i < qsize; i++) q[i] = 0;
poolpt = 1; qinpt = qsize / 2; cycles = 0;
way = 0;
isOctave = true;
}

void AudioEffectEnhanced::clipat(int percent)
{
muteEffects();
isClip = true;
}

void AudioEffectEnhanced::configureFixedDuet(float LFOsweep, float midlag, float amplitudereduction)
{
muteEffects();
LFOmodulationfrequency = (int)(LFOsweep * 44100);
middelay = (int)(midlag * 44100);
chorusreduction = (int)(amplitudereduction);
isFixedDuet = true;
}

void AudioEffectEnhanced::configureVarDuet(float LFOsweep, float midlag, float amplitudereduction)
{
muteEffects();
_LFOmodulationfrequency = (int)(LFOsweep * 44100);
_middelay = (int)(midlag * 44100);
_chorusreduction = (int)(amplitudereduction);
isVarDuet = true;
}

void AudioEffectEnhanced::editvoice(int voice, float mLFOsweep, float mmidlag, float mamplitudereduction)
{
if ((voice <= 0) | (voice > 5)) return;
voice--;
muteEffects();
mLFOmodulationfrequency[voice] = (int)(mLFOsweep * 44100);
mmiddelay[voice] = (int)(mmidlag * 44100);
mchorusreduction[voice] = (int)(mamplitudereduction);
}

void AudioEffectEnhanced::configureMultichorus(float mLFOsweep, float mmidlag, float mamplitudereduction)
{ muteEffects();
for (int voice = 0; voice < 5; voice++)
{
mLFOmodulationfrequency[voice] = (int)(mLFOsweep * 44100);
mmiddelay[voice] = (int)(mmidlag * 44100);
mchorusreduction[voice] = (int)(mamplitudereduction);
}
}
void AudioEffectEnhanced:assthrough()
{
muteEffects();
isPassThro=true;
}

void AudioEffectEnhanced::initMultichorus()
{
muteEffects();
isMulti = true;
}

void AudioEffectEnhanced::goFFT()
{
muteEffects();
isFFTactive = true;
}

void AudioEffectEnhanced::configureEcho(int n, float echolag, int attenuation)
{
muteEffects();
nechos = n; lag = (int)(echolag * 44100); sustain = attenuation;
isEcho = true;
}

void AudioEffectEnhanced::initEcho()
{
}

void qplant(int16_t value)
{ lastsample = value; lastqpt = qinpt;
q[qinpt] = value; qinpt++; qinpt = qinpt & qmask;
}

volatile uint16_t sigmax,lastsigmax; // maximum positive input excursion
int AudioEffectEnhanced::readmaxsig()
{ uint16_t temp = sigmax;
lastsigmax=sigmax;
sigmax = 0;
return temp;
}

const int interval=1;
const int fsinelength=interval*360;
float fSintab[fsinelength];

void makefsinetable()
{float sinval, radian;
for (int index = 0; index<fsinelength; index++)
{
radian = (float)index/(float)interval;
radian = radian* M_PI /180.0;
sinval = sin(radian); //-1 to +1
fSintab[index] = sinval * 1000.0; //-1000.0 to +1000.0
Serial.print(index); Serial.print(" = ");Serial.println( fSintab[index]);
}
}

void makesinetable()
{ float sinval, radian;
for (int degrees = 0; degrees < 360; degrees++)
{
radian = (float)(degrees) * M_PI / 180.0;
sinval = sin(radian); //-1 to +1
sintab[degrees] = (int)(sinval * 1000.0);
Serial.println(sintab[degrees]);
}
}

void makebitreversedtable()
{ int rev, ishift;
for (int i = 0; i < n; i++)
{ rev = 0;
ishift = i;
for (int s = 0; s < power; s++)
{ rev = rev << 1; rev = rev + (ishift & 1); ishift = ishift >> 1;
bitrev[i] = rev;
}
}
}

void AudioEffectEnhanced::begin()
{
pinMode(heartbeat, OUTPUT);
fillblockcount = preludeblocks; // 1/10 sec
muteEffects();
makesinetable();
makefsinetable();
makebitreversedtable();
}

void AudioEffectEnhanced::beginEnhanced_int(int dummy)
{
__disable_irq();

__enable_irq();
}

void AudioEffectEnhanced::setdelays(bool initiate, int n, int delta)
{
ndelays = n; sampledelta = delta; delaysactive = initiate;
}

void AudioEffectEnhanced::stop()
{
}

void AudioEffectEnhanced::update(void)
{
audio_block_t *block;
int16_t input;
uint16_t absinput;

pulsecount = (pulsecount + 1) % 500;
if (pulsecount == 0) digitalWrite(heartbeat, HIGH);
if (pulsecount == 20) digitalWrite(heartbeat, LOW);

if (isFFTactive) //nblocks= 0,1,2,3,4,5, 0,1,2,3,4,5
{ block = receiveWritable(0);
if (block == false) return;

nblocksby2 = nblocks / 2; // 0,0,1,1,2,2 0,0,1,1,2,2
outblocksby2 = (nblocksby2 + 2) % 3; // 2,2,0,0,1,1 2,2,0,0,1,1
offset = (nblocks & 1) * AUDIO_BLOCK_SAMPLES; // 0 or 128
for (int j = 0; j < AUDIO_BLOCK_SAMPLES; j++)
{ if (!sim)
{
input = block->data[j];
rFFTq[nblocksby2][offset + j] = input;
}
else {
input = sintab[sinept] * 25; rFFTq[nblocksby2][offset + j] = input; // attenuated
sinept = (sinept + hop) % 360;
}
iFFTq[nblocksby2][offset + j] = 0;
absinput = input; // input is 15bit+sign signed, absinput is 16 bit unsigned
if (input > sigmax) {
sigmax = absinput;
} // Serial.println(sigmax);}
}
// time to do FFT ?
if (offset != 0)
{
FFT(true , 8, rFFTq[nblocksby2], iFFTq[nblocksby2]);
//**
for (int i = 127; i >= 3; i--) rFFTq[nblocksby2][i] = rFFTq[nblocksby2][i - 2];
rFFTq[nblocksby2][1] = 0; rFFTq[nblocksby2][2] = 0;
for (int i = 127; i >= 3; i--)iFFTq[nblocksby2][i] = iFFTq[nblocksby2][i - 2];
iFFTq[nblocksby2][1] = 0; iFFTq[nblocksby2][2] = 0;

//**
//rFFTq[nblocksby2][2]=rFFTq[nblocksby2][1]*4; rFFTq[nblocksby2][1]=0;
//rFFTq[nblocksby2][253]=rFFTq[nblocksby2][254]*4; rFFTq[nblocksby2][254]=0;

//iFFTq[nblocksby2][3]=iFFTq[nblocksby2][1]; rFFTq[nblocksby2][254]=0;
//iFFTq[nblocksby2][252]=iFFTq[nblocksby2][254]; iFFTq[nblocksby2][254]=0;

FFT(false, 8, rFFTq[nblocksby2], iFFTq[nblocksby2]);

}

if (offset == 0)
{ for (int j = 0; j < AUDIO_BLOCK_SAMPLES; j++) block->data[j] = rFFTq[outblocksby2][j];
transmit(block, 0);
}
else
{ for (int j = 0; j < AUDIO_BLOCK_SAMPLES; j++) block->data[j] = rFFTq[outblocksby2][j + offset];
transmit(block, 0);
}

release(block);

nblocks = (nblocks + 1) % maxblocks; // 0 to 5
return;
}
else
{ // get a block of sample data
block = receiveWritable(0);
if (block == false) return;
// not Fourier Transform is chorus or echo
// fill queue with samples
if (fillblockcount >= 0)
{ digitalWrite(heartbeat, HIGH);
for (int j = 0; j < AUDIO_BLOCK_SAMPLES; j++)
{ input = block->data[j];
qplant(input);
}
release(block);
fillblockcount--;
// initialistion of pointers etc
ototal = input; //qinpt=qsize/2; poolpt=pooldim/2;
return;
} // end of queue initialisation

blockcount++;
if (blockcount == 35) {
blockcount = 0; // 1 sec on 1 sec off
pulse = !pulse;
digitalWrite(heartbeat, pulse);
}

// after prelude
for (int j = 0; j < AUDIO_BLOCK_SAMPLES; j++)
{ if (!sim) input = block->data[j]; else {
input = sintab[sinept] * 25; // attenuated
sinept = (sinept + hop) % 360;
}
x[0] = x[1]; x[1] = x[2]; x[2] = x[3]; x[3] = x[4]; x[4] = x[5];
x[5] = x[6]; x[6] = (int)input; // if autocorrelation
// smooth input values using 5pt quadratic least squares fit
if (SavitskyGolay) // bug now fixed !
{ input = (int16_t)((x[3] * 17 + (x[1] + x[5]) * 12 - (x[2] + x[4]) * 3) / 35);
slope = ((x[5] - x[1]) * 2 + (x[4] - x[2])) / 10;
}
absinput = input; // input is signed, absinput is not
if (input > sigmax) {
sigmax = absinput;
} // Serial.println(sigmax);}

// fill current block with historic samples from q[]
qplant(input);

delaypt = lastqpt;
total = 0;

if (isOctave)
{ oldval = ototal;
if (qinpt == 0) {Serial.print("** cycles =");
Serial.println(cycles);
cycles = 0;
}
poolbasept = (lastqpt - pooldim) & qmask;
poolpt++;
poolpt = poolpt & poolmask;

if (false)
{
// TO DO
}else
{
threshold = 5;
ototal = q[(poolbasept + poolpt) & qmask];

switch (way)
{
case 0 :
// entry phase here is in first margin
startwave = poolpt;
if ((oldval >= 0) && (ototal < 0))
{
peakwide = 0;
wavelength = 0;
way = 1;
}
break;

case 1 : // negative phase
// check peak width at end of -ve peak
if (ototal < 0)
{ // integrate time
peakwide++;
wavelength++;
}
else
{
if (peakwide < threshold) way = 0;
else
{peakwide = 0; way = 2;}
}
break;

case 2 : // positive phase
// long +ve peak
if (ototal >= 0)
{ // integrate time whilst +ve
peakwide++;
wavelength++;
}
else
{ way=3;
if (peakwide < threshold) way =0;
}
break;

case 3 :
// check that whole wavelength is in central 3/4 section of pool
way = 4; // assume final section
if (poolpt < pooldim * 7 / 8) way = 0;
if (wavelength < wavemin) way = 0;
break;

case 4 :

// full wave detected in final 1/8th margin
// scan from poolpt=0 upwards identifying
// similar wave as that which starts at startwave
//Serial.print("pool matchindexing at "); Serial.println(poolpt);
// Serial.print(" "); Serial.println();
//Serial.print("wavelength = "); Serial.println(wavelength);

maxOverlapIntegral = 0;

for (int poolindex = 0; poolindex < wavelength*4; poolindex++)
{ OverlapIntegral = 0;

for (int j = 0; j < wavelength; j++)
{ // rectify both signals to be autocorrelated
int unrectified_q = q[(poolbasept + poolindex + j) & qmask];
int unrectified_x = q[(poolbasept + startwave + j) & qmask];
OverlapIntegral = OverlapIntegral + unrectified_x * unrectified_q;
}
if (OverlapIntegral > maxOverlapIntegral)
{
maxOverlapIntegral = OverlapIntegral;
pmatchindex = poolindex;
if (poolindex==wavelength*2-1) Serial.println("No best overap");
}
}
// Serial.print("best match at "); Serial.println(poolmatchindex + 4);
// qmatchindex=pointer to start of early wave
poolpt = pmatchindex +wavelength+4;
ototal = q[(poolbasept + poolpt) & qmask];
cycles++;
way = 0;
break;
} // end true
}
total = (ototal*mix+input*(quantise-mix))/15;
} // end octave

if (isClip)
{ cliplimit = mypots[2]*40;
if (input > cliplimit) input= +cliplimit;
if (input < -cliplimit) input= -cliplimit;
total = input;
if (hasChanged("Clipping",2,cliplimit,""));
if (hasChanged("Mix",1,mix*4,"% raw"));
}

// echo previous entry echodelay elements stored in q[]
if (isEcho)
{ int mylag;
nechos=2;
if (mypots[2]>10) nechos=3;
if (mypots[2]<5) nechos=1;
mylag=qsize/mypots[3];
total=lastsample;
for (int n = 0; n < nechos; n++)
{ delaypt = (delaypt - mylag) & qmask;
total = total + q[delaypt];
}
if (hasChanged("Lag",3,mylag/44,"mS"));
if (hasChanged("nechos",2,nechos,""));
if (hasChanged("Mix",1,mix*4,"% raw"));
}

//boolean hasChanged(char *name,int potno,int value, char *units)
if (isMulti) // cHECK
{
for (int v = 0; v < 5; v++)
{ mLFOcount[v]++; if (mLFOcount[v] >= mLFOmodulationfrequency[v]) mLFOcount[v] = 0;
offset = (lastqpt - mmiddelay[v]) & qmask;
offset = offset + sintab[(int)(mLFOcount[v] * 359 / mLFOmodulationfrequency[v])] / mchorusreduction[v];
total = (total + q[offset & qmask]) / 5;
}
}

if (isFixedDuet) // override calling parameters
{
volatile float middelay=4410.0; //2205.0*(1.0+(float)(mypots[3]-quantise/2)/(float)quantise); // 0.5 to 1.5
volatile long long LFO=4*88200; //*(mypots[2])/quantise; //
volatile int depth=4*mypots[4]/5;
if (depth==0) depth=1;
LFOcount++; if (LFOcount >= LFO) LFOcount = LFOcount-LFO;
offset = (lastqpt - (int)middelay) & qmask;

offset=offset-2*sintab[(int)(LFOcount * 359 / LFO)]/depth;

total = (input*(quantise-mix) + q[offset & qmask]*mix) / quantise;

if (hasChanged("Mix",1,mix*4,"% effect"));
if (hasChanged("Depth",4,depth,""));
}

if (isVarDuet)
{
volatile float middelay=2205.0*(1.0+(float)(mypots[3]-quantise/2)/(float)quantise); // 0.5 to 1.5
volatile long long LFO=4*88200*(mypots[2])/quantise; //
volatile int depth=4*mypots[4]/5;
if (depth==0) depth=1;
LFOcount++; if (LFOcount >= LFO) LFOcount = LFOcount-LFO;
offset = (lastqpt - (int)middelay) & qmask;

offset=offset-2*sintab[(int)(LFOcount * 359 / LFO)]/depth;

total = (input*(quantise-mix) + q[offset & qmask]*mix) / quantise;

if (hasChanged("Mix",1,mix*4,"% effect"));
if (hasChanged("LFO",2,(int)(LFO*1000/44100)," mS"));
if (hasChanged("Mid Delay",3,(int)(middelay/44.0)," mS"));
if (hasChanged("Depth",4,depth,""));
}

if (isPassThro) total=lastsample;

if (hasChanged("Volume",0,vol*4,"%"));
block->data[j] = total*vol/quantise;
}


// transmit the smoothed? delayed block data and release the curent block after the prelude fill
// also transmit and release the FFT blocks
transmit(block, 0);
release(block);
}
}
void FFT(bool dir, int m, long long x[], long long y[])
{ int n = 1, i, v1, j, l, s1, v2;
float c1, c2, u1, u2, z;
long tx, ty; // prev float! now 17ms
long t1, t2; // now 18ms prev float
long tempxi, tempyi;
int rev;
for (i = 0; i < m; i++) n *= 2;
// bit reversal
for (i = 0; i < n - 1; i++)
{ rev = bitrev[i];
if (rev > i)
{ tx = x[i];
ty = y[i];
x[i] = x[rev];
y[i] = y[rev];
x[rev] = tx;
y[rev] = ty;
}
}
// Compute the FFT FLOAT TO LONG
c1 = -1.0;
c2 = 0.0;
v2 = 1;
for (l = 0; l < m; l++) {
s1 = v2;
v2 <<= 1;
u1 = 1.0;
u2 = 0.0;
for (j = 0; j < s1; j++) {
for (i = j; i < n; i += v2)
{ v1 = i + s1;
//tempxv1=x[v1]; tempyv1=y[v1];
tempxi = x[i]; tempyi = y[i];
t1 = u1 * x[v1] - u2 * y[v1];
t2 = u1 * y[v1] + u2 * x[v1];
x[v1] = tempxi - t1;
y[v1] = tempyi - t2;
x[i] = tempxi + t1;
y[i] = tempyi + t2;
}
z = u1 * c1 - u2 * c2;
u2 = u1 * c2 + u2 * c1;
u1 = z;
}
c2 = sqrt((1.0 - c1) / 2.0);
if (dir) c2 = -c2;
c1 = sqrt((1.0 + c1) / 2.0);
}
//* Scaling for forward transform
if (dir)for (i = 0; i < n; i++)
{
x[i] /= n;
y[i] /= n;
}
}

.h file
====

//effect_enhanced.h

#include "AudioStream.h"
void FFT(bool dir, int m, long long x[], long long y[]);

class AudioEffectEnhanced : public AudioStream
{
public:

AudioEffectEnhanced(void): AudioStream(1,inputQueueArray) { }

void configureVarDuet(float LFOsweep,float midlag,float amplitudereduction);
void configureFixedDuet(float LFOsweep,float midlag,float amplitudereduction);

void configureMultichorus(float mLFOsweep,float mmidlag,float mamplitudereduction);
void editvoice(int voiceno,float mLFOsweep,float mmidlag,float mamplitudereduction);

void initDuet();
void initMultichorus();
void configureEcho(int n,float echolag,int attenuation);
void initEcho();
void goFFT();
void passthrough();
void clipat(int percent);
void setOctave(int mix);
void initPots(int pots[],int npots,int tol);
void refreshPots(int pots[],int npots,int tol);

void begin();
void setDepth(float depth) {}
void beginEnhanced(float grain_length) {
if (grain_length <= 0.0) return;
beginEnhanced_int(grain_length);
}
void stop();
int readmaxsig();
void setdelays(bool active,int n,int delta);

virtual void update(void);

private:
void beginEnhanced_int(int n);
audio_block_t *inputQueueArray[1];
};