-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added Giulio's patches : ToneFilterPatch, OverOverPatch
- Loading branch information
1 parent
64eec2b
commit b073fa0
Showing
6 changed files
with
425 additions
and
70 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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__ |
Oops, something went wrong.