diff --git a/.gitignore b/.gitignore index 1addd27..13f531f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ */build/arduino.avr.nano +.DS_Store diff --git a/Acid/Acid.ino b/Acid/Acid.ino index 76e9302..824441b 100644 --- a/Acid/Acid.ino +++ b/Acid/Acid.ino @@ -6,15 +6,23 @@ // // This code is licenced under GPL v3 or later +// todo: +// add glide handling +// add accent handling +// test StateVariable vs Resonant filters +// exponential envelope https://sensorium.github.io/Mozzi/doc/html/07_8_envelopes_2_ead__envelope_2_ead__envelope_8ino-example.html + #include #include #include #include #include -#include +//#include +#include #include #include #include +#include #include "config.h" //Band limited SQUARE oscilator tables @@ -91,34 +99,39 @@ Oscil aSaw4096(SAW_MAX_4 Oscil aSaw8192(SAW_MAX_8192_AT_16384_512_DATA); MetaOscil aSaw {&aSaw138, &aSaw154, &aSaw182, &aSaw240, &aSaw292, &aSaw341, &aSaw390, &aSaw431, &aSaw512, &aSaw630, &aSaw744, &aSaw1170, &aSaw1638, &aSaw2730, &aSaw4096, &aSaw8192}; -IntMap kMapNote( 0, 4095, 24 * pitchSubSteps, 84 * pitchSubSteps ); +IntMap kMapNote( 0, 4095, 24 * pitchSubSteps, 84 * pitchSubSteps ); IntMap kMapLFOSpeed( 0, 1023, 500, 20000 ); IntMap kMapFreqMod( 0, 1023, 0, 1023 ); IntMap kMapResonance( 0, 1023, 200, 1 ); -IntMap kMapCutoff( 0, 1023, 60, 3600 ); -IntMap kMapAttack( 0, 1023, 0, 80 ); -IntMap kMapDecayRelease( 0, 1023, 8, 160 ); +IntMap kMapCutoff( 0, 1023, 1, 20 ); +IntMap kMapAttack( 0, 1023, 3, 100 ); +IntMap kMapDecayRelease( 0, 1023, 200, 2000 ); IntMap kMapSustain( 0, 1023, 0, 255 ); -ADSR envelope; +//ADSR envelope; +Ead kEnvelope(MOZZI_CONTROL_RATE); StateVariable lpf; + //Global variables byte gain; bool MIDINotePlaying; bool gateIsHigh = false; float noteFreq; bool envSwitchVal; +byte MIDIChannel; +byte memCode = "a"; //a = MIDI Channel stored in 1023 void MIDINoteOn(byte channel, byte note, byte velocity) { noteFreq = mtof((int) note); - envelope.noteOn(); + kEnvelope.start(); + //envelope.noteOn(); MIDINotePlaying = true; digitalWrite(LED, LOW); } void MIDINoteOff(byte channel, byte note, byte velocity) { - envelope.noteOff(); + //envelope.noteOff(); digitalWrite(LED, HIGH); } @@ -139,16 +152,83 @@ void setup(){ pinMode(EnvSwitch, INPUT_PULLUP); pinMode(DroneSwitch, INPUT_PULLUP); + //MIDI Channel setup + if(!digitalRead(GateIn)) { //&& !digitalRead(DroneSwitch) && !digitalRead(EnvSwitch)) { //MIDI setup mode, first knob you rotate = MIDI channel + int knob1Orig = analogRead(Knob1); + int knob2Orig = analogRead(Knob2); + int knob3Orig = analogRead(Knob3); + int knob4Orig = analogRead(Knob4); + int knobAOrig = analogRead(KnobA); + int knobDROrig = analogRead(KnobDR); + int knobSOrig = analogRead(KnobS); + int threshhold = 50; //To reduce noise from the input, needs testing and fine-tuning + while(true) { + if(abs(analogRead(Knob1) - knob1Orig) > threshhold) { + MIDIChannel = 1; + break; + } else if (abs(analogRead(Knob2) - knob2Orig) > threshhold) { + MIDIChannel = 2; + break; + } else if (abs(analogRead(Knob3) - knob3Orig) > threshhold) { + MIDIChannel = 3; + break; + } else if (abs(analogRead(Knob4) - knob4Orig) > threshhold) { + MIDIChannel = 4; + break; + } else if (abs(analogRead(KnobA) - knobAOrig) > threshhold) { + MIDIChannel = 5; + break; + } else if (abs(analogRead(KnobDR) - knobDROrig) > threshhold) { + MIDIChannel = 6; + break; + } else if (abs(analogRead(KnobS) - knobSOrig) > threshhold) { + MIDIChannel = 7; + break; + } + digitalWrite(LED, HIGH); //slow blinking to indicate the setup mode + delay(500); + digitalWrite(LED, LOW); + delay(500); + } + EEPROM.write(0, MIDIChannel); + EEPROM.write(1023, memCode); + digitalWrite(LED, LOW); + delay(150); + for(int i = 0; i < MIDIChannel; i ++) { //fast blink to indicate saved MIDI channel + digitalWrite(LED, HIGH); + delay(150); + digitalWrite(LED, LOW); + delay(150); + } + delay(500); + } else if (EEPROM.read(1023) == memCode) { + MIDIChannel = EEPROM.read(0); + } else { //Save default MIDI Channel to EEPROM if it wasn't set + MIDIChannel = 1; //channel 1 by default + EEPROM.write(0, MIDIChannel); + EEPROM.write(1023, memCode); + } + + disconnectDigitalIn(Knob1); + disconnectDigitalIn(Knob2); + disconnectDigitalIn(Knob3); + disconnectDigitalIn(Knob4); + disconnectDigitalIn(KnobA); + disconnectDigitalIn(KnobDR); + disconnectDigitalIn(KnobS); + disconnectDigitalIn(CVIn); + MIDI.setHandleNoteOn(MIDINoteOn); MIDI.setHandleNoteOff(MIDINoteOff); - MIDI.begin(MIDI_CHANNEL); + MIDI.begin(MIDIChannel); aSquare.setCutoffFreqs(90, 101, 122, 138, 154, 174, 210, 264, 327, 431, 546, 744, 1170, 1638, 2730, 8192); aSaw.setCutoffFreqs(138, 154, 182, 240, 292, 341, 390, 431, 512, 630, 744, 1170, 1638, 2730, 4096, 8192); startMozzi(); - envelope.setAttackLevel(255); + //envelope.setAttackLevel(255); + kEnvelope.setAttack(3); digitalWrite(LED, HIGH); } @@ -182,14 +262,15 @@ void updateControl(){ digitalWrite(LED, !gateInVal); if (gateInVal && !gateIsHigh) { gateIsHigh = true; - envelope.noteOn(); + kEnvelope.start(); + //envelope.noteOn(); } else if (!gateInVal && gateIsHigh) { gateIsHigh = false; - envelope.noteOff(); + //envelope.noteOff(); } - } else if (MIDINotePlaying && !envelope.playing()) { + } /*else if (MIDINotePlaying && !envelope.playing()) { MIDINotePlaying = false; - } + }*/ //Update Filter settings lpf.setResonance(resonance); @@ -199,37 +280,40 @@ void updateControl(){ int attackTime = kMapAttack(knobAVal); int decayReleaseTime = kMapDecayRelease(knobDRVal); int sustainLevel = kMapSustain(knobSVal); - envelope.setDecayLevel(sustainLevel); - envelope.setTimes(attackTime, decayReleaseTime, 30000, decayReleaseTime); //30000 is so the note will sustain 30 seconds unless a noteOff comes + kEnvelope.setDecay(decayReleaseTime); + kEnvelope.setAttack(attackTime); + //envelope.setDecayLevel(sustainLevel); + //envelope.setTimes(attackTime, decayReleaseTime, 30000, decayReleaseTime); //30000 is so the note will sustain 30 seconds unless a noteOff comes //Set Oscilator Frequency float oscFreq = noteFreq; aSquare.setFreq(oscFreq); aSaw.setFreq(oscFreq); - envelope.update(); + //envelope.update(); - int env = envelope.next(); + //int env = envelope.next(); + int env = kEnvelope.next(); if(!droneSwitchVal) { gain = env; } else { gain = 255; } - lpf.setCentreFreq(60 + env * 14); + lpf.setCentreFreq(env * cutoff); } AudioOutput updateAudio(){ long signal; if(envSwitchVal) { - signal = aSquare.next() * 0.3; + signal = aSquare.next() * 0.5; } else { - signal = aSaw.next() * 1.2; + signal = aSaw.next() * 1; } signal = lpf.next(signal); //filter signal = (signal * gain); //envelope - //signal = softClip((signal * (127 + driveAmount)) >> 8); //overdrive + signal = softClip((signal * (127 + driveAmount)) >> 6); //overdrive return MonoOutput::fromNBit(17, signal).clip(); } diff --git a/Acid/config.h b/Acid/config.h index 5d4097d..ae5f7ad 100644 --- a/Acid/config.h +++ b/Acid/config.h @@ -1,8 +1,7 @@ //Settings const int pitchSubSteps = 16; //set how many steps are there between semitones. set to 1 to quantize to semitones -const int driveAmount = 350; +const int driveAmount = 250; const bool oversamplingEnabled = false; //makes V/OCT tracking more precise, but adds a little portamento -#define MIDI_CHANNEL 2 //MIDI_CHANNEL_OMNI #define MOZZI_CONTROL_RATE 256 //Hardware Definitions