From 245982c5752ea58aa698991c5cb047278aeb1a0e Mon Sep 17 00:00:00 2001 From: Oleksiy Date: Sun, 13 Sep 2026 18:57:20 +0300 Subject: [PATCH] added glide, but still needs debugging --- Acid/Acid.ino | 130 ++++++++++++++++++++++++++++---------------------- Acid/config.h | 3 +- 2 files changed, 74 insertions(+), 59 deletions(-) diff --git a/Acid/Acid.ino b/Acid/Acid.ino index 7c8cc14..add7095 100644 --- a/Acid/Acid.ino +++ b/Acid/Acid.ino @@ -7,18 +7,17 @@ // This code is licenced under GPL v3 or later // todo: -// add glide handling -// make a glide on/off swithc instead of drone, both for MIDI and CV (sample pitch on gate) +// debug glide (slides down from lower notes?) // add accent handling +// bring back manual triggering (test conflicts with midi) #include #include #include #include -//#include #include -//#include #include +#include #include #include #include @@ -101,42 +100,57 @@ MetaOscil aSaw {&aSaw1 IntMap kMapNote( 0, 4095, 24 * pitchSubSteps, 84 * pitchSubSteps ); IntMap kMapResonance( 0, 1023, 0, 255 ); -IntMap kMapCutoff( 0, 1023, 20, 127 ); +IntMap kMapCutoff( 0, 1023, 26, 100 ); IntMap kMapAttack( 0, 1023, 3, 100 ); IntMap kMapDecay( 0, 1023, 200, 2000 ); -IntMap kMapEnvMod( 0, 1023, 0, 255 ); +IntMap kMapEnvMod( 0, 1023, 0, 160 ); +IntMap kMapDrive( 0, 1023, 0, 400 ); -//ADSR envelope; Ead kEnvelope(MOZZI_CONTROL_RATE); -//StateVariable lpf; LowPassFilter lpfr; - +Portamento glide; //Global variables byte gain; bool MIDINotePlaying; bool gateIsHigh = false; -float noteFreq; +Q16n16 noteFreq; bool envSwitchVal; +bool droneSwitchVal; byte MIDIChannel; byte memCode = "a"; //a = MIDI Channel stored in 1023 +int driveAmount; +byte notePlaying; + +// long debugMaxSignal = 0; +// long debugMinSignal = 0; void MIDINoteOn(byte channel, byte note, byte velocity) { - noteFreq = mtof((int) note); - kEnvelope.start(); - //envelope.noteOn(); + notePlaying = note; + noteFreq = Q16n16_mtof((int) note); + + if (!MIDINotePlaying || !droneSwitchVal) { + glide.setTime(0); + glide.start(note); + kEnvelope.start(); + } else { + glide.setTime(GLIDE_TIME); + glide.start(note); + } MIDINotePlaying = true; digitalWrite(LED, LOW); } void MIDINoteOff(byte channel, byte note, byte velocity) { - //envelope.noteOff(); - MIDINotePlaying = false; - digitalWrite(LED, HIGH); + if (note == notePlaying || !droneSwitchVal) { + MIDINotePlaying = false; + digitalWrite(LED, HIGH); + glide.setTime(0); + } } long softClip(long input) { - int threshold = 2048; + int threshold = 24000; if (input < -threshold) { return -threshold + (input + threshold) / 4; } else if (input > threshold) { @@ -153,7 +167,7 @@ void setup(){ pinMode(DroneSwitch, INPUT_PULLUP); //MIDI Channel setup - if(!digitalRead(GateIn)) { //&& !digitalRead(DroneSwitch) && !digitalRead(EnvSwitch)) { //MIDI setup mode, first knob you rotate = MIDI channel + if(!digitalRead(GateIn)) { //MIDI setup mode, first knob you rotate = MIDI channel int knob1Orig = analogRead(Knob1); int knob2Orig = analogRead(Knob2); int knob3Orig = analogRead(Knob3); @@ -161,7 +175,7 @@ void setup(){ 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 + int threshhold = 50; //To reduce noise from the input while(true) { if(abs(analogRead(Knob1) - knob1Orig) > threshhold) { MIDIChannel = 1; @@ -227,9 +241,6 @@ void setup(){ startMozzi(); - //envelope.setAttackLevel(255); - kEnvelope.setAttack(3); - digitalWrite(LED, HIGH); } @@ -247,74 +258,79 @@ void updateControl(){ int knobAVal = mozziAnalogRead(KnobA); int knobDRVal = mozziAnalogRead(KnobDR); int knobSVal = mozziAnalogRead(KnobS); - bool droneSwitchVal = digitalRead(DroneSwitch); + droneSwitchVal = digitalRead(DroneSwitch); envSwitchVal = digitalRead(EnvSwitch); bool gateInVal = !digitalRead(GateIn); int CVInVal = mozziAnalogRead(CVIn); - //Remap the values and assign to parameter + //Remap the values and assign to parameters int cutoff = kMapCutoff(knob1Val); int resonance = kMapResonance(knob2Val); int envMod = kMapEnvMod(knob3Val); + driveAmount = kMapDrive(knob4Val); //Set pitch and play notes on trigger - if (!MIDINotePlaying) { - noteFreq = mtof((float) kMapNote(CVInVal << 2) / pitchSubSteps); - digitalWrite(LED, !gateInVal); - if (gateInVal && !gateIsHigh) { - gateIsHigh = true; - kEnvelope.start(); - //envelope.noteOn(); - } else if (!gateInVal && gateIsHigh) { - gateIsHigh = false; - //envelope.noteOff(); - } - } /*else if (MIDINotePlaying && !envelope.playing()) { - MIDINotePlaying = false; - }*/ - - //Update Filter settings - //lpf.setResonance(resonance); + // if (!MIDINotePlaying) { + // noteFreq = mtof((float) kMapNote(CVInVal << 2) / pitchSubSteps); + // digitalWrite(LED, !gateInVal); + // if (gateInVal && !gateIsHigh) { + // gateIsHigh = true; + // kEnvelope.start(); + // } else if (!gateInVal && gateIsHigh) { + // gateIsHigh = false; + // } + // } //Update Envelope Settings int attackTime = kMapAttack(knobAVal); int decayReleaseTime = kMapDecay(knobDRVal); - //int sustainLevel = kMapSustain(knobSVal); 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 + + //Calculate makeup Gain + //makeupGain = (256L * 127) / (127 + driveAmount); // Q8 fixed-point compensation factor //Set Oscilator Frequency - float oscFreq = noteFreq; - aSquare.setFreq(oscFreq); - aSaw.setFreq(oscFreq); + Q16n16 oscFreq; + oscFreq = glide.next(); + aSquare.setFreq_Q16n16(oscFreq); + aSaw.setFreq_Q16n16(oscFreq); - //envelope.update(); - - //int env = envelope.next(); int env = kEnvelope.next(); - if(!droneSwitchVal) { + //if(!droneSwitchVal) { gain = env; - } else { - gain = 255; - } - //lpf.setCentreFreq(constrain(cutoff + (((long) env * envMod) >> 8), 20, 4096)); + //} else { + // gain = 255; + //} lpfr.setCutoffFreqAndResonance(constrain(cutoff + (((long)env * envMod * cutoff)>>16), 0, 255), resonance); + // static int debugCounter = 0; + // if (++debugCounter >= 100) { // adjust to taste - print every ~100 control cycles + // Serial.print("min: "); Serial.print(debugMinSignal); + // Serial.print(" max: "); Serial.println(debugMaxSignal); + // debugMaxSignal = 0; + // debugMinSignal = 0; + // debugCounter = 0; + // } + } AudioOutput updateAudio(){ long signal; if(envSwitchVal) { - signal = (aSquare.next() * 3) >> 2;// 0.75 gain to ~ match saw level + signal = (aSquare.next() * 3) >> 2; // tricky way to set 0.75 gain to ~ match saw level } else { signal = aSaw.next(); } signal = lpfr.next(signal); //filter signal = (signal * gain); //envelope - //signal = softClip((signal * (127 + driveAmount)) >> 6); //overdrive + //signal = softClip((signal * (127 + driveAmount)) >> 9); //overdrive + //signal = (signal * makeupGain) >> 7; //makeup gain compensation + + // if (signal > debugMaxSignal) debugMaxSignal = signal; + // if (signal < debugMinSignal) debugMinSignal = signal; + return MonoOutput::fromNBit(17, signal).clip(); } diff --git a/Acid/config.h b/Acid/config.h index ae5f7ad..e4479f0 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 = 250; -const bool oversamplingEnabled = false; //makes V/OCT tracking more precise, but adds a little portamento #define MOZZI_CONTROL_RATE 256 +#define GLIDE_TIME 150 //Hardware Definitions #define Knob1 A6 //Intensity