lsrichard
Member
First I would like to thank Kenny Peng for coding the waveshape example that I could build from.
I wanted to experiment with the waveshape effect in Teensy Audio for use with electric guitar and organ.
I found that is was best to test the math I would be using and visualize the shape curves first with the Processing 3 program.
I started with the sin function making a curve shape that is essentially a triangle wave to sine wave converter.
Then I applied the sin function on it self by putting the shape array thru the sin function again.
I continued to do this again up to 8 times.
The result is progressively sharper curves for waveshape.
With that done I converted the code to Arduino C for use with the Teensy 4.0 with Audio Shield.
I also added a power supply, a one transistor impedance buffer to the Audio Shield input and true bypass wiring to the circuit of the pedal.
This actually sounds pretty good for a guitar distortion effect.
It will get progressively more noisy at the higher settings but is still a useful effect.
It has the ability to be clean on most settings as long as you turn down the guitar volume enough and play very lightly or be a heavy lead distortion when you turn it up and play more heavily.
It is also useful on keyboards to give that organ patch a bit of a grind sound.
I will try different algorithms to apply different shapes in the future but this is enough for now.
Code for Arduino:
I wanted to experiment with the waveshape effect in Teensy Audio for use with electric guitar and organ.
I found that is was best to test the math I would be using and visualize the shape curves first with the Processing 3 program.
I started with the sin function making a curve shape that is essentially a triangle wave to sine wave converter.
Then I applied the sin function on it self by putting the shape array thru the sin function again.
I continued to do this again up to 8 times.
The result is progressively sharper curves for waveshape.
With that done I converted the code to Arduino C for use with the Teensy 4.0 with Audio Shield.
I also added a power supply, a one transistor impedance buffer to the Audio Shield input and true bypass wiring to the circuit of the pedal.
This actually sounds pretty good for a guitar distortion effect.
It will get progressively more noisy at the higher settings but is still a useful effect.
It has the ability to be clean on most settings as long as you turn down the guitar volume enough and play very lightly or be a heavy lead distortion when you turn it up and play more heavily.
It is also useful on keyboards to give that organ patch a bit of a grind sound.
I will try different algorithms to apply different shapes in the future but this is enough for now.
Code for Arduino:
Code:
// Up to 8 x Sin function for guitar distortion effect
// Thanks to Kenny Peng for making the waveshape example that I could build from
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
// GUItool: begin automatically generated code
AudioInputI2S i2s1; //xy=97,52
AudioEffectWaveshaper waveshape1; //xy=271,33
AudioEffectWaveshaper waveshape2; //xy=271,71
AudioOutputI2S i2s2; //xy=436,51
AudioConnection patchCord1(i2s1, 0, waveshape1, 0);
AudioConnection patchCord2(i2s1, 1, waveshape2, 0);
AudioConnection patchCord3(waveshape1, 0, i2s2, 0);
AudioConnection patchCord4(waveshape2, 0, i2s2, 1);
AudioControlSGTL5000 sgtl5000_1; //xy=252,111
// GUItool: end automatically generated code
float myWaveshape2[32769]; // array 1 // full sin function
float fi; // adjusted float version of i int
float ds16 = 8; // 1000 mutiplier of sin function result
float d16; // result of sin function math after multiplier
float dgain = 1; // amplitude of sin function result
const float pi = 3.141592654; // pi
float lg [32769]; // array 2 // re-applied sin function
float fv; // fv for re-aplication of sin function
float knob_A1;// re aplication control knob // 20KB used
float avg_A1;// average of knob
unsigned int n; // counter for averaging ect.
byte smode; // re-application mode 0 to 7
byte oldsmode; // old smode to check and do NewShape(); only one time after smode changes.
// switch to an analog pot divided down to 0 thru 7 // Use A1 pin 15 analog input with 10kB or 25kB pot
//const int switchPin2 = 2; // pin number switch 2
//const int switchPin3 = 3; // pin number switch 3
//const int switchPin4 = 4; // pin number switch 3
void setup() {
//Serial.begin(115200); // only needed for serial monitor
AudioMemory(12);
analogReadResolution(12); // read will be 0 to 4095 if set to (12).
//pinMode(switchPin2, INPUT_PULLUP); // switch 2
//pinMode(switchPin3, INPUT_PULLUP); // switch 3
//pinMode(switchPin4, INPUT_PULLUP); // switch 3
//int reading0 = ! digitalRead(switchPin2);
//int reading1 = ! digitalRead(switchPin3);
//int reading2 = ! digitalRead(switchPin4);
//smode = (reading2 * 4) + (reading1 * 2) + reading0;// three bit 01234567
knob_A1 = (float)analogRead(A1);// Selector 0 thru 7 divide by 596
smode = round ( knob_A1 / 596 );// must be rounded and 0 to 7
NewShape();// this is all that is needed for first setup
sgtl5000_1.enable();
sgtl5000_1.lineInLevel(11); // Optimal for 480mV_p-p signal
sgtl5000_1.lineOutLevel(25); // Under 25 causes soft clipping
//Serial.print(smode);
}// setup
void loop() {
knob_A1 = (float)analogRead(A1); // Read 20KB pot
avg_A1 = avg_A1 + knob_A1; // add for 32 loops
if ((n & 31) == 31) { // use if ((n & 31) == 31) will average 32 times should be enough // act when (n & 31) == 31 ( 5 bits high )
avg_A1 = avg_A1 / 32; //average of 32 knob readings
smode = round ( avg_A1 / 596 ); // round and divide down to 0 to 7 for switch function
avg_A1=0; //reset avg after use
} // if (n&31) == 31
//ReadSwitches();
if (smode != oldsmode) NewShape(); // only act if "smode" changed // if conditional so it only do this subroutine one time after "smode" changes
++n; // n counter for avg
n=n & 1023; // 10 bit roll over to 0
} // MAIN LOOP
void NewShape(void){
oldsmode = smode; // this is for the if conditional so it only do this subroutine one time after "smode" changes
for (int i=0; i<32769; i++) {
fi = i + 16384; // float needed for math to be correct // 16384 is starting point at top of sine
d16 = sin( pi * ( fi / 32768 )) * ( ds16 * 1000 ); // fi/32768 to give middle 1/2 cycle starting the peak of the sine.
myWaveshape2[i] = round( d16 * dgain ); // round and gain
myWaveshape2[i]= -(myWaveshape2[i]/8000); // needed math to divide back to -1.0 to 1.0
} //for i
for (int i=0; i<32769; i++) {
fv=myWaveshape2[i]*16384; // set fv for re-aplication of sin function
d16 = sin( pi * ( fv / 32768 )) * ( ds16 * 1000 );// sin function
lg[i] = round( d16 * dgain ); // round and gain
lg[i]= lg[i]/8000; // needed math to divide back to -1.0 to 1.0
} // for
//Serial.print(smode);
//Serial.println(reading0);
//Serial.println(reading1);
//Serial.println(reading2);
//Serial.println(smode);
switch (smode){
case 0 : // Full sin function
for (int i=0; i<32769; i++) {
lg[i]=(myWaveshape2[i]);// just transfer full sin fuction shape
} // for
break;
case 1 : // Double sin function
// nothing needed here // really do nothing for case 1 // uses lg[] defalt double sin function shape
break;
case 2 : // Triple sin function
SinReAp();// 3rd re-aplication of sin function
break;
case 3 : // Quad sin function
SinReAp();// 3rd re-aplication of sin function
SinReAp();// 4th re-aplication of sin function
break;
case 4 : // 5x sin function
SinReAp();// 3rd re-aplication of sin function
SinReAp();// 4th re-aplication of sin function
SinReAp();// 5th re-aplication of sin function
break;
case 5 : // 6x sin function
SinReAp();// 3rd re-aplication of sin function
SinReAp();// 4th re-aplication of sin function
SinReAp();// 5th re-aplication of sin function
SinReAp();// 6th re-aplication of sin function
break;
case 6 : // 7x sin function
SinReAp();// 3rd re-aplication of sin function
SinReAp();// 4th re-aplication of sin function
SinReAp();// 5th re-aplication of sin function
SinReAp();// 6th re-aplication of sin function
SinReAp();// 7th re-aplication of sin function
break;
case 7 : // 8x sin function
SinReAp();// 3rd re-aplication of sin function
SinReAp();// 4th re-aplication of sin function
SinReAp();// 5th re-aplication of sin function
SinReAp();// 6th re-aplication of sin function
SinReAp();// 7th re-aplication of sin function
SinReAp();// 8th re-aplication of sin function
break;
}// switch
waveshape1.shape(lg, 32769);// shape implementation L
waveshape2.shape(lg, 32769);// shape implementation R
//sgtl5000_1.enable(); // do not need to re-enable // this makes it cut out for a second if used
//sgtl5000_1.lineInLevel(11); // Optimal for 480mV_p-p signal // no need to re-do
//sgtl5000_1.lineOutLevel(25); // Under 25 causes soft clipping // no need to re-do
} // NewShape
void SinReAp(void){
for (int i=0; i<32769; i++) {
fv=lg[i]*16384; // set fv re-aplication of sin function as many times as needed
d16 = sin( pi * ( fv / 32768 )) * ( ds16 * 1000 );// sin function
lg[i] = round( d16 * dgain ); // round and gain
lg[i]= lg[i]/8000; // needed math to divide back to -1.0 to 1.0
} // for
} // SinReAp
//void ReadSwitches(void){
// int reading0 = ! digitalRead(switchPin2);
// int reading1 = ! digitalRead(switchPin3);
// int reading2 = ! digitalRead(switchPin4);
// smode = (reading2 * 4) + (reading1 * 2) + reading0;// three bit 01234567 // three separate bit binary to byte convert
//} // ReadSwitches