trevorbryden
Active member
Hello,
I have started putting a Teensy synth together. It has analog pots and switches, all working, connected to a teensy 3.5 with audio shield.
The overall design target is: 3 oscillators (Two and Three are pitch and volume selectable, Three is octave selectable, and all three are waveform selectable) one LFO with four destinations, ADSR, filter with cutoff and resonance, and finally delay and reverb.
I've been getting it up step-by-step with success: first the oscillators made pitches through the headphones, their level controls seemed to work, the sound was smooth, choosing different wave types was fine, etc. No crackles or pops or artifacts or whatever. Just neat sounding synthy waves of different kinds happily mixing together.
Then I incorporated the filter and it seemed ok, too- though there was some crackling when i swept the cutoff knob around, it wasn't too terrible and I decided to keep going. I used the mono reverb from the Audio library and got a wet/dry knob working well for that. In fact, the crackling helped me hear the quality of the reverb!
Then, I got midi in to work and print midi messages to the serial monitor (one of the projects on Teensy website, the example midi input one here: https://www.pjrc.com/teensy/td_libs_MIDI.html
...I also used the schematic to wire my MIDI IN port.)
Currently, the synth 'works' (and I'm pretty excited about it, it's my first try ever) but there is quite a delay between pressing a key on my midi controller and the wave changing; there is no note-off yet; and there is some weird stuff going on with the pitches and if i just leave a note on there is quite a bit of crackling here and there, stuff I associate with 'code not working correctly' because I tested all the hardware and there was very good sound quality back when it was just waves, oscillators and so on. In other words, I'm not overdriving one of the mixers or anything. And the distortion in the filter is unrelated to the amount of resonance- it happens when the resonance is well below the stated .707 threshold.
I haven't done the LFO yet, or the ADSR envelopes, or the delay. But I'm a bit stuck where I am.
Code-wise, there's no doubt I have made a mess of things because I simply know nothing about how to run code efficiently. So I am hoping to post it here and pray for some GENTLE advice about:
1. the best coding approach to efficiently and usefully change midi note to frequency and set my OSC1 to that frequency
2. any comments about my filter implementation and why that step introduced some crackling
3. any guidance about how to figure out (for a newbie) voice allocation or just how to get MIDI on and off working cleanly, at least mono for now
4. any stuff in advance to look forward to with ADSR envelopes and getting the delay (time, wet/dry) to work...for now, I didn't want to get into multiplexing for this first synth so I have only a few knobs and decided to eschew delay feedback on this one
The problem is the huge gap between my ability to read and understand, say, the code that the Teensy Tsynth runs (way too complicated for me to follow) and other examples of midi synths I'm trying to use and borrow from. I really do need help in the most basic stuff- how functions return values, how to make the main loop of code run efficiently, etc.
There are ABSOLUTELY bits of code floating around that I'm not using but were left over from stuff I cut and pasted and tried out...for example, I left some things in for voice allocation to 8 voices even though at the moment I'd be fine with a working monophonic synth.
I'll try to post code correctly here and hoping i sorta get the tags and stuff right. Thanks!
Trevor
I have started putting a Teensy synth together. It has analog pots and switches, all working, connected to a teensy 3.5 with audio shield.
The overall design target is: 3 oscillators (Two and Three are pitch and volume selectable, Three is octave selectable, and all three are waveform selectable) one LFO with four destinations, ADSR, filter with cutoff and resonance, and finally delay and reverb.
I've been getting it up step-by-step with success: first the oscillators made pitches through the headphones, their level controls seemed to work, the sound was smooth, choosing different wave types was fine, etc. No crackles or pops or artifacts or whatever. Just neat sounding synthy waves of different kinds happily mixing together.
Then I incorporated the filter and it seemed ok, too- though there was some crackling when i swept the cutoff knob around, it wasn't too terrible and I decided to keep going. I used the mono reverb from the Audio library and got a wet/dry knob working well for that. In fact, the crackling helped me hear the quality of the reverb!
Then, I got midi in to work and print midi messages to the serial monitor (one of the projects on Teensy website, the example midi input one here: https://www.pjrc.com/teensy/td_libs_MIDI.html
...I also used the schematic to wire my MIDI IN port.)
Currently, the synth 'works' (and I'm pretty excited about it, it's my first try ever) but there is quite a delay between pressing a key on my midi controller and the wave changing; there is no note-off yet; and there is some weird stuff going on with the pitches and if i just leave a note on there is quite a bit of crackling here and there, stuff I associate with 'code not working correctly' because I tested all the hardware and there was very good sound quality back when it was just waves, oscillators and so on. In other words, I'm not overdriving one of the mixers or anything. And the distortion in the filter is unrelated to the amount of resonance- it happens when the resonance is well below the stated .707 threshold.
I haven't done the LFO yet, or the ADSR envelopes, or the delay. But I'm a bit stuck where I am.
Code-wise, there's no doubt I have made a mess of things because I simply know nothing about how to run code efficiently. So I am hoping to post it here and pray for some GENTLE advice about:
1. the best coding approach to efficiently and usefully change midi note to frequency and set my OSC1 to that frequency
2. any comments about my filter implementation and why that step introduced some crackling
3. any guidance about how to figure out (for a newbie) voice allocation or just how to get MIDI on and off working cleanly, at least mono for now
4. any stuff in advance to look forward to with ADSR envelopes and getting the delay (time, wet/dry) to work...for now, I didn't want to get into multiplexing for this first synth so I have only a few knobs and decided to eschew delay feedback on this one
The problem is the huge gap between my ability to read and understand, say, the code that the Teensy Tsynth runs (way too complicated for me to follow) and other examples of midi synths I'm trying to use and borrow from. I really do need help in the most basic stuff- how functions return values, how to make the main loop of code run efficiently, etc.
There are ABSOLUTELY bits of code floating around that I'm not using but were left over from stuff I cut and pasted and tried out...for example, I left some things in for voice allocation to 8 voices even though at the moment I'd be fine with a working monophonic synth.
I'll try to post code correctly here and hoping i sorta get the tags and stuff right. Thanks!
Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <MIDI.h>
MIDI_CREATE_INSTANCE(HardwareSerial, Serial1, MIDI);
// GUItool: begin automatically generated code
AudioSynthWaveformModulated Osc1; //xy=129,88
AudioSynthWaveformModulated Osc2; //xy=129,175
AudioEffectChorus chorus1; //xy=128,452
AudioSynthWaveformModulated Osc3; //xy=133,256
AudioEffectEnvelope envelope1; //xy=136,496
AudioSynthWaveformModulated LFO_Osc2Freq; //xy=295,313
AudioSynthWaveformModulated LFO_DelayTime; //xy=300,393
AudioMixer4 Premix; //xy=319,159
AudioSynthWaveformModulated LFO_Volume; //xy=336,435
AudioSynthWaveformModulated LFO_Filter; //xy=340,351
AudioFilterStateVariable LowPassFilter; //xy=479,212
AudioEffectDelay Delay; //xy=589,340
AudioEffectFreeverb Verb; //xy=723,35
AudioOutputI2S i2s1; //xy=771,134
AudioMixer4 MasterR; //xy=776,345
AudioMixer4 MasterL; //xy=777,272
AudioConnection patchCord1(Osc1, 0, Premix, 0);
AudioConnection patchCord2(Osc2, 0, Premix, 1);
AudioConnection patchCord3(Osc3, 0, Premix, 2);
AudioConnection patchCord4(Premix, 0, LowPassFilter, 0);
AudioConnection patchCord5(LowPassFilter, 0, MasterL, 0);
AudioConnection patchCord6(LowPassFilter, 0, MasterR, 0);
AudioConnection patchCord7(LowPassFilter, 0, Verb, 0);
AudioConnection patchCord8(Verb, 0, MasterL, 1);
AudioConnection patchCord9(Verb, 0, MasterR, 1);
AudioConnection patchCord10(MasterR, 0, i2s1, 1);
AudioConnection patchCord11(MasterL, 0, i2s1, 0);
AudioControlSGTL5000 sgtl5000_1; //xy=771,409
// GUItool: end automatically generated code
//variables that hold the values/states of the knobs and switches
int attackVal;
int decayVal;
int sustainVal;
int releaseVal;
int resoVal;
int cutoffVal;
int osc2FreqVal;
int osc2LevelVal;
int osc3FreqVal;
int osc3LevelVal;
int osc1Shape1Val;
int osc1Shape2Val;
int osc2Shape1Val;
int osc2Shape2Val;
int osc3Shape1Val;
int osc3Shape2Val;
int osc3Octave1Val;
int osc3Octave2Val;
int lfoRateVal;
int lfoDepthVal;
int lfoShape1Val;
int lfoShape2Val;
int lfoDest1Val;
int lfoDest2Val;
int lfoDest3Val;
int volumeVal;
int delayLengthVal;
int delayMixVal;
int reverbMixVal;
//Pin assignments
int attackPin = 21;
int decayPin = 20;
int sustainPin = 17;
int releasePin = 16;
int resoPin = 31;
int cutoffPin = 32;
int osc1ShapePin1 = 1;
int osc1ShapePin2 = 2;
int osc2ShapePin1 = 27;
int osc2ShapePin2 = 28;
int osc3ShapePin1 = 8;
int osc3ShapePin2 = 5;
int osc2FreqPin = A14;
int osc2LevelPin = A18;
int osc3FreqPin = A20;
int osc3LevelPin = A17;
int osc3OctavePin1 = 30;
int osc3OctavePin2 = 29;
int lfoRatePin = A15;
int lfoDepthPin = A19;
int lfoShapePin1 = 3;
int lfoShapePin2 = 4;
int lfoDestPin1 = 25;
int lfoDestPin2 = 24;
int lfoDestPin3 = 26;
int volumePin = A1;
int delayLengthPin = A22;
int delayMixPin = A16;
int reverbMixPin = A21;
//oscillator and audio library object values
int osc1current_waveform = 0;
float osc1Pitch = 261.63;
int osc2current_waveform = 0;
float osc2Pitch = 130.86;
int osc3current_waveform = 0;
float osc3Pitch = 65.44;
float osc1Volume = 1.00;
float osc2Volume = 1.00;
float osc3Volume = 1.00;
float osc3Octave = 1.00;
float filterFreq = 6500.0;
float resAmount = 0.700;
float verbMix = 0.00;
float hertz = 1.000;
//midi code variables
unsigned long t=0;
int ledPin = 13;
// array for the notes to be assigned to one of eight voices
int voice[8];
//used to track how many voices are active
//when limit is reached, msg prints Voice Limit Exceeded!
int noteCount=0;
void setup() {
Serial.begin(38400);
pinMode (osc1ShapePin1, INPUT_PULLUP);
pinMode (osc1ShapePin2, INPUT_PULLUP);
pinMode (osc2ShapePin1, INPUT_PULLUP);
pinMode (osc2ShapePin2, INPUT_PULLUP);
pinMode (osc3ShapePin1, INPUT_PULLUP);
pinMode (osc3ShapePin2, INPUT_PULLUP);
pinMode (osc3OctavePin1, INPUT_PULLUP);
pinMode (osc3OctavePin2, INPUT_PULLUP);
pinMode (lfoShapePin1, INPUT_PULLUP);
pinMode (lfoShapePin2, INPUT_PULLUP);
pinMode (lfoDestPin1, INPUT_PULLUP);
pinMode (lfoDestPin2, INPUT_PULLUP);
pinMode (lfoDestPin3, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
AudioMemory(120);
sgtl5000_1.enable();
sgtl5000_1.volume(0.4);
LowPassFilter.frequency(filterFreq);
Verb.roomsize(0.75);
Verb.damping(0.45);
MIDI.setHandleNoteOn(OnNoteOn);
MIDI.setHandleNoteOff(OnNoteOff);
MIDI.begin(MIDI_CHANNEL_OMNI);
}
void loop() {
getReadings();
setWaveForms();
setWaveFreqs();
startWaves();
applyFilter();
reverbMix();
midiDetect();
}
void getReadings(){
attackVal = analogRead(attackPin);
decayVal = analogRead(decayPin);
sustainVal = analogRead(sustainPin);
releaseVal = analogRead(releasePin);
resoVal = analogRead(resoPin);
cutoffVal = analogRead(cutoffPin);
osc1Shape1Val = digitalRead(osc1ShapePin1);
osc1Shape2Val = digitalRead(osc1ShapePin2);
osc2Shape1Val = digitalRead(osc2ShapePin1);
osc2Shape2Val = digitalRead(osc2ShapePin2);
osc3Shape1Val = digitalRead(osc3ShapePin1);
osc3Shape2Val = digitalRead(osc3ShapePin2);
osc3Octave1Val = digitalRead(osc3OctavePin1);
osc3Octave2Val = digitalRead(osc3OctavePin2);
osc2FreqVal = analogRead(osc2FreqPin);
osc2LevelVal = analogRead(osc2LevelPin);
osc3FreqVal = analogRead(osc3FreqPin);
osc3LevelVal = analogRead(osc3LevelPin);
lfoRateVal = analogRead(lfoRatePin);
lfoDepthVal = analogRead(lfoDepthPin);
lfoShape1Val = digitalRead(lfoShapePin1);
lfoShape2Val = digitalRead(lfoShapePin2);
lfoDest1Val = digitalRead(lfoDestPin1);
lfoDest2Val = digitalRead(lfoDestPin2);
lfoDest3Val = digitalRead(lfoDestPin3);
volumeVal = analogRead(volumePin);
delayLengthVal = analogRead(delayLengthPin);
delayMixVal = analogRead(delayMixPin);
reverbMixVal = analogRead(reverbMixPin);
}
void setWaveForms(){
if (osc1Shape1Val == 0){
osc1current_waveform = WAVEFORM_SQUARE;
}
else if (osc1Shape2Val == 0){
osc1current_waveform = WAVEFORM_SAWTOOTH;
}
else {
osc1current_waveform = WAVEFORM_SINE;
}
if (osc2Shape1Val == 0){
osc2current_waveform = WAVEFORM_SQUARE;
}
else if (osc2Shape2Val == 0){
osc2current_waveform = WAVEFORM_SAWTOOTH;
}
else {
osc2current_waveform = WAVEFORM_TRIANGLE;
}
if (osc3Shape1Val == 0){
osc3current_waveform = WAVEFORM_SQUARE;
}
else if (osc3Shape2Val == 0){
osc3current_waveform = WAVEFORM_SAWTOOTH;
}
else {
osc3current_waveform = WAVEFORM_TRIANGLE;
}
}
void setWaveFreqs(){
osc1Pitch = hertz;
osc2Pitch = (osc1Pitch * 3.00);
if (osc3Octave1Val == 0){
osc3Octave = 0.50;
}
else if (osc3Octave2Val == 0){
osc3Octave = 2.00;
}
else {
osc3Octave = 1.00;
}
osc3Pitch = (osc1Pitch / osc3Octave);
}
void startWaves(){
osc2Volume = ((osc2FreqVal / 1023.00) * osc1Volume);
osc3Volume = ((osc3LevelVal / 1023.00) * osc1Volume);
Osc1.begin(osc1Volume, osc1Pitch, osc1current_waveform);
Osc2.begin(osc2Volume, osc2Pitch, osc2current_waveform);
Osc3.begin(osc3Volume, osc3Pitch, osc3current_waveform);
}
void applyFilter(){
filterFreq = expf((float)cutoffVal / 145.00) * 10.00 + 00.00;
LowPassFilter.frequency(filterFreq);
resAmount = (.690 + (resoVal / 235.000));
LowPassFilter.resonance(resAmount);
}
void reverbMix(){
verbMix = (reverbMixVal / 1023.00);
MasterL.gain(0, 1.0);
MasterR.gain(0, 1.0);
MasterL.gain(1, verbMix);
MasterR.gain(1, verbMix);
}
void handleNoteOn(byte channel, byte pitch, byte velocity)
{
}
void handleNoteOff(byte channel, byte pitch, byte velocity)
{
// Do something when the note is released.
// Note that NoteOn messages with 0 velocity are interpreted as NoteOffs.
}
void midiDetect(){
int type, note, velocity, channel, d1, d2;
if (MIDI.read()) { // Is there a MIDI message incoming ?
byte type = MIDI.getType();
switch (type) {
case midi::NoteOn:
note = MIDI.getData1();
velocity = MIDI.getData2();
channel = MIDI.getChannel();
if (velocity > 0) {
hertz = (pow (2.0, ((float)(note-69)/12.0)))*440;
Serial.println(String("Note On: ch=") + channel + ", note=" + note + ", velocity=" + velocity + ", hertz=" + hertz);
} else {
Serial.println(String("Note Off: ch=") + channel + ", note=" + note);
}
break;
case midi::NoteOff:
note = MIDI.getData1();
velocity = MIDI.getData2();
channel = MIDI.getChannel();
Serial.println(String("Note Off: ch=") + channel + ", note=" + note + ", velocity=" + velocity);
break;
default:
d1 = MIDI.getData1();
d2 = MIDI.getData2();
Serial.println(String("Message, type=") + type + ", data = " + d1 + " " + d2);
}
t = millis();
}
if (millis() - t > 10000) {
t += 10000;
Serial.println("(inactivity)");
}
}
void MIDInoteOn(int note, int vel)
{
if(noteCount==8){
Serial.println("Voice Limit Exceeded!");
}
for (int i=0; i<8;i++){
if(!voice[i]){
voice[i]=note;
Serial.print("New Note # ");
Serial.println(note);
Serial.println(vel);
Serial.print("Assigned to Voice # ");
Serial.println(i+1);
digitalWrite(ledPin, HIGH);
delay(100);
digitalWrite(ledPin, LOW);
noteCount++;
if(voice[i]){return;
}
}
}
}
//functin called when a Note Off msg is received. Will scan the voice array to locate
//the note to be shut off. Thus returning the element to zero value. which frees up that
//voice for use of a new note.
void MIDInoteOff(int note, int vel)
{
for(int i=0; i<8; i++){
if(voice[i]==note){
voice[i]=0;
Serial.print("-note off # ");
Serial.println(note);
Serial.print("Shut Off Voice # ");
Serial.println(i+1);
noteCount--;
if(noteCount<0){noteCount=0;
}
digitalWrite(ledPin, HIGH);
delay(100);
digitalWrite(ledPin, LOW);
}
}
}
void OnNoteOn(byte channel, byte note, byte velocity)
{
MIDInoteOn(note, velocity);
}
void OnNoteOff(byte channel, byte note, byte velocity)
{
MIDInoteOff(note, velocity);
digitalWrite(ledPin, LOW);
Serial.print("-note off # ");
Serial.println(note);
}
Trevor