Skip to content

Commit

Permalink
Added Giulio's patches : ToneFilterPatch, OverOverPatch
Browse files Browse the repository at this point in the history
  • Loading branch information
GuillaumeLeNost committed Oct 31, 2014
1 parent 64eec2b commit b073fa0
Show file tree
Hide file tree
Showing 6 changed files with 425 additions and 70 deletions.
85 changes: 85 additions & 0 deletions OverOverPatch.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
////////////////////////////////////////////////////////////////////////////////////////////////////

/*
LICENSE:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/* created by the OWL team 2014 */


////////////////////////////////////////////////////////////////////////////////////////////////////
/*An overdrive with 4x oversampling and BigMuff-like tone control*/

#ifndef __OverOverPatch_hpp__
#define __OverOverPatch_hpp__
#include "OversampledOverdrive.hpp"
#include "ToneFilter.hpp"
#include "StompBox.h"
class OverOverPatch : public Patch {
private:
OversampledOverdrive os;
ToneFilter tf;
float* buf[2];
public:
OverOverPatch(): tf(getSampleRate()){
registerParameter(PARAMETER_A, "Drive");
registerParameter(PARAMETER_B, "Offset");
registerParameter(PARAMETER_C, "Tone");
registerParameter(PARAMETER_D, "Level");
registerParameter(PARAMETER_E, "DrivePedal");
os=OversampledOverdrive();
}
void processAudio(AudioBuffer &buffer){
static float drive_=0;
static float offset_=0;
static float level_=0;
static float tone_=0;
float expr = 1 - getParameterValue(PARAMETER_E);
float drive = expr*getParameterValue(PARAMETER_A);
float offset = getParameterValue(PARAMETER_B);
float tone = getParameterValue(PARAMETER_C);
float level = getParameterValue(PARAMETER_D);
float input;
float output;
offset /= 10.f;
drive += 0.03f; //make sure we don't kill the signal when drive=0
drive *= 40.f;
level*= .5f;
int size = buffer.getSize();
float numCh=1; //Use only one channel until oversampling becomes less expensive. Should instead be numCh=buffer.getChannels();numCh= numCh<=2 ? numCh : 2;
for (int ch = 0; ch<numCh; ch++) { //get pointers to the buffers.
buf[ch] = buffer.getSamples(ch);
}
tf.setTone(tone_);
for (int i = 0; i < size; i++) { //process each sample
drive_=drive*0.001f + drive_*0.999f; //parameter smoothing
offset_=offset*0.001f + offset_*0.999f; //parameter smoothing
tone_=tone*0.001f + tone_*0.999f; //parameter smoothing
level_=level*0.001f + level_*0.999f; //parameter smoothing
for(int ch=0;ch<numCh; ch++) {
input=buf[ch][i]; //average the inputs
input = (input + offset_)*drive_;
output = os.processSample(input);
output=tf.processSample(output,0);
buf[ch][i] = level_*output;
}
}
}
};

#endif // __OverOverPatch_hpp__
91 changes: 91 additions & 0 deletions OversampledOverdrive.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#ifndef __OversampledOverdrive_hpp__
#define __OversampledOverdrive_hpp__

#define OS_FL 4 //length of the filter
#define OS_L 4 //oversampling factor

// Coefficients for Butterworth filter
// [B A]=butter(3,0.1); #for Fs=48000, cutoff=19200
//upsampling filter coefficients
// float Bu[OS_FL] = { 0.00289819463372143, 0.00869458390116429, 0.00869458390116429, 0.00289819463372143};
// float Au[OS_FL] = { 2.374094743709352, -1.929355669091215, 0.532075368312092, 0 };//removed the leading 1 and added a trailing 0, signs are inverted in order to have only additions
//downsampling filter coefficients
// float Bd[OS_FL] = { 0.00289819463372143, 0.00869458390116429, 0.00869458390116429, 0.00289819463372143};
// float Ad[OS_FL] = { 2.374094743709352, -1.929355669091215, 0.532075368312092, 0 };//removed the leading 1 and added a trailing 0, signs are inverted in order to have only additions

// Coefficients for elliptic filter
// [b a]=ellip(3,2,70,0.1); #for Fs=48000, cutoff=19200
//upsampling filter coefficients
float Bu[OS_FL] = { 0.00198291863379792 , 0.00260151378660307, 0.00260151378660307 , 0.00198291863379791};
float Au[OS_FL] = {2.699187468127642, -2.501953456902938, 0.793597123934494, 0};//removed the leading 1 and added a trailing 0, signs are inverted in order to have only additions
//downsampling filter coefficients
float Bd[OS_FL] = { 0.00198291863379792 , 0.00260151378660307, 0.00260151378660307 , 0.00198291863379791};
float Ad[OS_FL] = {2.699187468127642, -2.501953456902938, 0.793597123934494, 0};//removed the leading 1 and added a trailing 0, signs are inverted in order to have only additions


class OversampledOverdrive {
public:
OversampledOverdrive();
OversampledOverdrive(int factor);
~OversampledOverdrive();
float processSample(float sample);
float processSample2(float sample);
private:
void init();
float oversampledFunction(float oversampledSample);
float xu[OS_L]; //upsampled input signal -- output of the upsampling filter -- input to the oversampledFunction
int xup; //index in xu
float yu[OS_L];//oversampled output signal -- output of the oversampledFunction -- input to the downsampling filter
int yup; //index in yu
float yd[OS_L];//outupt of the donwsampling filter -- the output of the whole oversampling has to be taken from here (1 sample of output per each 1 sample of input to the oversampling)
int ydp; //index in ydp
} ;
OversampledOverdrive::OversampledOverdrive(){
init();
}
OversampledOverdrive::OversampledOverdrive(int factor){ //factor currently ignored
init();
}
void OversampledOverdrive::init(){
yup=0;
ydp = 0;
xup = 0;
for(int i=0;i<OS_L;i++){
xu[i] = 0;
yd[i] = 0;
yu[i] = 0;
}
}
OversampledOverdrive::~OversampledOverdrive(){}

float OversampledOverdrive::processSample(float x){
x*=4; //compensate for the gain of the filter
float xB;
float yTemp;
for (int m = 0; m<OS_L; m++) {
//oversampling
//the oversampling filter requires at most one multiplication for the inputs, as the input is something like [x 0 0 0], so here we save a few multiplies
// if (m + 1 <= OS_FL) //this check is not needed as long as the length of the filter is the same as the oversampling factor
xB = x*Bu[m];
// else
// xB = 0;
float xTemp = (Au[0] * xu[xup] + Au[1] * xu[(xup + 1) & 3] + //output of the upsampling filter
Au[2] * xu[(xup + 2) & 3] + Au[3] * xu[(xup + 3) & 3]) + xB;
xup = (xup - 1 + 4) & 3;
xu[xup] = xTemp;
yu[yup] = oversampledFunction(xTemp); //applies the waveshaper or whatever
//downsampling
float ydB = Bd[0] * yu[yup] + Bd[1] * yu[(yup + 1) & 3] + Bd[2] * yu[(yup + 2) & 3]
+ Bd[3] * yu[(yup + 3) & 3];
yup = (yup - 1 + 4) & 3;
yTemp = (Ad[0] * yd[ydp] + Ad[1] * yd[(ydp + 1) & 3] + //output of the downsampling filter
Ad[2] * yd[(ydp + 2) & 3] + Ad[3] * yd[(ydp + 3) & 3]) + ydB;
ydp = (ydp - 1 + 4) & 3;
yd[ydp] = yTemp;
}
return(yTemp); //the output of the downsampling can be whatever sample, we pick the last one
}
inline float OversampledOverdrive::oversampledFunction(float x){
return x * ( 27 + x*x ) / ( 27 + 9*x*x ); //You can replace this with your favourite waveshaping function
}
#endif // __OversampledOverdrive_hpp__
115 changes: 115 additions & 0 deletions ToneFilter.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*Copyright (c) 2014 Giulio Moro All rights reserved. giuliomoro@yahoo.it
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the source is acknowledged.*/

/* A class that computes and applies the coefficients of a BigMuff-style tone circuit
using a second-order IIR filter. It has two data channels which share the filter coefficients*/
#ifndef __ToneFilter_hpp__
#define __ToneFilter_hpp__

class ToneFilter {
public:
ToneFilter();
ToneFilter(float newFs);
void setTone(float newTone);
void setFs(float newFs);
float processSample(float newX); //defaults to the first channel
float processSample(float newX, int ch);
float processSample2(float newX, int ch);
private:
void init(float newFs);
void updateCoefficients(); //computes the filter coefficients
float B[3];
float A[3];
float tone;//tone control, must be between 0 and 1. Defaults to 0.5.
float fs;//sampling frequency, defaults to 48000
float x[2][4]; //current and previous input samples, two channels
float y[2][4];//current and previous output samples, two channels
};

ToneFilter::ToneFilter(){
init(48000);
}
ToneFilter::ToneFilter(float newFs){
init(newFs);
}

void ToneFilter::init(float newFs){
for(int n=0;n<3;n++){
B[n]=0;
A[n]=0;
for(int m=0;m<2;m++){
x[m][n]=0;
y[m][n]=0;
}
}
setTone(0.5);
setFs(newFs);
}
float ToneFilter::processSample(float newX){
return processSample(newX, 0);
}
/*
float ToneFilter::processSample(float newX, int ch){ //Bucket-brigade style memory copy
x[ch][0]=newX;
y[ch][0]=x[ch][0]*B[0]+x[ch][1]*B[1]+x[ch][2]*B[2] - (A[1]*y[ch][1] + A[2]*y[ch][2]);
y[ch][2]=y[ch][1];
y[ch][1]=y[ch][0];
x[ch][2]=x[ch][1];
x[ch][1]=x[ch][0];
return y[ch][0];
}*/
float ToneFilter::processSample(float newX, int ch){ // circular buffer with pointer wrapped with "&"
static int pointers[2]={0,0};
int p=pointers[ch];
x[ch][p]=newX;
y[ch][p]=x[ch][p]*B[0]+x[ch][(p+1)&3]*B[1]+x[ch][(p+2)&3]*B[2] - (A[1]*y[ch][(p+1)&3] + A[2]*y[ch][(p+2)&3]);
float output=y[ch][p];
pointers[ch]=(p-1+4)&3;
return output;
}
void ToneFilter::setTone(float newTone){ //float tone must be a float between 0 and 1 (no check is performed)
tone=newTone;
updateCoefficients();
}
void ToneFilter::setFs(float newFs){
fs=newFs;
updateCoefficients();
}
void ToneFilter::updateCoefficients(){/* Computes the filter coefficients for a bigMuff-style tone circuit, adpated from Matlab code by Guillaume Le Nost */
// Values from schematics
const float pot = 100e3; // tone pot
const float cb = 3.3e-9; // C9 +/- 5%
const float rb = 27e3 ; // R5 // better than 27e3 (nominal value)
const float r1 = pot * (1-tone);
const float rt = 27e3; // R8
const float ct = 10e-9; // C8 better than 10e-9. Better than 0.9*10e-9 for Ton=0.5
float r2 = pot * (tone) ;

// Equation from circuit analysis (H(s))
float b2 = ct*cb*r2*rt;
float b1 = cb*(r1+r2+rt);
float b0 = 1+ r1/rb;
float a2 = ct*rt*cb*(r1+r2);
float a1 = ct*rt*(1+(r1+r2)/rb) + cb*(r1+r2+rt);
float a0 = 1+(r1+r2+rt)/rb;

// Bilinear Transform
float c = 2*fs; // c=2/T
float cc = c*c; // cc=c^2;
float value=0.5-tone;
float gain=3*( value>=0 ? value: -value ); //gain=3*(abs(0.5-tone));
gain=powf(10,(gain/20)); //from dB to linear
float A0=a0 + a1*c + a2*cc;
A[0]= 1;
A[1] = (2*a0 - 2*a2*cc)/A0;
A[2] = (a0 - a1*c + a2*cc)/A0;

float gainOverA0=gain/A0;
B[0] = (b0 + b1*c + b2*cc)*gainOverA0;
B[1]= (2*b0 - 2*b2*cc)*gainOverA0;
B[2] = (b0 - b1*c + b2*cc)*gainOverA0;
}
//~ int main(){return 0;}

#endif // __ToneFilter_hpp__
64 changes: 64 additions & 0 deletions ToneFilterPatch.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
////////////////////////////////////////////////////////////////////////////////////////////////////

/*
LICENSE:
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/* created by the OWL team 2014 */


////////////////////////////////////////////////////////////////////////////////////////////////////


#ifndef __ToneFilterPatch_hpp__
#define __ToneFilterPatch_hpp__
#include "ToneFilter.hpp"
#include "StompBox.h"

class ToneFilterPatch : public Patch {
private:
ToneFilter tf;
public:
ToneFilterPatch() : tf(getSampleRate()) {
registerParameter(PARAMETER_A, "Tone");
registerParameter(PARAMETER_B, "Gain");
registerParameter(PARAMETER_E, "Tone");
}
void processAudio(AudioBuffer &buffer){
static float gain_=0;
float* buf[2];
float expr = 1 - getParameterValue(PARAMETER_E);
float tone = getParameterValue(PARAMETER_A)*expr;
float gain = getParameterValue(PARAMETER_B)*10;
int size = buffer.getSize();
int numCh = buffer.getChannels();
numCh = numCh<=2 ? numCh : 2; //we only allocated two pointers to buffer, so in the case that we have more, ignore the others.
for (int ch = 0; ch<numCh; ch++) {//get the pointers to the buffers
buf[ch] = buffer.getSamples(ch);
}
tf.setTone(tone); // placing this here instead of doing it for every sample roughly saves 200 OPS per channel, with no audible clicks in the output.
for (int i = 0; i < size; i++) { //process each sample
gain_=gain*0.001 + gain_*0.999; //parameter smoothing
for (int ch = 0; ch<numCh; ch++) { //for each channel
buf[ch][i] = gain_*tf.processSample(buf[ch][i], ch);
}
}
}
};

#endif // __ToneFilterPatch_hpp__
Loading

0 comments on commit b073fa0

Please sign in to comment.