First commit. Arduino sketch plus devboard plans.
This commit is contained in:
commit
d26ae04e09
7 changed files with 7128 additions and 0 deletions
339
NuEVI.ino
Normal file
339
NuEVI.ino
Normal file
|
@ -0,0 +1,339 @@
|
|||
#include <Wire.h>
|
||||
#include <Adafruit_MPR121.h>
|
||||
|
||||
/*
|
||||
NAME: NuEVI
|
||||
WRITTEN BY: JOHAN BERGLUND
|
||||
DATE: 2017-08-08
|
||||
FILE SAVED AS: NuEVI.ino
|
||||
FOR: PJRC Teensy LC or 3.2 and a MPR121 capactive touch sensor board
|
||||
PROGRAMME FUNCTION: EVI Wind Controller using the Freescale MP3V5004GP breath sensor
|
||||
and capacitive touch keys. Output to both USB MIDI and DIN MIDI.
|
||||
|
||||
*/
|
||||
|
||||
//_______________________________________________________________________________________________ DECLARATIONS
|
||||
|
||||
#define ON_Thr 370 // Set threshold level before switching ON
|
||||
#define ON_Delay 20 // Set Delay after ON threshold before velocity is checked (wait for tounging peak)
|
||||
#define breath_max 1023 // Maximum breath level
|
||||
#define PB_sens 8191 // Pitch Bend sensitivity 0 to 8191 where 8191 is full pb range, 4095 half range
|
||||
#define VIB_depth 1023 // Vibrato depth 0 to 8191
|
||||
#define touch_Thr 1800 // sensitivity for Teensy touch sensors
|
||||
#define CCN_Port 5 // Controller number for portamento level
|
||||
#define CCN_PortOnOff 65// Controller number for portamento on/off
|
||||
|
||||
|
||||
// Send CC data no more than every CC_INTERVAL
|
||||
// milliseconds
|
||||
#define CC_INTERVAL 5
|
||||
|
||||
// The three states of our state machine
|
||||
|
||||
// No note is sounding
|
||||
#define NOTE_OFF 1
|
||||
|
||||
// We've observed a transition from below to above the
|
||||
// threshold value. We wait a while to see how fast the
|
||||
// breath velocity is increasing
|
||||
#define RISE_WAIT 2
|
||||
|
||||
// A note is sounding
|
||||
#define NOTE_ON 3
|
||||
|
||||
|
||||
//variables setup
|
||||
|
||||
int state; // The state of the state machine
|
||||
unsigned long ccSendTime = 0L; // The last time we sent CC values
|
||||
unsigned long breath_on_time = 0L; // Time when breath sensor value went over the ON threshold
|
||||
int initial_breath_value; // The breath value at the time we observed the transition
|
||||
|
||||
unsigned long lastDebounceTime = 0;// The last time the fingering was changed
|
||||
unsigned long debounceDelay = 10; // The debounce time; increase if the output flickers
|
||||
int lastFingering = 0; // Keep the last fingering value for debouncing
|
||||
|
||||
byte MIDIchannel=1; // MIDI channel 1
|
||||
|
||||
int breathLevel=0; // breath level (smoothed) not mapped to CC value
|
||||
int oldbreath=0;
|
||||
|
||||
int pressureSensor; // pressure data from breath sensor, for midi breath cc and breath threshold checks
|
||||
byte velocity; // remapped midi velocity from breath sensor
|
||||
|
||||
int biteSensor=0; // capacitance data from bite sensor, for midi cc and threshold checks
|
||||
byte portIsOn=0; // keep track and make sure we send CC with 0 value when off threshold
|
||||
int biteThr=1730; // Set threshold level before switching ON (value to use if no pots installed)
|
||||
int biteMax=3300; // Upper limit for pressure (value to use if no pots installed)
|
||||
|
||||
int pitchBend=0;
|
||||
int oldpb=8192;
|
||||
int pbThr=1200;
|
||||
int pbMax=2400;
|
||||
|
||||
int vibThr=1800;
|
||||
int oldvibRead=0;
|
||||
|
||||
int fingeredNote; // note calculated from fingering (switches) and octave joystick position
|
||||
byte activeNote; // note playing
|
||||
byte startNote=36; // set startNote to C (change this value in steps of 12 to start in other octaves)
|
||||
|
||||
Adafruit_MPR121 touchSensor = Adafruit_MPR121(); // This is the 12-input touch sensor
|
||||
|
||||
|
||||
//_______________________________________________________________________________________________ SETUP
|
||||
|
||||
void setup() {
|
||||
|
||||
state = NOTE_OFF; // initialize state machine
|
||||
if (!touchSensor.begin(0x5A)) {
|
||||
while (1); // Touch sensor initialization failed - stop doing stuff
|
||||
}
|
||||
Serial3.begin(31250); // start serial with midi baudrate 31250
|
||||
Serial3.flush();
|
||||
|
||||
}
|
||||
|
||||
//_______________________________________________________________________________________________ MAIN LOOP
|
||||
|
||||
void loop() {
|
||||
pressureSensor = analogRead(A0); // Get the pressure sensor reading from analog pin A0
|
||||
|
||||
if (state == NOTE_OFF) {
|
||||
if (pressureSensor > ON_Thr) {
|
||||
// Value has risen above threshold. Move to the ON_Delay
|
||||
// state. Record time and initial breath value.
|
||||
breath_on_time = millis();
|
||||
initial_breath_value = pressureSensor;
|
||||
state = RISE_WAIT; // Go to next state
|
||||
}
|
||||
} else if (state == RISE_WAIT) {
|
||||
if (pressureSensor > ON_Thr) {
|
||||
// Has enough time passed for us to collect our second
|
||||
// sample?
|
||||
if (millis() - breath_on_time > ON_Delay) {
|
||||
// Yes, so calculate MIDI note and velocity, then send a note on event
|
||||
readSwitches();
|
||||
//fingeredNote = startNote + 24;
|
||||
// We should be at tonguing peak, so set velocity based on current pressureSensor value
|
||||
// If initial value is greater than value after delay, go with initial value, constrain input to keep mapped output within 1 to 127
|
||||
velocity = map(constrain(max(pressureSensor,initial_breath_value),ON_Thr,breath_max),ON_Thr,breath_max,1,127);
|
||||
breathLevel=constrain(max(pressureSensor,initial_breath_value),ON_Thr,breath_max);
|
||||
breath(); // send breath data
|
||||
usbMIDI.sendNoteOn(fingeredNote, velocity, MIDIchannel); // send Note On message for new note
|
||||
dinMIDIsendNoteOn(fingeredNote, velocity, MIDIchannel - 1);
|
||||
activeNote=fingeredNote;
|
||||
state = NOTE_ON;
|
||||
}
|
||||
} else {
|
||||
// Value fell below threshold before ON_Delay passed. Return to
|
||||
// NOTE_OFF state (e.g. we're ignoring a short blip of breath)
|
||||
state = NOTE_OFF;
|
||||
}
|
||||
} else if (state == NOTE_ON) {
|
||||
if (pressureSensor < ON_Thr) {
|
||||
// Value has fallen below threshold - turn the note off
|
||||
usbMIDI.sendNoteOff(activeNote, velocity, MIDIchannel); // send Note Off message
|
||||
dinMIDIsendNoteOff(activeNote, velocity, MIDIchannel - 1);
|
||||
breathLevel=0;
|
||||
state = NOTE_OFF;
|
||||
} else {
|
||||
readSwitches();
|
||||
//fingeredNote = startNote + 24;
|
||||
if (fingeredNote != lastFingering){ //
|
||||
// reset the debouncing timer
|
||||
lastDebounceTime = millis();
|
||||
}
|
||||
if ((millis() - lastDebounceTime) > debounceDelay) {
|
||||
// whatever the reading is at, it's been there for longer
|
||||
// than the debounce delay, so take it as the actual current state
|
||||
if (fingeredNote != activeNote) {
|
||||
// Player has moved to a new fingering while still blowing.
|
||||
// Send a note off for the current note and a note on for
|
||||
// the new note.
|
||||
velocity = map(constrain(pressureSensor,ON_Thr,breath_max),ON_Thr,breath_max,7,127); // set new velocity value based on current pressure sensor level
|
||||
usbMIDI.sendNoteOn(fingeredNote, velocity, MIDIchannel); // send Note On message for new note
|
||||
dinMIDIsendNoteOn(fingeredNote, velocity, MIDIchannel - 1);
|
||||
usbMIDI.sendNoteOff(activeNote, 0, MIDIchannel); // send Note Off message for previous note (legato)
|
||||
dinMIDIsendNoteOff(activeNote, 0, MIDIchannel - 1);
|
||||
activeNote=fingeredNote;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Is it time to send more CC data?
|
||||
if (millis() - ccSendTime > CC_INTERVAL) {
|
||||
// deal with Breath, Pitch Bend and Modulation
|
||||
breath();
|
||||
pitch_bend();
|
||||
portamento();
|
||||
ccSendTime = millis();
|
||||
}
|
||||
lastFingering=fingeredNote;
|
||||
}
|
||||
//_______________________________________________________________________________________________ FUNCTIONS
|
||||
|
||||
// Send a three byte din midi message
|
||||
void midiSend(byte midistatus, byte data1, byte data2) {
|
||||
Serial3.write(midistatus);
|
||||
Serial3.write(data1);
|
||||
Serial3.write(data2);
|
||||
}
|
||||
|
||||
//**************************************************************
|
||||
|
||||
// Send din pitchbend
|
||||
void dinMIDIsendPitchBend(int pb, byte ch) {
|
||||
int pitchLSB = pb & 0x007F;
|
||||
int pitchMSB = (pb >>7) & 0x007F;
|
||||
midiSend((0xE0 | ch), pitchLSB, pitchMSB);
|
||||
}
|
||||
|
||||
//**************************************************************
|
||||
|
||||
// Send din control change
|
||||
void dinMIDIsendControlChange(byte ccNumber, int cc, byte ch) {
|
||||
midiSend((0xB0 | ch), ccNumber, cc);
|
||||
}
|
||||
|
||||
//**************************************************************
|
||||
|
||||
// Send din note on
|
||||
void dinMIDIsendNoteOn(byte note, int vel, byte ch) {
|
||||
midiSend((0x90 | ch), note, vel);
|
||||
}
|
||||
|
||||
//**************************************************************
|
||||
|
||||
// Send din note off
|
||||
void dinMIDIsendNoteOff(byte note, int vel, byte ch) {
|
||||
midiSend((0x80 | ch), note, vel);
|
||||
}
|
||||
|
||||
//**************************************************************
|
||||
|
||||
void breath(){
|
||||
int breathCC;
|
||||
breathLevel = breathLevel*0.8+pressureSensor*0.2; // smoothing of breathLevel value
|
||||
breathCC = map(constrain(breathLevel,ON_Thr,breath_max),ON_Thr,breath_max,0,127);
|
||||
if (breathCC != oldbreath){ // only send midi data if breath has changed from previous value
|
||||
usbMIDI.sendControlChange(2, breathCC, MIDIchannel);
|
||||
dinMIDIsendControlChange(2, breathCC, MIDIchannel - 1);
|
||||
oldbreath = breathCC;
|
||||
}
|
||||
}
|
||||
|
||||
//**************************************************************
|
||||
|
||||
void pitch_bend(){
|
||||
int pbUp = touchRead(23);
|
||||
int pbDn = touchRead(22);
|
||||
int vibRead = touchRead(1);
|
||||
if ((vibRead > vibThr)&&(vibRead > (oldvibRead+7))){
|
||||
pitchBend=oldpb*0.7+0.3*(8192 + VIB_depth);
|
||||
} else if ((vibRead > vibThr)&&(vibRead < (oldvibRead-7))){
|
||||
pitchBend=oldpb*0.7+0.3*(8191 - VIB_depth);
|
||||
} else {
|
||||
pitchBend = oldpb*0.4+8192*0.6; // released, so smooth your way back to zero
|
||||
if ((pitchBend > 8187) && (pitchBend < 8197)) pitchBend = 8192; // 8192 is 0 pitch bend, don't miss it bc of smoothing
|
||||
}
|
||||
oldvibRead = vibRead;
|
||||
if (pbUp > pbThr){
|
||||
pitchBend=pitchBend*0.8+0.2*map(constrain(pbUp,pbThr,pbMax),pbThr,pbMax,8192,(8192 + PB_sens));
|
||||
} else if (pbDn > pbThr){
|
||||
pitchBend=pitchBend*0.8+0.2*map(constrain(pbDn,pbThr,pbMax),pbThr,pbMax,8192,(8191 - PB_sens));
|
||||
} else if (oldvibRead < vibThr){
|
||||
pitchBend = pitchBend*0.8+8192*0.2; // released, so smooth your way back to zero
|
||||
if ((pitchBend > 8187) && (pitchBend < 8197)) pitchBend = 8192; // 8192 is 0 pitch bend, don't miss it bc of smoothing
|
||||
}
|
||||
pitchBend=constrain(pitchBend, 0, 16383);
|
||||
if (pitchBend != oldpb){// only send midi data if pitch bend has changed from previous value
|
||||
usbMIDI.sendPitchBend(pitchBend, MIDIchannel);
|
||||
dinMIDIsendPitchBend(pitchBend, MIDIchannel - 1);
|
||||
oldpb=pitchBend;
|
||||
}
|
||||
}
|
||||
|
||||
//***********************************************************
|
||||
|
||||
void portamento(){
|
||||
biteSensor=biteSensor*0.6+0.4*touchRead(0); // get sensor data, do some smoothing
|
||||
if (biteSensor >= biteThr) { // if we are over the threshold, send portamento
|
||||
if (!portIsOn) {
|
||||
portOn();
|
||||
}
|
||||
port();
|
||||
} else if (portIsOn) { // we have just gone below threshold, so send zero value
|
||||
portOff();
|
||||
}
|
||||
}
|
||||
|
||||
//***********************************************************
|
||||
|
||||
void portOn(){
|
||||
usbMIDI.sendControlChange(CCN_PortOnOff, 127, MIDIchannel);
|
||||
dinMIDIsendControlChange(CCN_PortOnOff, 127, MIDIchannel - 1);
|
||||
portIsOn=1;
|
||||
}
|
||||
|
||||
//***********************************************************
|
||||
|
||||
void port(){
|
||||
int portCC;
|
||||
portCC = map(constrain(biteSensor,biteThr,biteMax),biteThr,biteMax,1,127);
|
||||
usbMIDI.sendControlChange(CCN_Port, portCC, MIDIchannel);
|
||||
dinMIDIsendControlChange(CCN_Port, portCC, MIDIchannel - 1);
|
||||
}
|
||||
|
||||
//***********************************************************
|
||||
|
||||
void portOff(){
|
||||
usbMIDI.sendControlChange(CCN_Port, 0, MIDIchannel);
|
||||
dinMIDIsendControlChange(CCN_Port, 0, MIDIchannel - 1);
|
||||
usbMIDI.sendControlChange(CCN_PortOnOff, 0, MIDIchannel);
|
||||
dinMIDIsendControlChange(CCN_PortOnOff, 0, MIDIchannel - 1);
|
||||
portIsOn=0;
|
||||
}
|
||||
|
||||
//***********************************************************
|
||||
|
||||
void readSwitches(){
|
||||
|
||||
// Key variables, TRUE (1) for pressed, FALSE (0) for not pressed
|
||||
byte K1; // Valve 1 (pitch change -2)
|
||||
byte K2; // Valve 2 (pitch change -1)
|
||||
byte K3; // Valve 3 (pitch change -3)
|
||||
byte K4; // Left Hand index finger (pitch change -5)
|
||||
byte K5; // Trill key 1 (pitch change +2)
|
||||
byte K6; // Trill key 2 (pitch change +1)
|
||||
byte K7; // Trill key 3 (pitch change +4)
|
||||
|
||||
byte octave = 0;
|
||||
|
||||
// Read touch pads (MPR121) and put value in variables
|
||||
uint16_t touchValue = touchSensor.touched();
|
||||
|
||||
// Octave rollers
|
||||
if ((touchValue >> 5) & 0x01) octave = 6;
|
||||
else if ((touchValue >> 4) & 0x01) octave = 5;
|
||||
else if ((touchValue >> 3) & 0x01) octave = 4;
|
||||
else if ((touchValue >> 2) & 0x01) octave = 3;
|
||||
else if ((touchValue >> 1) & 0x01) octave = 2;
|
||||
else if ((touchValue >> 0) & 0x01) octave = 1;
|
||||
|
||||
// Valves and trill keys
|
||||
K1=((touchValue >> 6) & 0x01);
|
||||
K2=((touchValue >> 7) & 0x01);
|
||||
K3=((touchValue >> 8) & 0x01);
|
||||
K5=((touchValue >> 9) & 0x01);
|
||||
K6=((touchValue >> 10) & 0x01);
|
||||
K7=((touchValue >> 11) & 0x01);
|
||||
// Read touch pads (Teensy built in) and put value in variables
|
||||
K4=touchRead(15) > touch_Thr;
|
||||
|
||||
// Calculate midi note number from pressed keys
|
||||
fingeredNote=startNote-2*K1-K2-3*K3-5*K4+2*K5+K6+4*K7+octave*12;
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue