diff --git a/NuEVI/FilterOnePole.cpp b/NuEVI/FilterOnePole.cpp new file mode 100644 index 0000000..478cd03 --- /dev/null +++ b/NuEVI/FilterOnePole.cpp @@ -0,0 +1,240 @@ +// Copyright 2014 Jonathan Driscoll +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "FilterOnePole.h" + +#include + +FilterOnePole::FilterOnePole( FILTER_TYPE ft, float fc, float initialValue ) { + setFilter( ft, fc, initialValue ); +} + +void FilterOnePole::setFilter( FILTER_TYPE ft, float fc, float initialValue ) { + FT = ft; + setFrequency( fc ); + + Y = initialValue; + Ylast = initialValue; + X = initialValue; + + LastUS = micros(); +} + +float FilterOnePole::input( float inVal ) { + long time = micros(); + ElapsedUS = float(time - LastUS); // cast to float here, for math + LastUS = time; // update this now + + // shift the data values + Ylast = Y; + X = inVal; // this is now the most recent input value + + // filter value is controlled by a parameter called X + // tau is set by the user in microseconds, but must be converted to samples here + TauSamps = TauUS / ElapsedUS; + + float ampFactor; +#ifdef ARM_FLOAT + ampFactor = expf( -1.0 / TauSamps ); // this is 1 if called quickly +#else + ampFactor = exp( -1.0 / TauSamps ); // this is 1 if called quickly +#endif + + Y = (1.0-ampFactor)*X + ampFactor*Ylast; // set the new value + + return output(); +} + +void FilterOnePole::setFrequency( float newFrequency ) { + setTau( 1.0/(TWO_PI*newFrequency ) ); // τ=1/ω +} + +void FilterOnePole::setTau( float newTau ) { + TauUS = newTau * 1e6; +} + +float FilterOnePole::output() { + // figure out which button to read + switch (FT) { + case LOWPASS: + // return the last value + return Y; + break; + case INTEGRATOR: + // using a lowpass, but normaize + return Y * (TauUS/1.0e6); + break; + case HIGHPASS: + // highpass is the _difference_ + return X-Y; + break; + case DIFFERENTIATOR: + // like a highpass, but normalize + return (X-Y)/(TauUS/1.0e6); + break; + default: + // should never get to here, return 0 just in case + return 0; + } +} + +void FilterOnePole::print() { + Serial.println(""); + Serial.print(" Y: "); Serial.print( Y ); + Serial.print(" Ylast: "); Serial.print( Ylast ); + Serial.print(" X "); Serial.print( X ); + Serial.print(" ElapsedUS "); Serial.print( ElapsedUS ); + Serial.print(" TauSamps: "); Serial.print( TauSamps ); + //Serial.print(" ampFactor " ); Serial.print( ampFactor ); + Serial.print(" TauUS: "); Serial.print( TauUS ); + Serial.println(""); +} + +void FilterOnePole::test() { + float tau = 10; + float updateInterval = 1; + float nextupdateTime = millis()*1e-3; + + float inputValue = 0; + FilterOnePole hp( HIGHPASS, tau, inputValue ); + FilterOnePole lp( LOWPASS, tau, inputValue ); + + while( true ) { + float now = millis()*1e-3; + + // switch input values on a 20 second cycle + if( round(now/20.0)-(now/20.0) < 0 ) + inputValue = 0; + else + inputValue = 100; + + hp.input(inputValue); + lp.input(inputValue); + + if( now > nextupdateTime ) { + nextupdateTime += updateInterval; + + Serial.print("inputValue: "); Serial.print( inputValue ); + Serial.print("\t high-passed: "); Serial.print( hp.output() ); + Serial.print("\t low-passed: "); Serial.print( lp.output() ); + Serial.println(); + } + } +} + +void FilterOnePole::setToNewValue( float newVal ) { + Y = Ylast = X = newVal; +} + + +// stuff for filter2 (lowpass only) +// should be able to set a separate fall time as well +FilterOnePoleCascade::FilterOnePoleCascade( float riseTime, float initialValue ) { + setRiseTime( riseTime ); + setToNewValue( initialValue ); +} + +void FilterOnePoleCascade::setRiseTime( float riseTime ) { + float tauScale = 3.36; // found emperically, by running test(); + + Pole1.setTau( riseTime / tauScale ); + Pole2.setTau( riseTime / tauScale ); +} + +float FilterOnePoleCascade::input( float inVal ) { + Pole2.input( Pole1.input( inVal )); + return output(); +} + +// clears out the values in the filter +void FilterOnePoleCascade::setToNewValue( float newVal ) { + Pole1.setToNewValue( newVal ); + Pole2.setToNewValue( newVal ); +} + +float FilterOnePoleCascade::output() { + return Pole2.output(); +} + +void FilterOnePoleCascade::test() { + // make a filter, how fast does it run: + + float rise = 1.0; + FilterOnePoleCascade myFilter( rise ); + + // first, test the filter speed ... + long nLoops = 1000; + + Serial.print( "testing filter with a rise time of "); + Serial.print( rise ); Serial.print( "s" ); + + Serial.print( "\n running filter speed loop ... "); + + float startTime, stopTime; + + startTime = millis()*1e-3; + for( long i=0; i 0.1 && !crossedTenPercent ) { + // filter first crossed the 10% point + startTime = millis()*1e-3; + crossedTenPercent = true; + } + } + stopTime = millis()*1e-3; + + Serial.print( "done, rise time: " ); Serial.print( stopTime-startTime ); + + Serial.print( "testing attenuation at f = 1/risetime" ); + + myFilter.setToNewValue( 0.0 ); + + float maxVal = 0; + float valWasOutputThisCycle = true; + + __unused float lastFilterVal = 0; + + while( true ) { + float now = 1e-3*millis(); + + float currentFilterVal = myFilter.input( sin( TWO_PI*now) ); + + if( currentFilterVal < 0.0 ) { + if( !valWasOutputThisCycle ) { + // just crossed below zero, output the max + Serial.print( maxVal*100 ); Serial.print( " %\n" ); + valWasOutputThisCycle = true; + } + + } + + } + +} diff --git a/NuEVI/FilterOnePole.h b/NuEVI/FilterOnePole.h new file mode 100644 index 0000000..cf55b86 --- /dev/null +++ b/NuEVI/FilterOnePole.h @@ -0,0 +1,89 @@ +// Copyright 2014 Jonathan Driscoll +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// FilterOnePole has been copied from https://github.com/JonHub/Filters + + +#ifndef FilterOnePole_h +#define FilterOnePole_h + +enum FILTER_TYPE { + HIGHPASS, + LOWPASS, + INTEGRATOR, + DIFFERENTIATOR +}; + +// the recursive filter class implements a recursive filter (low / pass / highpass +// note that this must be updated in a loop, using the most recent acquired values and the time acquired +// Y = a0*X + a1*Xm1 +// + b1*Ylast +struct FilterOnePole { + FILTER_TYPE FT; + float TauUS; // decay constant of the filter, in US + float TauSamps; // tau, measued in samples (this changes, depending on how long between input()s + + // filter values - these are public, but should not be set externally + float Y; // most recent output value (gets computed on update) + float Ylast; // prevous output value + + float X; // most recent input value + + // elapsed times are kept in long, and will wrap every + // 35 mins, 47 seconds ... however, the wrap does not matter, + // because the delta will still be correct (always positive and small) + float ElapsedUS; // time since last update + long LastUS; // last time measured + + FilterOnePole( FILTER_TYPE ft=LOWPASS, float fc=1.0, float initialValue=0 ); + + // sets or resets the parameters and state of the filter + void setFilter( FILTER_TYPE ft, float tauS, float initialValue ); + + void setFrequency( float newFrequency ); + + void setTau( float newTau ); + + float input( float inVal ); + + float output(); + + void print(); + + void test(); + + void setToNewValue( float newVal ); // resets the filter to a new value +}; + +// two pole filter, these are very useful +struct FilterOnePoleCascade { + + FilterOnePole Pole1; + FilterOnePole Pole2; + + FilterOnePoleCascade( float riseTime=1.0, float initialValue=0 ); // rise time to step function, 10% to 90% + + // rise time is 10% to 90%, for a step input + void setRiseTime( float riseTime ); + + void setToNewValue( float newVal ); + + float input( float inVal ); + + float output(); + + void test(); +}; + +#endif diff --git a/NuEVI/NuEVI.ino b/NuEVI/NuEVI.ino index 23b5ba2..2f22029 100644 --- a/NuEVI/NuEVI.ino +++ b/NuEVI/NuEVI.ino @@ -1,16 +1,16 @@ #include - #include #include #include -#include // for the breath signal LP filtering, https://github.com/edgar-bonet/Filters +#include "FilterOnePole.h" // for the breath signal low-pass filtering, from https://github.com/JonHub/Filters #include "globals.h" #include "hardware.h" #include "midi.h" #include "menu.h" #include "config.h" #include "settings.h" +#include "led.h" /* NAME: NuEVI @@ -80,6 +80,11 @@ unsigned short vibControl = 0; unsigned short fastPatch[7] = {0,0,0,0,0,0,0}; +uint16_t bcasMode; //Legacy CASSIDY compile flag +uint16_t trill3_interval; +uint16_t fastBoot; +uint16_t dacMode; + byte rotatorOn = 0; byte currentRotation = 0; uint16_t rotations[4]; // semitones { -5, -10, -7, -14 }; @@ -90,8 +95,6 @@ uint16_t gateOpenEnable = 0; uint16_t specialKeyEnable = 0; -uint16_t wlPower = 0; - int touch_Thr = 1300; byte ccList[11] = {0,1,2,7,11,1,2,7,11,74,20}; // OFF, Modulation, Breath, Volume, Expression (then same sent in hires), CC74 (cutoff/brightness), CC20 (UNO Cutoff) @@ -230,9 +233,6 @@ byte slurSustain = 0; byte parallelChord = 0; byte subOctaveDouble = 0; -const int breathLedBrightness = 500; // up to 4095, PWM -const int portamLedBrightness = 500; // up to 4095, PWM - Adafruit_MPR121 touchSensor = Adafruit_MPR121(); // This is the 12-input touch sensor FilterOnePole breathFilter; @@ -260,126 +260,10 @@ void setup() { pinMode(biteJumperGndPin, OUTPUT); //PBITE digitalWrite(biteJumperGndPin, LOW); //PBITE - // if stored settings are not for current version, or Enter+Menu are pressed at startup, they are replaced by factory settings - uint16_t settingsVersion = readSetting(VERSION_ADDR); + //Read eeprom data into global vars + readEEPROM(); - if (((settingsVersion != VERSION) && (settingsVersion < 24)) || (!digitalRead(ePin) && !digitalRead(mPin)) || (settingsVersion == 0xffffu)) { - writeSetting(VERSION_ADDR,VERSION); - writeSetting(BREATH_THR_ADDR,BREATH_THR_FACTORY); - writeSetting(BREATH_MAX_ADDR,BREATH_MAX_FACTORY); - if (digitalRead(biteJumperPin)){ //PBITE (if pulled low with jumper, pressure sensor is used instead of capacitive bite sensing) - writeSetting(PORTAM_THR_ADDR,PORTAM_THR_FACTORY); - writeSetting(PORTAM_MAX_ADDR,PORTAM_MAX_FACTORY); - } else { - writeSetting(PORTAM_THR_ADDR,PORTPR_THR_FACTORY); - writeSetting(PORTAM_MAX_ADDR,PORTPR_MAX_FACTORY); - } - writeSetting(PITCHB_THR_ADDR,PITCHB_THR_FACTORY); - writeSetting(PITCHB_MAX_ADDR,PITCHB_MAX_FACTORY); - writeSetting(EXTRAC_THR_ADDR,EXTRAC_THR_FACTORY); - writeSetting(EXTRAC_MAX_ADDR,EXTRAC_MAX_FACTORY); - writeSetting(CTOUCH_THR_ADDR,CTOUCH_THR_FACTORY); - } - - if ((settingsVersion != VERSION) || (!digitalRead(ePin) && !digitalRead(mPin))) { - writeSetting(VERSION_ADDR,VERSION); - - writeSetting(TRANSP_ADDR,TRANSP_FACTORY); - writeSetting(MIDI_ADDR,MIDI_FACTORY); - writeSetting(BREATH_CC_ADDR,BREATH_CC_FACTORY); - writeSetting(BREATH_AT_ADDR,BREATH_AT_FACTORY); - writeSetting(VELOCITY_ADDR,VELOCITY_FACTORY); - writeSetting(PORTAM_ADDR,PORTAM_FACTORY); - writeSetting(PB_ADDR,PB_FACTORY); - writeSetting(EXTRA_ADDR,EXTRA_FACTORY); - writeSetting(VIBRATO_ADDR,VIBRATO_FACTORY); - writeSetting(DEGLITCH_ADDR,DEGLITCH_FACTORY); - writeSetting(PATCH_ADDR,PATCH_FACTORY); - writeSetting(OCTAVE_ADDR,OCTAVE_FACTORY); - writeSetting(BREATHCURVE_ADDR,BREATHCURVE_FACTORY); - writeSetting(VEL_SMP_DL_ADDR,VEL_SMP_DL_FACTORY); - writeSetting(VEL_BIAS_ADDR,VEL_BIAS_FACTORY); - writeSetting(PINKY_KEY_ADDR,PINKY_KEY_FACTORY); - writeSetting(FP1_ADDR,0); - writeSetting(FP2_ADDR,0); - writeSetting(FP3_ADDR,0); - writeSetting(FP4_ADDR,0); - writeSetting(FP5_ADDR,0); - writeSetting(FP6_ADDR,0); - writeSetting(FP7_ADDR,0); - writeSetting(DIPSW_BITS_ADDR,DIPSW_BITS_FACTORY); - writeSetting(PARAL_ADDR,PARAL_FACTORY); - writeSetting(ROTN1_ADDR,ROTN1_FACTORY); - writeSetting(ROTN2_ADDR,ROTN2_FACTORY); - writeSetting(ROTN3_ADDR,ROTN3_FACTORY); - writeSetting(ROTN4_ADDR,ROTN4_FACTORY); - writeSetting(PRIO_ADDR,PRIO_FACTORY); - writeSetting(VIB_SENS_ADDR,VIB_SENS_FACTORY); - writeSetting(VIB_RETN_ADDR,VIB_RETN_FACTORY); - writeSetting(VIB_SQUELCH_ADDR,VIB_SQUELCH_FACTORY); - writeSetting(VIB_DIRECTION_ADDR,VIB_DIRECTION_FACTORY); - writeSetting(BREATH_CC2_ADDR,BREATH_CC2_FACTORY); - writeSetting(BREATH_CC2_RISE_ADDR,BREATH_CC2_RISE_FACTORY); - writeSetting(VIB_SENS_BITE_ADDR,VIB_SENS_BITE_FACTORY); - writeSetting(VIB_SQUELCH_BITE_ADDR,VIB_SQUELCH_BITE_FACTORY); - writeSetting(VIB_CONTROL_ADDR,VIB_CONTROL_FACTORY); - } - // read settings from EEPROM - breathThrVal = readSetting(BREATH_THR_ADDR); - breathMaxVal = readSetting(BREATH_MAX_ADDR); - portamThrVal = readSetting(PORTAM_THR_ADDR); - portamMaxVal = readSetting(PORTAM_MAX_ADDR); - pitchbThrVal = readSetting(PITCHB_THR_ADDR); - pitchbMaxVal = readSetting(PITCHB_MAX_ADDR); - transpose = readSetting(TRANSP_ADDR); - MIDIchannel = readSetting(MIDI_ADDR); - breathCC = readSetting(BREATH_CC_ADDR); - breathAT = readSetting(BREATH_AT_ADDR); - velocity = readSetting(VELOCITY_ADDR); - portamento = readSetting(PORTAM_ADDR); - PBdepth = readSetting(PB_ADDR); - extraCT = readSetting(EXTRA_ADDR); - vibrato = readSetting(VIBRATO_ADDR); - deglitch = readSetting(DEGLITCH_ADDR); - extracThrVal = readSetting(EXTRAC_THR_ADDR); - extracMaxVal = readSetting(EXTRAC_MAX_ADDR); - patch = readSetting(PATCH_ADDR); - octave = readSetting(OCTAVE_ADDR); - ctouchThrVal = readSetting(CTOUCH_THR_ADDR); - curve = readSetting(BREATHCURVE_ADDR); - velSmpDl = readSetting(VEL_SMP_DL_ADDR); - velBias = readSetting(VEL_BIAS_ADDR); - pinkySetting = readSetting(PINKY_KEY_ADDR); - fastPatch[0] = readSetting(FP1_ADDR); - fastPatch[1] = readSetting(FP2_ADDR); - fastPatch[2] = readSetting(FP3_ADDR); - fastPatch[3] = readSetting(FP4_ADDR); - fastPatch[4] = readSetting(FP5_ADDR); - fastPatch[5] = readSetting(FP6_ADDR); - fastPatch[6] = readSetting(FP7_ADDR); - dipSwBits = readSetting(DIPSW_BITS_ADDR); - parallel = readSetting(PARAL_ADDR); - rotations[0] = readSetting(ROTN1_ADDR); - rotations[1] = readSetting(ROTN2_ADDR); - rotations[2] = readSetting(ROTN3_ADDR); - rotations[3] = readSetting(ROTN4_ADDR); - priority = readSetting(PRIO_ADDR); - vibSens = readSetting(VIB_SENS_ADDR); - vibRetn = readSetting(VIB_RETN_ADDR); - vibSquelch = readSetting(VIB_SQUELCH_ADDR); - vibDirection = readSetting(VIB_DIRECTION_ADDR); - breathCC2 = readSetting(BREATH_CC2_ADDR); - breathCC2Rise = readSetting(BREATH_CC2_RISE_ADDR); - vibSensBite = readSetting(VIB_SENS_BITE_ADDR); - vibSquelchBite = readSetting(VIB_SQUELCH_BITE_ADDR); - vibControl = readSetting(VIB_CONTROL_ADDR); - - legacy = dipSwBits & (1<<1); - legacyBrAct = dipSwBits & (1<<2); - slowMidi = dipSwBits & (1<<3); - gateOpenEnable = dipSwBits & (1<<4); - specialKeyEnable = dipSwBits & (1<<5); activePatch = patch; touch_Thr = map(ctouchThrVal,ctouchHiLimit,ctouchLoLimit,ttouchLoLimit,ttouchHiLimit); @@ -404,9 +288,10 @@ void setup() { vibZero += touchRead(vibratoPin); breathCalZero += analogRead(breathSensorPin); if (biteJumper) vibZeroBite += analogRead(A7); else vibZeroBite += touchRead(bitePin); - digitalWrite( statusLedPin, i&1 ); - delay(250); + statusLed(i&1); + delay(fastBoot?75:250); //Shorter delay for fastboot } + vibZero /= sampleCount; breathCalZero /= sampleCount; vibZeroBite /= sampleCount; @@ -416,15 +301,14 @@ void setup() { vibThrBite = vibZeroBite - vibSquelchBite; vibThrBiteLo = vibZeroBite + vibSquelchBite; - digitalWrite(statusLedPin, LOW); - delay(250); - digitalWrite(statusLedPin,HIGH); - delay(250); - digitalWrite(statusLedPin,LOW); + if(!fastBoot) { + statusLedFlash(500); + statusLedOff(); - showVersion(); + showVersion(); - delay(1500); + delay(1500); + } mainState = NOTE_OFF; // initialize main state machine @@ -438,7 +322,7 @@ void setup() { //Serial.begin(9600); // debug - digitalWrite(statusLedPin,HIGH); // Switch on the onboard LED to indicate power on/ready + statusLedOn(); // Switch on the onboard LED to indicate power on/ready } @@ -469,54 +353,37 @@ void loop() { mainState = RISE_WAIT; // Go to next state } if (legacy || legacyBrAct) { - #if defined(CASSIDY) - if (((pbUp > ((pitchbMaxVal + pitchbThrVal) / 2)) && (pbDn > ((pitchbMaxVal + pitchbThrVal) / 2)) && legacy) || - ((analogRead(breathSensorPin) < breathCalZero - 900) && legacyBrAct)) { // both pb pads touched or br suck - #else - if (((pbUp > ((pitchbMaxVal + pitchbThrVal) / 2)) && (pbDn > ((pitchbMaxVal + pitchbThrVal) / 2)) && legacy) || - ((analogRead(breathSensorPin) < breathCalZero - 800) && legacyBrAct && (pbUp > (pitchbMaxVal + pitchbThrVal) / 2) && (pbDn < (pitchbMaxVal + pitchbThrVal) / 2))) { // both pb pads touched or br suck - #endif + + bool bothPB = (pbUp > ((pitchbMaxVal + pitchbThrVal) / 2)) && (pbDn > ((pitchbMaxVal + pitchbThrVal) / 2)); + bool brSuck = analogRead(breathSensorPin) < (breathCalZero - (bcasMode?900:800)); + + if ( + (bothPB && legacy) || + (brSuck && legacyBrAct && (bothPB || bcasMode)) + ) { // both pb pads touched or br suck + + fingeredNoteUntransposed = patchLimit(fingeredNoteUntransposed + 1); if (exSensor >= ((extracThrVal + extracMaxVal) / 2)) { // instant midi setting if ((fingeredNoteUntransposed >= 73) && (fingeredNoteUntransposed <= 88)) { MIDIchannel = fingeredNoteUntransposed - 72; // Mid C and up - #if !defined(CASSIDY) - digitalWrite(statusLedPin, LOW); - delay(150); - digitalWrite(statusLedPin, HIGH); - #endif } } else { if (!pinkyKey) { // note number to patch number if (patch != fingeredNoteUntransposed) { patch = fingeredNoteUntransposed; doPatchUpdate = 1; - #if !defined(CASSIDY) - digitalWrite(statusLedPin, LOW); - delay(150); - digitalWrite(statusLedPin, HIGH); - #endif } } else { // hi and lo patch numbers if (fingeredNoteUntransposed > 75) { if (patch != patchLimit(fingeredNoteUntransposed + 24)) { patch = patchLimit(fingeredNoteUntransposed + 24); // add 24 to get high numbers 108 to 127 doPatchUpdate = 1; - #if !defined(CASSIDY) - digitalWrite(statusLedPin, LOW); - delay(150); - digitalWrite(statusLedPin, HIGH); - #endif } } else { if (patch != patchLimit(fingeredNoteUntransposed - 36)) { patch = patchLimit(fingeredNoteUntransposed - 36); // subtract 36 to get low numbers 0 to 36 doPatchUpdate = 1; - #if !defined(CASSIDY) - digitalWrite(statusLedPin, LOW); - delay(150); - digitalWrite(statusLedPin, HIGH); - #endif } } } @@ -817,7 +684,7 @@ void loop() { } else { if (slowMidi) breath(); extraController(); - statusLEDs(); + updateSensorLEDs(); doorKnobCheck(); } ccSendTime = millis(); @@ -827,14 +694,14 @@ void loop() { // this is one of the big reasons the display is for setup use only drawSensorPixels(); // live sensor monitoring for the setup screens if (rotatorOn || slurSustain || parallelChord || subOctaveDouble || gateOpen) { - digitalWrite(statusLedPin, !digitalRead(statusLedPin)); - } else if (!digitalRead(statusLedPin)) { - digitalWrite(statusLedPin, HIGH); + statusLedFlip(); + } else { + statusLedOn(); } pixelUpdateTime = millis(); } - #if defined(CVSCALEBOARD) // pitch CV from DAC and breath CV from PWM on pin 6, for filtering and scaling on separate board + if(dacMode == DAC_MODE_PITCH) { // pitch CV from DAC and breath CV from PWM on pin 6, for filtering and scaling on separate board targetPitch = (fingeredNote-24)*42; if (portIsOn){ if (targetPitch > cvPitch){ @@ -851,9 +718,9 @@ void loop() { } analogWrite(dacPin,constrain(cvPitch+map(pitchBend,0,16383,-84,84),0,4095)); analogWrite(pwmDacPin,breathCurve(map(constrain(pressureSensor,breathThrVal,breathMaxVal),breathThrVal,breathMaxVal,500,4095))); //starting at 0.6V to match use of cv from sensor, so recalibration of cv offset/scaler is not needed - #else // else breath CV on DAC pin, directly to unused pin of MIDI DIN jack + } else if(dacMode == DAC_MODE_BREATH) { // else breath CV on DAC pin, directly to unused pin of MIDI DIN jack analogWrite(dacPin,breathCurve(map(constrain(pressureSensor,breathThrVal,breathMaxVal),breathThrVal,breathMaxVal,0,4095))); - #endif + } midiDiscardInput(); @@ -908,23 +775,6 @@ int patchLimit(int value) { //************************************************************** -void statusLEDs() { - if (breathLevel > breathThrVal) { // breath indicator LED, labeled "B" on PCB - //analogWrite(bLedPin, map(breathLevel,0,4096,5,breathLedBrightness)); - analogWrite(bLedPin, map(constrain(breathLevel, breathThrVal, breathMaxVal), breathThrVal, breathMaxVal, 5, breathLedBrightness)); - } else { - analogWrite(bLedPin, 0); - } - if (portIsOn) { // portamento indicator LED, labeled "P" on PCB - //analogWrite(pLedPin, map(biteSensor,0,4096,5,portamLedBrightness)); - analogWrite(pLedPin, map(constrain(oldport, 0, 127), 0, 127, 5, portamLedBrightness)); - } else { - analogWrite(pLedPin, 0); - } -} - -//************************************************************** - void breath() { int breathCCval, breathCCvalFine,breathCC2val; unsigned int breathCCvalHires; @@ -970,8 +820,8 @@ void pitch_bend() { int vibMax; int calculatedPBdepth; byte pbTouched = 0; - int vibRead; - int vibReadBite; + int vibRead = 0; + int vibReadBite = 0; pbUp = touchRead(pbUpPin); // SENSOR PIN 23 - PCB PIN "Pu" pbDn = touchRead(pbDnPin); // SENSOR PIN 22 - PCB PIN "Pd" halfPitchBendKey = (pinkySetting == PBD) && (touchRead(halfPitchBendKeyPin) > touch_Thr); // SENSOR PIN 1 - PCB PIN "S1" - hold for 1/2 pitchbend value @@ -1071,10 +921,10 @@ void pitch_bend() { pitchBend = constrain(pitchBend, 0, 16383); if (subVibSquelch && (8192 != pitchBend)) { - digitalWrite(statusLedPin, LOW); + statusLedOff(); vibLedOff = 1; } else if (vibLedOff) { - digitalWrite(statusLedPin, HIGH); + statusLedOn(); vibLedOff = 0; } @@ -1099,25 +949,14 @@ void doorKnobCheck() { if ((touchValue[K4Pin] < ctouchThrVal) && (touchValue[R1Pin] < ctouchThrVal) && (touchValue[R2Pin] < ctouchThrVal) && (touchValue[R3Pin] < ctouchThrVal)) { // doorknob grip on canister if (!gateOpen && (pbUp > ((pitchbMaxVal + pitchbThrVal) / 2))) { gateOpen = 1; - digitalWrite(statusLedPin, LOW); - delay(50); - digitalWrite(statusLedPin, HIGH); - delay(50); + statusLedFlash(100); } else if (gateOpen && (pbDn > ((pitchbMaxVal + pitchbThrVal) / 2))) { gateOpen = 0; midiPanic(); - digitalWrite(statusLedPin, LOW); - delay(50); - digitalWrite(statusLedPin, HIGH); - delay(50); - digitalWrite(statusLedPin, LOW); - delay(50); - digitalWrite(statusLedPin, HIGH); - delay(50); - digitalWrite(statusLedPin, LOW); - delay(50); - digitalWrite(statusLedPin, HIGH); - delay(700); + statusLedFlash(100); + statusLedFlash(100); + statusLedFlash(100); + delay(600); } } } else if (gateOpen) { @@ -1258,62 +1097,44 @@ void portOff() { void readSwitches() { - int qTransp; - // Read touch pads (MPR121) and put value in variables - int touchValue[12]; + // Read touch pads (MPR121), compare against threshold value + bool touchKeys[12]; for (byte i = 0; i < 12; i++) { - touchValue[i] = touchSensor.filteredData(i); + touchKeys[i] = touchSensor.filteredData(i) < ctouchThrVal; } // Octave rollers octaveR = 0; - if ((touchValue[R5Pin] < ctouchThrVal) && (touchValue[R3Pin] < ctouchThrVal)) octaveR = 6; //R6 = R5 && R3 - else if (touchValue[R5Pin] < ctouchThrVal) octaveR = 5; //R5 - else if (touchValue[R4Pin] < ctouchThrVal) octaveR = 4; //R4 - else if ((touchValue[R3Pin] < ctouchThrVal) && lastOctaveR) octaveR = 3; //R3 - else if (touchValue[R2Pin] < ctouchThrVal) octaveR = 2; //R2 - else if (touchValue[R1Pin] < ctouchThrVal) octaveR = 1; //R1 + if (touchKeys[R5Pin] && touchKeys[R3Pin]) octaveR = 6; //R6 = R5 && R3 + else if (touchKeys[R5Pin]) octaveR = 5; //R5 + else if (touchKeys[R4Pin]) octaveR = 4; //R4 + else if (touchKeys[R3Pin] && lastOctaveR) octaveR = 3; //R3 + else if (touchKeys[R2Pin]) octaveR = 2; //R2 + else if (touchKeys[R1Pin]) octaveR = 1; //R1 lastOctaveR = octaveR; // Valves and trill keys - K4 = (touchValue[K4Pin] < ctouchThrVal); - K1 = (touchValue[K1Pin] < ctouchThrVal); - K2 = (touchValue[K2Pin] < ctouchThrVal); - K3 = (touchValue[K3Pin] < ctouchThrVal); - K5 = (touchValue[K5Pin] < ctouchThrVal); - K6 = (touchValue[K6Pin] < ctouchThrVal); - K7 = (touchValue[K7Pin] < ctouchThrVal); + K1 = touchKeys[K1Pin]; + K2 = touchKeys[K2Pin]; + K3 = touchKeys[K3Pin]; + K4 = touchKeys[K4Pin]; + K5 = touchKeys[K5Pin]; + K6 = touchKeys[K6Pin]; + K7 = touchKeys[K7Pin]; pinkyKey = (touchRead(halfPitchBendKeyPin) > touch_Thr); // SENSOR PIN 1 - PCB PIN "S1" - if ((pinkySetting < 12) && pinkyKey) { - qTransp = pinkySetting - 12; - } else if ((pinkySetting > 12) && pinkyKey) { - qTransp = pinkySetting - 12; - } else { - qTransp = 0; - } + int qTransp = pinkyKey ? pinkySetting-12 : 0; // Calculate midi note number from pressed keys - #if defined(CASSIDY) fingeredNoteUntransposed = startNote - 2*K1 - K2 - 3*K3 //"Trumpet valves" - 5*K4 //Fifth key - + 2*K5 + K6 + 3*K7 //Trill keys (different from standard) + + 2*K5 + K6 + trill3_interval*K7 //Trill keys. 3rd trill key interval controlled by setting + octaveR*12; //Octave rollers - #else - - fingeredNoteUntransposed = startNote - - 2*K1 - K2 - 3*K3 //"Trumpet valves" - - 5*K4 //Fifth key - + 2*K5 + K6 + 4*K7 //Trill keys - + octaveR*12; //Octave rollers - - #endif - int fingeredNoteRead = fingeredNoteUntransposed + transpose - 12 + qTransp; if (pinkyKey) pitchlatch = fingeredNoteUntransposed; //use pitchlatch to make settings based on note fingered diff --git a/NuEVI/config.h b/NuEVI/config.h index 86fae05..84520ea 100644 --- a/NuEVI/config.h +++ b/NuEVI/config.h @@ -5,14 +5,9 @@ // Compile options, comment/uncomment to change -#define FIRMWARE_VERSION "1.3.9" // FIRMWARE VERSION NUMBER HERE <<<<<<<<<<<<<<<<<<<<<<< - - -//#define CASSIDY -#define CVSCALEBOARD +#define FIRMWARE_VERSION "1.4.0" // FIRMWARE VERSION NUMBER HERE <<<<<<<<<<<<<<<<<<<<<<< #define ON_Delay 20 // Set Delay after ON threshold before velocity is checked (wait for tounging peak) -//#define touch_Thr 1200 // sensitivity for Teensy touch sensors #define CCN_Port 5 // Controller number for portamento level #define CCN_PortOnOff 65// Controller number for portamento on/off @@ -21,7 +16,6 @@ #define CC_INTERVAL 2 -// MAybe move these to config.h (as defines?) #define breathLoLimit 0 #define breathHiLimit 4095 #define portamLoLimit 700 @@ -36,4 +30,9 @@ #define ttouchHiLimit 1900 +#define MIN_LED_BRIGHTNESS 5 // lowest PWM value that still is visible +#define BREATH_LED_BRIGHTNESS 500 // up to 4095, PWM +#define PORTAM_LED_BRIGHTNESS 500 // up to 4095, PWM + + #endif diff --git a/NuEVI/globals.h b/NuEVI/globals.h index 56d0c41..03fabd6 100644 --- a/NuEVI/globals.h +++ b/NuEVI/globals.h @@ -73,7 +73,11 @@ extern byte currentRotation; extern uint16_t rotations[4]; extern uint16_t parallel; // semitones -extern uint16_t wlPower; +extern uint16_t bcasMode; //Legacy CASSIDY compile flag +extern uint16_t trill3_interval; +extern uint16_t fastBoot; +extern uint16_t dacMode; + extern int touch_Thr; @@ -112,6 +116,10 @@ extern int vibZeroBite; extern int vibThrBite; extern int vibThrBiteLo; +extern int breathLevel; +extern byte portIsOn; +extern int oldport; + // Key variables, TRUE (1) for pressed, FALSE (0) for not pressed extern byte K1; // Valve 1 (pitch change -2) extern byte K2; // Valve 2 (pitch change -1) diff --git a/NuEVI/led.cpp b/NuEVI/led.cpp new file mode 100644 index 0000000..0f0d2f4 --- /dev/null +++ b/NuEVI/led.cpp @@ -0,0 +1,48 @@ +#include +#include "hardware.h" +#include "globals.h" +#include "config.h" + +// Do things with status LED. +void statusLedOn() { + digitalWrite(statusLedPin, HIGH); +} + +void statusLedOff() { + digitalWrite(statusLedPin, LOW); +} + +void statusLed(bool state) { + digitalWrite(statusLedPin, state); +} + +void statusLedFlip() { + digitalWrite(statusLedPin, !digitalRead(statusLedPin)); +} + +void statusLedFlash(uint16_t delayTime) { + statusLedOff(); + delay(delayTime/2); + statusLedOn(); + delay(delayTime/2); +} + +void statusLedBlink() { + statusLedFlash(300); + statusLedFlash(300); +} + +void updateSensorLEDs() { + if (breathLevel > breathThrVal) { // breath indicator LED, labeled "B" on PCB + //analogWrite(bLedPin, map(breathLevel,0,4096,5,breathLedBrightness)); + analogWrite(bLedPin, map(constrain(breathLevel, breathThrVal, breathMaxVal), breathThrVal, breathMaxVal, MIN_LED_BRIGHTNESS, BREATH_LED_BRIGHTNESS)); + } else { + analogWrite(bLedPin, 0); + } + if (portIsOn) { // portamento indicator LED, labeled "P" on PCB + //analogWrite(pLedPin, map(biteSensor,0,4096,5,portamLedBrightness)); + analogWrite(pLedPin, map(constrain(oldport, 0, 127), 0, 127, MIN_LED_BRIGHTNESS, PORTAM_LED_BRIGHTNESS)); + } else { + analogWrite(pLedPin, 0); + } +} \ No newline at end of file diff --git a/NuEVI/led.h b/NuEVI/led.h new file mode 100644 index 0000000..cd893d1 --- /dev/null +++ b/NuEVI/led.h @@ -0,0 +1,12 @@ +#ifndef __LED_H +#define __LED_H + +void statusLedOn(); +void statusLedOff(); +void statusLedFlip(); +void statusLed(bool state); +void statusLedFlash(uint16_t delayTime); +void statusLedBlink(); +void updateSensorLEDs(); + +#endif \ No newline at end of file diff --git a/NuEVI/menu.cpp b/NuEVI/menu.cpp index a7c911b..5788e68 100644 --- a/NuEVI/menu.cpp +++ b/NuEVI/menu.cpp @@ -1,5 +1,3 @@ - -#include #include #include #include @@ -12,6 +10,7 @@ #include "globals.h" #include "midi.h" #include "numenu.h" +#include "led.h" enum CursorIdx { EMain, @@ -156,14 +155,6 @@ void initDisplay() { void showVersion() { display.setTextColor(WHITE); display.setTextSize(1); - #if defined(CASSIDY) - display.setCursor(0,0); - display.print("BC"); - #endif - #if defined(CVSCALEBOARD) - display.setCursor(15,0); - display.print("CV"); - #endif display.setCursor(85,52); display.print("v."); display.println(FIRMWARE_VERSION); @@ -374,32 +365,9 @@ void drawMenuCursor(byte itemNo, byte color){ //*********************************************************** -// TODO: Move these to a settings.cpp maybe? -void writeSetting(byte address, unsigned short value){ - union { - byte v[2]; - unsigned short val; - } data; - data.val = value; - EEPROM.update(address, data.v[0]); - EEPROM.update(address+1, data.v[1]); -} - -unsigned short readSetting(byte address){ - union { - byte v[2]; - unsigned short val; - } data; - data.v[0] = EEPROM.read(address); - data.v[1] = EEPROM.read(address+1); - return data.val; -} - -//*********************************************************** - static int readTrills() { readSwitches(); - return K5+2*K6+4*K7; + return K5+2*K6+trill3_interval*K7; } //*********************************************************** @@ -450,7 +418,6 @@ static void midiCustomDrawFunc(SubMenuRef __unused, char* __unused, const char** } } - //*********************************************************** const MenuEntrySub legacyPBMenu = { @@ -458,8 +425,7 @@ const MenuEntrySub legacyPBMenu = { [](SubMenuRef __unused, char* out, const char ** __unused unit) { strncpy(out, legacy?"ON":"OFF", 4); }, [](const MenuEntrySub & __unused sub) { - dipSwBits = dipSwBits & ~(1<<1); - dipSwBits |= (legacy <<1); + setBit(dipSwBits, DIPSW_LEGACY, legacy); writeSetting(DIPSW_BITS_ADDR,dipSwBits); } , nullptr @@ -470,9 +436,8 @@ const MenuEntrySub legacyBRMenu = { [](SubMenuRef __unused, char* out, const char ** __unused unit) { strncpy(out, legacyBrAct?"ON":"OFF", 4); }, [](const MenuEntrySub & __unused sub) { - dipSwBits = dipSwBits & ~(1<<2); - dipSwBits |= (legacyBrAct <<2); - writeSetting(DIPSW_BITS_ADDR,dipSwBits); + setBit(dipSwBits, DIPSW_LEGACYBRACT, legacyBrAct); + writeSetting(DIPSW_BITS_ADDR, dipSwBits); } , nullptr }; @@ -482,9 +447,8 @@ const MenuEntrySub gateOpenMenu = { [](SubMenuRef __unused, char* out, const char ** __unused unit) { strncpy(out, gateOpenEnable?"ON":"OFF", 4); }, [](const MenuEntrySub & __unused sub) { - dipSwBits = dipSwBits & ~(1<<4); - dipSwBits |= (gateOpenEnable <<4); - writeSetting(DIPSW_BITS_ADDR,dipSwBits); + setBit(dipSwBits, DIPSW_GATEOPEN, gateOpenEnable); + writeSetting(DIPSW_BITS_ADDR, dipSwBits); } , nullptr }; @@ -494,29 +458,89 @@ const MenuEntrySub specialKeyMenu = { [](SubMenuRef __unused, char* out, const char ** __unused unit) { strncpy(out, specialKeyEnable?"ON":"OFF", 4); }, [](const MenuEntrySub & __unused sub) { - dipSwBits = dipSwBits & ~(1<<5); - dipSwBits |= (specialKeyEnable <<5); - writeSetting(DIPSW_BITS_ADDR,dipSwBits); + setBit(dipSwBits, DIPSW_SPKEYENABLE, specialKeyEnable); + writeSetting(DIPSW_BITS_ADDR, dipSwBits); + } + , nullptr +}; + +const MenuEntrySub trill3Menu = { + MenuType::ESub, "3RD TRILL", "3RD TRILL", &trill3_interval, 3, 4, MenuEntryFlags::ENone, + [](SubMenuRef __unused, char* out, const char** __unused unit) { + numToString(trill3_interval, out, true); + }, + [](SubMenuRef __unused) { writeSetting(TRILL3_INTERVAL_ADDR, trill3_interval); } + , nullptr +}; + +const MenuEntrySub bcasModeMenu = { + MenuType::ESub, "BCAS MODE", "BCAS MODE", &bcasMode, 0, 1, MenuEntryFlags::ENone, + [](SubMenuRef __unused, char* out, const char** __unused unit) { + strncpy(out, bcasMode?"ON":"OFF", 4); + }, + [](SubMenuRef __unused) { + setBit(dipSwBits, DIPSW_BCASMODE, bcasMode); + writeSetting(DIPSW_BITS_ADDR, dipSwBits); + } + , nullptr +}; + +const MenuEntrySub dacModeMenu = { + MenuType::ESub, "DAC OUT", "DAC OUT", &dacMode, 0, 1, MenuEntryFlags::ENone, + [](SubMenuRef __unused, char* out, const char** __unused unit) { + const char* dacModeLabels[] = { "BRTH", "PTCH"}; + strncpy(out, dacModeLabels[dacMode], 5); + }, + [](SubMenuRef __unused) { writeSetting(DAC_MODE_ADDR, dacMode); } + , nullptr +}; + +const MenuEntrySub fastBootMenu = { + MenuType::ESub, "FAST BOOT", "FAST BOOT", &fastBoot, 0, 1, MenuEntryFlags::ENone, + [](SubMenuRef __unused, char* out, const char** __unused unit) { + strncpy(out, fastBoot?"ON":"OFF", 4); + }, + [](SubMenuRef __unused) { + setBit(dipSwBits, DIPSW_FASTBOOT, fastBoot); + writeSetting(DIPSW_BITS_ADDR, dipSwBits); } , nullptr }; +static uint16_t wireless_power=0; +static uint16_t wireless_channel=4; + const MenuEntrySub wlPowerMenu = { - MenuType::ESub, "WL POWER", "WL POWER", &wlPower, 0, 3, MenuEntryFlags::ENone, + MenuType::ESub, "WL POWER", "WL POWER", &wireless_power, 0, 3, MenuEntryFlags::ENone, [](SubMenuRef __unused, char* out, const char** __unused unit) { - numToString(-6*wlPower, out, true); + numToString(-6*wireless_power, out, true); }, - [](SubMenuRef __unused) { sendWLPower(wlPower); } + [](SubMenuRef __unused) { sendWLPower(wireless_power); } , nullptr }; +const MenuEntrySub wlChannelMenu = { + MenuType::ESub, "WL CHAN", "WL CHAN", &wireless_channel, 4, 80, MenuEntryFlags::ENone, + [](SubMenuRef __unused, char* out, const char** __unused unit) { + numToString(wireless_channel, out, false); + }, + [](SubMenuRef __unused) { sendWLChannel(wireless_channel); } + , nullptr +}; + + const MenuEntry* extrasMenuEntries[] = { (MenuEntry*)&legacyPBMenu, (MenuEntry*)&legacyBRMenu, (MenuEntry*)&gateOpenMenu, (MenuEntry*)&specialKeyMenu, + (MenuEntry*)&trill3Menu, + (MenuEntry*)&bcasModeMenu, + (MenuEntry*)&dacModeMenu, + (MenuEntry*)&fastBootMenu, (MenuEntry*)&wlPowerMenu, + (MenuEntry*)&wlChannelMenu, }; const MenuPage extrasMenuPage = { @@ -1205,17 +1229,6 @@ static bool updatePage(const MenuPage *page, KeyState &input, uint32_t timeNow) return redraw; } -//*********************************************************** -// This should be moved to a separate file/process that handles only led -static void statusBlink() { - digitalWrite(statusLedPin,LOW); - delay(150); - digitalWrite(statusLedPin,HIGH); - delay(150); - digitalWrite(statusLedPin,LOW); - delay(150); - digitalWrite(statusLedPin,HIGH); -} //*********************************************************** @@ -1368,12 +1381,12 @@ static bool idlePageUpdate(KeyState& __unused input, uint32_t __unused timeNow) legacyBrAct = !legacyBrAct; dipSwBits = dipSwBits ^ (1<<2); writeSetting(DIPSW_BITS_ADDR,dipSwBits); - statusBlink(); + statusLedBlink(); } else if ((exSensor >= ((extracThrVal+extracMaxVal)/2))) { // switch pb pad activated legacy settings control on/off legacy = !legacy; dipSwBits = dipSwBits ^ (1<<1); writeSetting(DIPSW_BITS_ADDR,dipSwBits); - statusBlink(); + statusLedBlink(); } else if (pinkyKey && !specialKey){ //hold pinky key for rotator menu, and if too high touch sensing blocks regular menu, touching special key helps display.ssd1306_command(SSD1306_DISPLAYON); menuState= ROTATOR_MENU; diff --git a/NuEVI/menu.h b/NuEVI/menu.h index 93567a8..20373ab 100644 --- a/NuEVI/menu.h +++ b/NuEVI/menu.h @@ -41,8 +41,6 @@ void initDisplay(); void showVersion(); void menu(); void drawSensorPixels(); -unsigned short readSetting(byte address); -void writeSetting(byte address, unsigned short value); int updateAdjustMenu(uint32_t timeNow, KeyState &input, bool firstRun, bool drawSensor); bool adjustPageUpdate(KeyState &input, uint32_t timeNow); diff --git a/NuEVI/midi.cpp b/NuEVI/midi.cpp index 8a49feb..56e8d74 100644 --- a/NuEVI/midi.cpp +++ b/NuEVI/midi.cpp @@ -163,4 +163,20 @@ void sendWLPower(const uint8_t level) { buf[5] = level; dinMIDIsendSysex(buf, 6); -} \ No newline at end of file +} + + +void sendWLChannel(const uint8_t channel) { + uint8_t buf[6] = { + 0x00, 0x21, 0x11, //Manufacturer id + 0x02, //TX02 + 0x05, //Set channel + 0x04 //Channel value (4-80) + }; + + if(channel<4 || channel>80) return; //Don't send invalid values + + buf[5] = channel; + dinMIDIsendSysex(buf, 6); + +} diff --git a/NuEVI/midi.h b/NuEVI/midi.h index 2b2e85f..800d1db 100644 --- a/NuEVI/midi.h +++ b/NuEVI/midi.h @@ -30,8 +30,7 @@ void dinMIDIsendProgramChange(uint8_t value, uint8_t ch); void dinMIDIsendPitchBend(uint16_t pb, uint8_t ch); void dinMIDIsendSysex(const uint8_t data[], const uint8_t length); - - void sendWLPower(const uint8_t level); +void sendWLChannel(const uint8_t channel); #endif diff --git a/NuEVI/settings.cpp b/NuEVI/settings.cpp new file mode 100644 index 0000000..913e06f --- /dev/null +++ b/NuEVI/settings.cpp @@ -0,0 +1,201 @@ +#include +#include + +#include "settings.h" +#include "globals.h" +#include "menu.h" +#include "hardware.h" +#include "config.h" + +//Read settings from eeprom. Returns wether or not anything was written (due to factory reset or upgrade) +void readEEPROM() { + bool factoryReset = !digitalRead(ePin) && !digitalRead(mPin); + + // if stored settings are not for current version, or Enter+Menu are pressed at startup, they are replaced by factory settings + uint16_t settingsVersion = readSetting(VERSION_ADDR); + + // blank eeprom will be 0xFFFF. For a full reset, call it "version 0" so everything gets overwritten. + if (factoryReset || settingsVersion == 0xffffu) { + settingsVersion = 0; + } + + + if(settingsVersion != EEPROM_VERSION) { + + if(settingsVersion < 24) { //Oldest version from which any settings are recognized + writeSetting(BREATH_THR_ADDR, BREATH_THR_FACTORY); + writeSetting(BREATH_MAX_ADDR, BREATH_MAX_FACTORY); + if (digitalRead(biteJumperPin)){ //PBITE (if pulled low with jumper, pressure sensor is used instead of capacitive bite sensing) + writeSetting(PORTAM_THR_ADDR, PORTAM_THR_FACTORY); + writeSetting(PORTAM_MAX_ADDR, PORTAM_MAX_FACTORY); + } else { + writeSetting(PORTAM_THR_ADDR, PORTPR_THR_FACTORY); + writeSetting(PORTAM_MAX_ADDR, PORTPR_MAX_FACTORY); + } + writeSetting(PITCHB_THR_ADDR, PITCHB_THR_FACTORY); + writeSetting(PITCHB_MAX_ADDR, PITCHB_MAX_FACTORY); + writeSetting(EXTRAC_THR_ADDR, EXTRAC_THR_FACTORY); + writeSetting(EXTRAC_MAX_ADDR, EXTRAC_MAX_FACTORY); + writeSetting(CTOUCH_THR_ADDR, CTOUCH_THR_FACTORY); + + writeSetting(TRANSP_ADDR, TRANSP_FACTORY); + writeSetting(MIDI_ADDR, MIDI_FACTORY); + writeSetting(BREATH_CC_ADDR, BREATH_CC_FACTORY); + writeSetting(BREATH_AT_ADDR, BREATH_AT_FACTORY); + writeSetting(VELOCITY_ADDR, VELOCITY_FACTORY); + writeSetting(PORTAM_ADDR, PORTAM_FACTORY); + writeSetting(PB_ADDR, PB_FACTORY); + writeSetting(EXTRA_ADDR, EXTRA_FACTORY); + writeSetting(VIBRATO_ADDR, VIBRATO_FACTORY); + writeSetting(DEGLITCH_ADDR, DEGLITCH_FACTORY); + writeSetting(PATCH_ADDR, PATCH_FACTORY); + writeSetting(OCTAVE_ADDR, OCTAVE_FACTORY); + writeSetting(BREATHCURVE_ADDR, BREATHCURVE_FACTORY); + writeSetting(VEL_SMP_DL_ADDR, VEL_SMP_DL_FACTORY); + writeSetting(VEL_BIAS_ADDR, VEL_BIAS_FACTORY); + writeSetting(PINKY_KEY_ADDR, PINKY_KEY_FACTORY); + } + + if(settingsVersion < 26) { + writeSetting(FP1_ADDR, 0); + writeSetting(FP2_ADDR, 0); + writeSetting(FP3_ADDR, 0); + writeSetting(FP4_ADDR, 0); + writeSetting(FP5_ADDR, 0); + writeSetting(FP6_ADDR, 0); + writeSetting(FP7_ADDR, 0); + writeSetting(DIPSW_BITS_ADDR, DIPSW_BITS_FACTORY); + } + + if(settingsVersion < 28) { + writeSetting(PARAL_ADDR, PARAL_FACTORY); + writeSetting(ROTN1_ADDR, ROTN1_FACTORY); + writeSetting(ROTN2_ADDR, ROTN2_FACTORY); + writeSetting(ROTN3_ADDR, ROTN3_FACTORY); + writeSetting(ROTN4_ADDR, ROTN4_FACTORY); + writeSetting(PRIO_ADDR, PRIO_FACTORY); + } + + if(settingsVersion < 29) { + writeSetting(VIB_SENS_ADDR, VIB_SENS_FACTORY); + writeSetting(VIB_RETN_ADDR, VIB_RETN_FACTORY); + } + + if(settingsVersion < 31) { + writeSetting(VIB_SQUELCH_ADDR, VIB_SQUELCH_FACTORY); + writeSetting(VIB_DIRECTION_ADDR, VIB_DIRECTION_FACTORY); + } + + if(settingsVersion < 32) { + writeSetting(BREATH_CC2_ADDR, BREATH_CC2_FACTORY); + writeSetting(BREATH_CC2_RISE_ADDR, BREATH_CC2_RISE_FACTORY); + writeSetting(VIB_SENS_BITE_ADDR, VIB_SENS_BITE_FACTORY); + writeSetting(VIB_SQUELCH_BITE_ADDR, VIB_SQUELCH_BITE_FACTORY); + writeSetting(VIB_CONTROL_ADDR, VIB_CONTROL_FACTORY); + writeSetting(TRILL3_INTERVAL_ADDR, TRILL3_INTERVAL_FACTORY); + writeSetting(DAC_MODE_ADDR, DAC_MODE_FACTORY); + } + + writeSetting(VERSION_ADDR, EEPROM_VERSION); + } + + // read all settings from EEPROM + breathThrVal = readSettingBounded(BREATH_THR_ADDR, breathLoLimit, breathHiLimit, BREATH_THR_FACTORY); + breathMaxVal = readSettingBounded(BREATH_MAX_ADDR, breathLoLimit, breathHiLimit, BREATH_MAX_FACTORY); + portamThrVal = readSettingBounded(PORTAM_THR_ADDR, portamLoLimit, portamHiLimit, PORTAM_THR_FACTORY); + portamMaxVal = readSettingBounded(PORTAM_MAX_ADDR, portamLoLimit, portamHiLimit, PORTAM_MAX_FACTORY); + pitchbThrVal = readSettingBounded(PITCHB_THR_ADDR, pitchbLoLimit, pitchbHiLimit, PITCHB_THR_FACTORY); + pitchbMaxVal = readSettingBounded(PITCHB_MAX_ADDR, pitchbLoLimit, pitchbHiLimit, PITCHB_MAX_FACTORY); + transpose = readSettingBounded(TRANSP_ADDR, 0, 24, TRANSP_FACTORY); + MIDIchannel = readSettingBounded(MIDI_ADDR, 1, 16, MIDI_FACTORY); + breathCC = readSettingBounded(BREATH_CC_ADDR, 0, 127, BREATH_CC_FACTORY); + breathAT = readSettingBounded(BREATH_AT_ADDR, 0, 1, BREATH_AT_FACTORY); + velocity = readSettingBounded(VELOCITY_ADDR, 0, 127, VELOCITY_FACTORY); + portamento = readSettingBounded(PORTAM_ADDR, 0, 2, PORTAM_FACTORY); + PBdepth = readSettingBounded(PB_ADDR, 0, 12, PB_FACTORY); + extraCT = readSettingBounded(EXTRA_ADDR, 0, 4, EXTRA_FACTORY); + vibrato = readSettingBounded(VIBRATO_ADDR, 0, 9, VIBRATO_FACTORY); + deglitch = readSettingBounded(DEGLITCH_ADDR, 0, 70, DEGLITCH_FACTORY); + extracThrVal = readSettingBounded(EXTRAC_THR_ADDR, extracLoLimit, extracHiLimit, EXTRAC_THR_FACTORY); + extracMaxVal = readSettingBounded(EXTRAC_MAX_ADDR, extracLoLimit, extracHiLimit, EXTRAC_MAX_FACTORY); + patch = readSettingBounded(PATCH_ADDR, 0, 127, PATCH_FACTORY); + octave = readSettingBounded(OCTAVE_ADDR, 0, 6, OCTAVE_FACTORY); + ctouchThrVal = readSettingBounded(CTOUCH_THR_ADDR, ctouchLoLimit, ctouchHiLimit, CTOUCH_THR_FACTORY); + curve = readSettingBounded(BREATHCURVE_ADDR, 0, 12, BREATHCURVE_FACTORY); + velSmpDl = readSettingBounded(VEL_SMP_DL_ADDR, 0, 30, VEL_SMP_DL_FACTORY); + velBias = readSettingBounded(VEL_BIAS_ADDR, 0, 9, VEL_BIAS_FACTORY); + pinkySetting = readSettingBounded(PINKY_KEY_ADDR, 0, 24, PINKY_KEY_FACTORY); + fastPatch[0] = readSettingBounded(FP1_ADDR, 0, 127, 0); + fastPatch[1] = readSettingBounded(FP2_ADDR, 0, 127, 0); + fastPatch[2] = readSettingBounded(FP3_ADDR, 0, 127, 0); + fastPatch[3] = readSettingBounded(FP4_ADDR, 0, 127, 0); + fastPatch[4] = readSettingBounded(FP5_ADDR, 0, 127, 0); + fastPatch[5] = readSettingBounded(FP6_ADDR, 0, 127, 0); + fastPatch[6] = readSettingBounded(FP7_ADDR, 0, 127, 0); + dipSwBits = readSetting(DIPSW_BITS_ADDR); + parallel = readSettingBounded(PARAL_ADDR, 0, 48, PARAL_FACTORY); + rotations[0] = readSettingBounded(ROTN1_ADDR, 0, 48, ROTN1_FACTORY); + rotations[1] = readSettingBounded(ROTN2_ADDR, 0, 48, ROTN2_FACTORY); + rotations[2] = readSettingBounded(ROTN3_ADDR, 0, 48, ROTN3_FACTORY); + rotations[3] = readSettingBounded(ROTN4_ADDR, 0, 48, ROTN4_FACTORY); + priority = readSettingBounded(PRIO_ADDR, 0, 1, PRIO_FACTORY); + vibSens = readSettingBounded(VIB_SENS_ADDR, 1, 12, VIB_SENS_FACTORY); + vibRetn = readSettingBounded(VIB_RETN_ADDR, 0, 4, VIB_RETN_FACTORY); + vibSquelch = readSettingBounded(VIB_SQUELCH_ADDR, 1, 30, VIB_SQUELCH_FACTORY); + vibDirection = readSettingBounded(VIB_DIRECTION_ADDR, 0, 1, VIB_DIRECTION_FACTORY); + breathCC2 = readSettingBounded(BREATH_CC2_ADDR, 0, 127, BREATH_CC2_FACTORY); + breathCC2Rise = readSettingBounded(BREATH_CC2_RISE_ADDR, 1, 10, BREATH_CC2_RISE_FACTORY); + vibSensBite = readSettingBounded(VIB_SENS_BITE_ADDR, 1, 12, VIB_SENS_BITE_FACTORY); + vibSquelchBite = readSettingBounded(VIB_SQUELCH_BITE_ADDR, 1, 30, VIB_SQUELCH_BITE_FACTORY); + vibControl = readSettingBounded(VIB_CONTROL_ADDR, 0, 1, VIB_CONTROL_FACTORY); + dacMode = readSettingBounded(DAC_MODE_ADDR, DAC_MODE_BREATH, DAC_MODE_PITCH, DAC_MODE_FACTORY); + trill3_interval = readSettingBounded(TRILL3_INTERVAL_ADDR, 3, 4, TRILL3_INTERVAL_FACTORY); + + //Flags stored in bit field + fastBoot = (dipSwBits & (1< max) { + val = defaultValue; + writeSetting(address, val); + } + return val; +} diff --git a/NuEVI/settings.h b/NuEVI/settings.h index bc155e8..cab78ba 100644 --- a/NuEVI/settings.h +++ b/NuEVI/settings.h @@ -1,8 +1,7 @@ - #ifndef __SETTINGS_H #define __SETTINGS_H - +#include // EEPROM addresses for settings #define VERSION_ADDR 0 @@ -54,9 +53,26 @@ #define VIB_SENS_BITE_ADDR 92 #define VIB_SQUELCH_BITE_ADDR 94 #define VIB_CONTROL_ADDR 96 +#define TRILL3_INTERVAL_ADDR 98 +#define DAC_MODE_ADDR 100 + + + +//DAC output modes +#define DAC_MODE_BREATH 0 +#define DAC_MODE_PITCH 1 + +#define DIPSW_FASTBOOT 0 +#define DIPSW_LEGACY 1 +#define DIPSW_LEGACYBRACT 2 +#define DIPSW_SLOWMIDI 3 +#define DIPSW_GATEOPEN 4 +#define DIPSW_SPKEYENABLE 5 +#define DIPSW_BCASMODE 6 + //"factory" values for settings -#define VERSION 32 +#define EEPROM_VERSION 32 #define BREATH_THR_FACTORY 1400 #define BREATH_MAX_FACTORY 4000 #define PORTAM_THR_FACTORY 2600 @@ -101,4 +117,15 @@ #define VIB_SQUELCH_BITE_FACTORY 10 #define VIB_CONTROL_FACTORY 0 +#define TRILL3_INTERVAL_FACTORY 4 +#define DAC_MODE_FACTORY DAC_MODE_BREATH + + +void readEEPROM(); +void setBit(uint16_t &bitfield, const uint8_t pos, const uint16_t value); +uint16_t readSetting(uint16_t address); +void writeSetting(uint16_t address, uint16_t value); +uint16_t readSettingBounded(uint16_t address, uint16_t min, uint16_t max, uint16_t defaultValue); + + #endif diff --git a/README.md b/README.md index c8f04fb..d39ee77 100644 --- a/README.md +++ b/README.md @@ -16,11 +16,7 @@ added directly via the Library Manager in the Arduino IDE: * Adafruit MPR121 * Adafruit GFX * Adafruit SSD1306 (version 1.2.9 or above) - -You also need to install [Edgar Bonet's Filters library](https://github.com/edgar-bonet/Filters), -specifically the `fix-integer-overflow` branch. One of the easiest way to do that is to download the -git repo [as a zip file](https://github.com/edgar-bonet/Filters/archive/fix-integer-overflow.zip), -and then add that in the Arduino IDE (under Sketch -> Include Library -> Add .ZIP library) +* NuEVI also includes on the [Filters](https://github.com/JonHub/Filters) library by Jonathan Driscoll, but that is no longer an external dependency. ### Compile options diff --git a/simulation/Makefile b/simulation/Makefile index 640a642..1a603dd 100644 --- a/simulation/Makefile +++ b/simulation/Makefile @@ -17,7 +17,7 @@ CXXFLAGS= $(CFLAGS) -std=c++14 LIBS=-framework SDL2 -lc++ -lc -framework OpenGL LDFLAGS=-macosx_version_min 10.9 -rpath @executable_path/../Frameworks -SYSINC = ~/Documents/Arduino/libraries/Filters ./include +SYSINC = ./include INCS = ../NuEVI ./include ./imgui ./gl3w INCDIRS = $(addprefix -isystem ,$(SYSINC)) @@ -28,6 +28,9 @@ TARGET=nuevisim CXXFILES= ../NuEVI/menu.cpp \ ../NuEVI/adjustmenu.cpp \ + ../NuEVI/midi.cpp \ + ../NuEVI/settings.cpp \ + ../NuEVI/led.cpp \ src/nuevisim.cpp \ src/simeeprom.cpp \ src/Print.cpp \ @@ -35,7 +38,6 @@ CXXFILES= ../NuEVI/menu.cpp \ src/simwire.cpp \ src/simusbmidi.cpp \ src/filters.cpp \ - ../NuEVI/midi.cpp \ src/Adafruit_GFX_sim.cpp \ src/Adafruit_SSD1306_sim.cpp \ src/Adafruit_MPR121_sim.cpp \ @@ -45,6 +47,7 @@ CXXFILES= ../NuEVI/menu.cpp \ imgui/examples/imgui_impl_sdl.cpp \ imgui/examples/imgui_impl_opengl3.cpp + CFILES= gl3w/gl3w.c OBJS=$(CXXFILES:.cpp=.o) $(CFILES:.c=.o) diff --git a/simulation/src/filters.cpp b/simulation/src/filters.cpp index 6f7eed9..2860caf 100644 --- a/simulation/src/filters.cpp +++ b/simulation/src/filters.cpp @@ -1,3 +1,3 @@ #include -#include "FilterOnepole.cpp" +#include "FilterOnePole.cpp"