1501 lines
59 KiB
C++
1501 lines
59 KiB
C++
#include <Wire.h>
|
|
|
|
#include <Adafruit_MPR121.h>
|
|
#include <SPI.h>
|
|
#include <EEPROM.h>
|
|
#include <Adafruit_GFX.h>
|
|
#include <Adafruit_SSD1306.h>
|
|
#include <Filters.h> // for the breath signal LP filtering, https://github.com/edgar-bonet/Filters
|
|
|
|
#include "hardware.h"
|
|
#include "midi.h"
|
|
#include "menu.h"
|
|
|
|
/*
|
|
NAME: NuEVI
|
|
WRITTEN BY: JOHAN BERGLUND
|
|
DATE: 2018-04-19
|
|
FILE SAVED AS: NuEVI.ino
|
|
FOR: PJRC Teensy 3.2 and a MPR121 capactive touch sensor board.
|
|
Uses an SSD1306 controlled OLED display communicating over I2C.
|
|
PROGRAMME FUNCTION: EVI Wind Controller using the Freescale MP3V5004GP breath sensor
|
|
and capacitive touch keys. Output to both USB MIDI and DIN MIDI.
|
|
|
|
*/
|
|
|
|
//_______________________________________________________________________________________________ DECLARATIONS
|
|
|
|
// Compile options, comment/uncomment to change
|
|
|
|
#define FIRMWARE_VERSION "1.3.2" // FIRMWARE VERSION NUMBER HERE <<<<<<<<<<<<<<<<<<<<<<<
|
|
|
|
|
|
//#define CASSIDY
|
|
|
|
|
|
#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
|
|
|
|
|
|
// Send breath CC data no more than every CC_INTERVAL (other CC is sent with double interval)
|
|
// milliseconds (due to timing errors, the value should be about half the actual wanted value)
|
|
#define CC_INTERVAL 2
|
|
|
|
|
|
// The three states of our main 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
|
|
|
|
|
|
// EEPROM addresses for settings
|
|
#define VERSION_ADDR 0
|
|
#define BREATH_THR_ADDR 2
|
|
#define BREATH_MAX_ADDR 4
|
|
#define PORTAM_THR_ADDR 6
|
|
#define PORTAM_MAX_ADDR 8
|
|
#define PITCHB_THR_ADDR 10
|
|
#define PITCHB_MAX_ADDR 12
|
|
#define TRANSP_ADDR 14
|
|
#define MIDI_ADDR 16
|
|
#define BREATH_CC_ADDR 18
|
|
#define BREATH_AT_ADDR 20
|
|
#define VELOCITY_ADDR 22
|
|
#define PORTAM_ADDR 24
|
|
#define PB_ADDR 26
|
|
#define EXTRA_ADDR 28
|
|
#define VIBRATO_ADDR 30
|
|
#define DEGLITCH_ADDR 32
|
|
#define EXTRAC_THR_ADDR 34
|
|
#define EXTRAC_MAX_ADDR 36
|
|
#define PATCH_ADDR 38
|
|
#define OCTAVE_ADDR 40
|
|
#define CTOUCH_THR_ADDR 42
|
|
#define BREATHCURVE_ADDR 44
|
|
#define VEL_SMP_DL_ADDR 46
|
|
#define VEL_BIAS_ADDR 48
|
|
#define PINKY_KEY_ADDR 50
|
|
#define FP1_ADDR 52
|
|
#define FP2_ADDR 54
|
|
#define FP3_ADDR 56
|
|
#define FP4_ADDR 58
|
|
#define FP5_ADDR 60
|
|
#define FP6_ADDR 62
|
|
#define FP7_ADDR 64
|
|
#define DIPSW_BITS_ADDR 66
|
|
#define PARAL_ADDR 68
|
|
#define ROTN1_ADDR 70
|
|
#define ROTN2_ADDR 72
|
|
#define ROTN3_ADDR 74
|
|
#define ROTN4_ADDR 76
|
|
#define PRIO_ADDR 78
|
|
#define VIB_SENS_ADDR 80
|
|
#define VIB_RETN_ADDR 82
|
|
#define VIB_SQUELCH_ADDR 84
|
|
#define VIB_DIRECTION_ADDR 86
|
|
|
|
//"factory" values for settings
|
|
#define VERSION 31
|
|
#define BREATH_THR_FACTORY 1400
|
|
#define BREATH_MAX_FACTORY 4000
|
|
#define PORTAM_THR_FACTORY 2600
|
|
#define PORTAM_MAX_FACTORY 3300
|
|
#define PITCHB_THR_FACTORY 1400
|
|
#define PITCHB_MAX_FACTORY 2300
|
|
#define EXTRAC_THR_FACTORY 1200
|
|
#define EXTRAC_MAX_FACTORY 2400
|
|
#define TRANSP_FACTORY 12 // 12 is 0 transpose
|
|
#define MIDI_FACTORY 1 // 1-16
|
|
#define BREATH_CC_FACTORY 2 //thats CC#2, see ccList
|
|
#define BREATH_AT_FACTORY 0 //aftertouch default off
|
|
#define VELOCITY_FACTORY 0 // 0 is dynamic/breath controlled velocity
|
|
#define PORTAM_FACTORY 2 // 0 - OFF, 1 - ON, 2 - SW
|
|
#define PB_FACTORY 1 // 0 - OFF, 1 - 12
|
|
#define EXTRA_FACTORY 2 // 0 - OFF, 1 - Modulation wheel, 2 - Foot pedal, 3 - Filter Cutoff, 4 - Sustain pedal
|
|
#define VIBRATO_FACTORY 4 // 0 - OFF, 1 - 9 depth
|
|
#define DEGLITCH_FACTORY 20 // 0 - OFF, 5 to 70 ms in steps of 5
|
|
#define PATCH_FACTORY 1 // MIDI program change 1-128
|
|
#define OCTAVE_FACTORY 3 // 3 is 0 octave change
|
|
#define CTOUCH_THR_FACTORY 125 // MPR121 touch threshold
|
|
#define BREATHCURVE_FACTORY 4 // 0 to 12 (-4 to +4, S1 to S4)
|
|
#define VEL_SMP_DL_FACTORY 20 // 0 to 30 ms in steps of 5
|
|
#define VEL_BIAS_FACTORY 0 // 0 to 9
|
|
#define PINKY_KEY_FACTORY 12 // 0 - 11 (QuickTranspose -12 to -1), 12 (pb/2), 13 - 22 (QuickTranspose +1 to +12)
|
|
#define DIPSW_BITS_FACTORY 0 // virtual dip switch settings for special modes (work in progress)
|
|
#define PARAL_FACTORY 31 // 7 (+ 24) Rotator parallel
|
|
#define ROTN1_FACTORY 19 // -5 (+24) Rotation 1
|
|
#define ROTN2_FACTORY 14 // -10 (+24) Rotation 2
|
|
#define ROTN3_FACTORY 17 // -7 (+24) Rotation 3
|
|
#define ROTN4_FACTORY 10 // -14 (+24) Rotation 4
|
|
#define PRIO_FACTORY 0 // Mono priority 0 - BAS(e note), 1 - ROT(ating note)
|
|
#define VIB_SENS_FACTORY 6 // 1 least sensitive, higher more sensitive
|
|
#define VIB_RETN_FACTORY 2 // 0, no return, 1 slow return, higher faster return
|
|
#define VIB_SQUELCH_FACTORY 15 // 0 to 30, vib signal squelch
|
|
#define VIB_DIRECTION_FACTORY 0
|
|
|
|
#define OLED_RESET 4
|
|
Adafruit_SSD1306 display(OLED_RESET);
|
|
|
|
|
|
// 'NuEVI' logo
|
|
static const unsigned char PROGMEM nuevi_logo_bmp[] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xe3, 0x60, 0x00, 0x07, 0x73, 0x60, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xff, 0xff, 0xff, 0xe3, 0x60, 0x00, 0x0e, 0xe3, 0x60, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x60, 0x00, 0x1d, 0xc3, 0x60, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0x00, 0x3b, 0x83, 0x60, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0x00, 0x77, 0x03, 0x60, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x60, 0x00, 0xee, 0x03, 0x60, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x60, 0x01, 0xdc, 0x03, 0x60, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x60, 0x03, 0xb8, 0x03, 0x60, 0x00,
|
|
0x00, 0x00, 0x00, 0x20, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x60, 0x07, 0x70, 0x03, 0x60, 0x00,
|
|
0x00, 0x00, 0x00, 0x60, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0x0e, 0xe0, 0x03, 0x60, 0x00,
|
|
0x00, 0x00, 0x00, 0x60, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0x1d, 0xc0, 0x03, 0x60, 0x00,
|
|
0x00, 0x03, 0x00, 0x60, 0x00, 0x01, 0x80, 0x00, 0x00, 0x03, 0x60, 0x3b, 0x80, 0x03, 0x60, 0x00,
|
|
0x00, 0x03, 0x00, 0xe0, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0x77, 0x00, 0x03, 0x60, 0x00,
|
|
0x00, 0x03, 0x00, 0xc0, 0x00, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x60, 0xee, 0x00, 0x03, 0x60, 0x00,
|
|
0x00, 0x03, 0x80, 0xc0, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x61, 0xdc, 0x00, 0x03, 0x60, 0x00,
|
|
0x00, 0x07, 0x80, 0xc0, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x63, 0xb8, 0x00, 0x03, 0x60, 0x00,
|
|
0x00, 0x07, 0xc0, 0xc0, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x67, 0x70, 0x00, 0x03, 0x60, 0x00,
|
|
0x00, 0x06, 0xc0, 0xc0, 0x00, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x6e, 0xe0, 0x00, 0x03, 0x60, 0x00,
|
|
0x00, 0x06, 0x60, 0xc1, 0x01, 0x01, 0xb0, 0x00, 0x00, 0x03, 0x7d, 0xc0, 0x00, 0x03, 0x60, 0x00,
|
|
0x00, 0x06, 0x30, 0xc3, 0x03, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x7b, 0x80, 0x00, 0x03, 0x60, 0x00,
|
|
0x00, 0x0c, 0x30, 0xc3, 0x07, 0x01, 0xbf, 0xff, 0xff, 0xe3, 0x77, 0x00, 0x00, 0x03, 0x60, 0x00,
|
|
0x00, 0x0c, 0x1c, 0xc3, 0x06, 0x01, 0x80, 0x00, 0x00, 0x03, 0x0e, 0x00, 0x00, 0x03, 0x60, 0x00,
|
|
0x00, 0x0c, 0x0c, 0xc2, 0x0e, 0x01, 0xff, 0xff, 0xff, 0xe3, 0xfc, 0x00, 0x00, 0x03, 0x60, 0x00,
|
|
0x00, 0x0c, 0x0e, 0xc6, 0x1e, 0x01, 0xff, 0xff, 0xff, 0xe3, 0xf8, 0x00, 0x00, 0x03, 0x60, 0x00,
|
|
0x00, 0x0c, 0x07, 0xc6, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x0c, 0x03, 0xc6, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x0c, 0x01, 0xc7, 0xe6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x0c, 0x00, 0xc7, 0xc6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x0c, 0x00, 0x03, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
|
|
#ifndef SSD1306_128_64
|
|
#error("Incorrect display type, please fix Adafruit_SSD1306.h!");
|
|
#endif
|
|
|
|
|
|
//variables setup
|
|
|
|
unsigned short breathThrVal;// = 350;
|
|
unsigned short breathMaxVal;// = 1000;
|
|
unsigned short portamThrVal;// = 1730;
|
|
unsigned short portamMaxVal;// = 3300;
|
|
unsigned short pitchbThrVal;// = 1200;
|
|
unsigned short pitchbMaxVal;// = 2400;
|
|
unsigned short extracThrVal;// = 1200;
|
|
unsigned short extracMaxVal;// = 2400;
|
|
unsigned short ctouchThrVal;// = 120;
|
|
unsigned short transpose;
|
|
unsigned short MIDIchannel;
|
|
unsigned short breathCC; // OFF:MW:BR:VL:EX:MW+:BR+:VL+:EX+:CF
|
|
unsigned short breathAT;
|
|
unsigned short velocity;
|
|
unsigned short portamento;// switching on cc65? just cc5 enabled? SW:ON:OFF
|
|
unsigned short PBdepth; // OFF:1-12 divider
|
|
unsigned short extraCT; // OFF:MW:FP:CF:SP
|
|
unsigned short vibrato; // OFF:1-9
|
|
unsigned short deglitch; // 0-70 ms in steps of 5
|
|
unsigned short patch; // 1-128
|
|
unsigned short octave;
|
|
unsigned short curve;
|
|
unsigned short velSmpDl; // 0-30 ms
|
|
unsigned short velBias; // 0-9
|
|
unsigned short pinkySetting; // 0 - 11 (QuickTranspose -12 to -1), 12 (pb/2), 13 - 24 (QuickTranspose +1 to +12)
|
|
unsigned short dipSwBits; // virtual dip switch settings for special modes (work in progress)
|
|
unsigned short priority; // mono priority for rotator chords
|
|
|
|
unsigned short vibSens = 2; // vibrato sensitivity
|
|
unsigned short vibRetn = 2; // vibrato return speed
|
|
unsigned short vibSquelch = 15; //vibrato signal squelch
|
|
unsigned short vibDirection = DNWD; //direction of first vibrato wave UPWD or DNWD
|
|
|
|
unsigned short fastPatch[7] = {0,0,0,0,0,0,0};
|
|
|
|
byte rotatorOn = 0;
|
|
byte currentRotation = 0;
|
|
int rotations[4] = { -5, -10, -7, -14 }; // semitones { -5, -10, -7, -14 };
|
|
int parallel = 7; // semitones
|
|
|
|
byte gateOpen = 0; // setting for gate always open, note on sent for every time fingering changes, no matter the breath status
|
|
|
|
int breathLoLimit = 0;
|
|
int breathHiLimit = 4095;
|
|
int portamLoLimit = 1000;
|
|
int portamHiLimit = 5000;
|
|
int pitchbLoLimit = 500;
|
|
int pitchbHiLimit = 4000;
|
|
int extracLoLimit = 500;
|
|
int extracHiLimit = 4000;
|
|
int ctouchLoLimit = 50;
|
|
int ctouchHiLimit = 350;
|
|
int ttouchLoLimit = 50;
|
|
int ttouchHiLimit = 1900;
|
|
|
|
int touch_Thr = 1300;
|
|
|
|
|
|
int breathStep;
|
|
int portamStep;
|
|
int pitchbStep;
|
|
int extracStep;
|
|
int ctouchStep;
|
|
|
|
|
|
|
|
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
|
|
|
|
int pbDepthList[13] = {8192,8192,4096,2731,2048,1638,1365,1170,1024,910,819,744,683};
|
|
|
|
byte cursorNow;
|
|
byte forcePix = 0;
|
|
byte forceRedraw = 0;
|
|
|
|
int pos1;
|
|
int pos2;
|
|
|
|
// the following variables are unsigned longs because the time, measured in
|
|
// milliseconds, will quickly become a bigger number than can be stored in an int.
|
|
unsigned long lastDebounceTime = 0; // the last time the output pin was toggled
|
|
unsigned long debounceDelay = 30; // the debounce time; increase if the output flickers
|
|
unsigned long buttonRepeatTime = 0;
|
|
unsigned long buttonPressedTime = 0;
|
|
unsigned long buttonRepeatInterval = 50;
|
|
unsigned long buttonRepeatDelay = 400;
|
|
unsigned long pixelUpdateTime = 0;
|
|
unsigned long pixelUpdateInterval = 80;
|
|
unsigned long cursorBlinkTime = 0; // the last time the cursor was toggled
|
|
unsigned long cursorBlinkInterval = 300; // the cursor blink toggle interval time
|
|
unsigned long patchViewTime = 0;
|
|
unsigned long patchViewTimeUp = 2000; // ms until patch view shuts off
|
|
unsigned long menuTime = 0;
|
|
unsigned long menuTimeUp = 60000; // menu shuts off after one minute of button inactivity
|
|
unsigned long lastDeglitchTime = 0; // The last time the fingering was changed
|
|
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 lastFingering = 0; // Keep the last fingering value for debouncing
|
|
|
|
int mainState; // The state of the main state machine
|
|
|
|
int initial_breath_value; // The breath value at the time we observed the transition
|
|
|
|
byte activeMIDIchannel; // MIDI channel
|
|
byte activePatch=0;
|
|
byte doPatchUpdate=0;
|
|
|
|
byte legacy = 0;
|
|
byte legacyBrAct = 0;
|
|
byte halfTime = 0;
|
|
byte FPD = 0;
|
|
boolean programonce = false;
|
|
byte slowMidi = 0;
|
|
|
|
int breathLevel=0; // breath level (smoothed) not mapped to CC value
|
|
int oldbreath=0;
|
|
unsigned int oldbreathhires=0;
|
|
float filterFreq = 30.0;
|
|
|
|
float filterVal = 0.15;
|
|
float smoothedVal;
|
|
int pressureSensor; // pressure data from breath sensor, for midi breath cc and breath threshold checks
|
|
int lastPressure;
|
|
byte velocitySend; // remapped midi velocity from breath sensor (or set to static value if selected)
|
|
int breathCalZero;
|
|
|
|
|
|
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 oldport=0;
|
|
int lastBite=0;
|
|
|
|
int exSensor=0;
|
|
byte extracIsOn=0;
|
|
int oldextrac=0;
|
|
int lastEx=0;
|
|
|
|
int pitchBend=8192;
|
|
int oldpb=8192;
|
|
int vibSignal=0;
|
|
int pbUp=0;
|
|
int pbDn=0;
|
|
int lastPbUp=0;
|
|
int lastPbDn=0;
|
|
byte vibLedOff = 0;
|
|
byte oldpkey = 0;
|
|
|
|
float vibDepth[10] = {0,0.05,0.1,0.15,0.2,0.25,0.3,0.35,0.40,0.45}; // max pitch bend values (+/-) for the vibrato settings
|
|
int vibMaxList[12] = {300,275,250,225,200,175,150,125,100,75,50,25};
|
|
|
|
unsigned int curveM4[] = {0,4300,7000,8700,9900,10950,11900,12600,13300,13900,14500,15000,15450,15700,16000,16250,16383};
|
|
unsigned int curveM3[] = {0,2900,5100,6650,8200,9500,10550,11500,12300,13100,13800,14450,14950,15350,15750,16150,16383};
|
|
unsigned int curveM2[] = {0,2000,3600,5000,6450,7850,9000,10100,11100,12100,12900,13700,14400,14950,15500,16000,16383};
|
|
unsigned int curveM1[] = {0,1400,2850,4100,5300,6450,7600,8700,9800,10750,11650,12600,13350,14150,14950,15650,16383};
|
|
unsigned int curveIn[] = {0,1023,2047,3071,4095,5119,6143,7167,8191,9215,10239,11263,12287,13311,14335,15359,16383};
|
|
unsigned int curveP1[] = {0,600,1350,2150,2900,3800,4700,5600,6650,7700,8800,9900,11100,12300,13500,14850,16383};
|
|
unsigned int curveP2[] = {0,400,800,1300,2000,2650,3500,4300,5300,6250,7400,8500,9600,11050,12400,14100,16383};
|
|
unsigned int curveP3[] = {0,200,500,900,1300,1800,2350,3100,3800,4600,5550,6550,8000,9500,11250,13400,16383};
|
|
unsigned int curveP4[] = {0,100,200,400,700,1050,1500,1950,2550,3200,4000,4900,6050,7500,9300,12100,16383};
|
|
unsigned int curveS1[] = {0,600,1350,2150,2900,3800,4700,6000,8700,11000,12400,13400,14300,14950,15500,16000,16383};
|
|
unsigned int curveS2[] = {0,600,1350,2150,2900,4000,6100,9000,11000,12100,12900,13700,14400,14950,15500,16000,16383};
|
|
//unsigned int curveS3[] = {0,600,1350,2300,3800,6200,8700,10200,11100,12100,12900,13700,14400,14950,15500,16000,16383};
|
|
//unsigned int curveS4[] = {0,600,1700,4000,6600,8550,9700,10550,11400,12200,12900,13700,14400,14950,15500,16000,16383};
|
|
|
|
unsigned int curveZ1[] = {0,1400,2100,2900,3200,3900,4700,5600,6650,7700,8800,9900,11100,12300,13500,14850,16383};
|
|
unsigned int curveZ2[] = {0,2000,3200,3800,4096,4800,5100,5900,6650,7700,8800,9900,11100,12300,13500,14850,16383};
|
|
|
|
int vibThr; // this gets auto calibrated in setup
|
|
int vibThrLo;
|
|
int vibZero;
|
|
|
|
|
|
int fingeredNote; // note calculated from fingering (switches), transpose and octave settings
|
|
int fingeredNoteUntransposed; // note calculated from fingering (switches), for on the fly settings
|
|
byte activeNote; // note playing
|
|
byte startNote=36; // set startNote to C (change this value in steps of 12 to start in other octaves)
|
|
int slurBase; // first note in slur sustain chord
|
|
|
|
int slurInterval[9] = {-5,0,0,0,0,0,0,0,0};
|
|
byte addedIntervals = 1;
|
|
|
|
// 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 octaveR = 0;
|
|
byte lastOctaveR = 0;
|
|
|
|
byte halfPitchBendKey;
|
|
byte specialKey;
|
|
byte pinkyKey;
|
|
byte lastSpecialKey = 0;
|
|
byte pitchlatch;
|
|
int reverb;
|
|
|
|
byte slurSustain = 0;
|
|
byte parallelChord = 0;
|
|
byte subOctaveDouble = 0;
|
|
|
|
byte breathLedBrightness = 2000; // up to 4095, PWM
|
|
byte portamLedBrightness = 2000; // up to 4095, PWM
|
|
|
|
Adafruit_MPR121 touchSensor = Adafruit_MPR121(); // This is the 12-input touch sensor
|
|
FilterOnePole breathFilter;
|
|
|
|
|
|
//_______________________________________________________________________________________________ SETUP
|
|
|
|
void setup() {
|
|
|
|
analogReadResolution(12); // set resolution of ADCs to 12 bit
|
|
analogWriteResolution(12);
|
|
|
|
pinMode(dPin, INPUT_PULLUP);
|
|
pinMode(ePin, INPUT_PULLUP);
|
|
pinMode(uPin, INPUT_PULLUP);
|
|
pinMode(mPin, INPUT_PULLUP);
|
|
|
|
pinMode(bLedPin, OUTPUT); // breath indicator LED
|
|
pinMode(pLedPin, OUTPUT); // portam indicator LED
|
|
pinMode(statusLedPin,OUTPUT); // Teensy onboard LED
|
|
|
|
|
|
// if stored settings are not for current version, or Enter+Menu are pressed at startup, they are replaced by factory settings
|
|
// if stored settings are not for current version, or Enter+Menu are pressed at startup, they are replaced by factory settings
|
|
|
|
if ((readSetting(VERSION_ADDR) != VERSION) && (readSetting(VERSION_ADDR) < 24) || (!digitalRead(ePin) && !digitalRead(mPin))){
|
|
writeSetting(VERSION_ADDR,VERSION);
|
|
writeSetting(BREATH_THR_ADDR,BREATH_THR_FACTORY);
|
|
writeSetting(BREATH_MAX_ADDR,BREATH_MAX_FACTORY);
|
|
writeSetting(PORTAM_THR_ADDR,PORTAM_THR_FACTORY);
|
|
writeSetting(PORTAM_MAX_ADDR,PORTAM_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 ((readSetting(VERSION_ADDR) != 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);
|
|
}
|
|
// 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)-24;
|
|
rotations[0] = readSetting(ROTN1_ADDR)-24;
|
|
rotations[1] = readSetting(ROTN2_ADDR)-24;
|
|
rotations[2] = readSetting(ROTN3_ADDR)-24;
|
|
rotations[3] = readSetting(ROTN4_ADDR)-24;
|
|
priority = readSetting(PRIO_ADDR);
|
|
vibSens = readSetting(VIB_SENS_ADDR);
|
|
vibRetn = readSetting(VIB_RETN_ADDR);
|
|
vibSquelch = readSetting(VIB_SQUELCH_ADDR);
|
|
vibDirection = readSetting(VIB_DIRECTION_ADDR);
|
|
|
|
legacy = dipSwBits & (1<<1);
|
|
legacyBrAct = dipSwBits & (1<<2);
|
|
slowMidi = dipSwBits & (1<<3);
|
|
activePatch = patch;
|
|
|
|
breathStep = (breathHiLimit - breathLoLimit)/92; // 92 is the number of pixels in the settings bar
|
|
portamStep = (portamHiLimit - portamLoLimit)/92;
|
|
pitchbStep = (pitchbHiLimit - pitchbLoLimit)/92;
|
|
extracStep = (extracHiLimit - extracLoLimit)/92;
|
|
ctouchStep = (ctouchHiLimit - ctouchLoLimit)/92;
|
|
|
|
touch_Thr = map(ctouchThrVal,ctouchHiLimit,ctouchLoLimit,ttouchLoLimit,ttouchHiLimit);
|
|
|
|
if (!touchSensor.begin(0x5A)) {
|
|
while (1); // Touch sensor initialization failed - stop doing stuff
|
|
}
|
|
|
|
|
|
// by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
|
|
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3D (for the 128x64)
|
|
// init done
|
|
|
|
breathFilter.setFilter(LOWPASS, filterFreq, 0.0); // create a one pole (RC) lowpass filter
|
|
|
|
// Show image buffer on the display hardware.
|
|
// Since the buffer is intialized with an Adafruit splashscreen
|
|
// internally, this will display the splashscreen.
|
|
|
|
display.clearDisplay();
|
|
display.drawBitmap(0,0,nuevi_logo_bmp,128,64,1);
|
|
display.display();
|
|
|
|
//auto-calibrate the vibrato threshold while showing splash screen
|
|
int cv1=touchRead(vibratoPin);
|
|
int bc1=analogRead(breathSensorPin);
|
|
digitalWrite(statusLedPin,HIGH);
|
|
delay(250);
|
|
int cv2=touchRead(vibratoPin);
|
|
int bc2=analogRead(breathSensorPin);
|
|
digitalWrite(statusLedPin,LOW);
|
|
delay(250);
|
|
int cv3=touchRead(vibratoPin);
|
|
int bc3=analogRead(breathSensorPin);
|
|
digitalWrite(statusLedPin,HIGH);
|
|
delay(250);
|
|
digitalWrite(statusLedPin,LOW);
|
|
int cv4=touchRead(vibratoPin);
|
|
int bc4=analogRead(breathSensorPin);
|
|
vibZero=(cv1+cv2+cv3+cv4)/4;
|
|
vibThr=vibZero-vibSquelch;
|
|
vibThrLo=vibZero+vibSquelch;
|
|
breathCalZero=(bc1+bc2+bc3+bc4)/4;
|
|
delay(250);
|
|
digitalWrite(statusLedPin,HIGH);
|
|
delay(250);
|
|
digitalWrite(statusLedPin,LOW);
|
|
display.setTextColor(WHITE);
|
|
display.setTextSize(1);
|
|
#if defined(CASSIDY)
|
|
display.setCursor(0,0);
|
|
display.print("BC");
|
|
#endif
|
|
display.setCursor(85,52);
|
|
display.print("v.");
|
|
display.println(FIRMWARE_VERSION);
|
|
display.display();
|
|
|
|
delay(1500);
|
|
|
|
mainState = NOTE_OFF; // initialize main state machine
|
|
|
|
if (!digitalRead(ePin)) {
|
|
activePatch=0;
|
|
doPatchUpdate=1;
|
|
}
|
|
|
|
activeMIDIchannel = MIDIchannel;
|
|
midiInitialize(MIDIchannel);
|
|
|
|
//Serial.begin(9600); // debug
|
|
|
|
digitalWrite(statusLedPin,HIGH); // Switch on the onboard LED to indicate power on/ready
|
|
|
|
}
|
|
|
|
//_______________________________________________________________________________________________ MAIN LOOP
|
|
|
|
void loop() {
|
|
breathFilter.input(analogRead(breathSensorPin));
|
|
pressureSensor = constrain((int)breathFilter.output(),0,4095); // Get the filtered pressure sensor reading from analog pin A0, input from sensor MP3V5004GP
|
|
//pressureSensor = analogRead(A0);
|
|
//pressureSensor = smooth(analogRead(0), filterVal, smoothedVal); // second parameter determines smoothness - 0 is off, .9999 is max smooth
|
|
if (mainState == NOTE_OFF) {
|
|
if (activeMIDIchannel != MIDIchannel) {
|
|
activeMIDIchannel = MIDIchannel; // only switch channel if no active note
|
|
midiSetChannel(activeMIDIchannel);
|
|
}
|
|
if ((activePatch != patch) && doPatchUpdate){
|
|
activePatch = patch;
|
|
midiSendProgramChange(activePatch);
|
|
slurSustain = 0;
|
|
parallelChord = 0;
|
|
subOctaveDouble = 0;
|
|
doPatchUpdate = 0;
|
|
}
|
|
if ((pressureSensor > breathThrVal) || gateOpen) {
|
|
// Value has risen above threshold. Move to the RISE_WAIT
|
|
// state. Record time and initial breath value.
|
|
breath_on_time = millis();
|
|
initial_breath_value = pressureSensor;
|
|
mainState = RISE_WAIT; // Go to next state
|
|
}
|
|
if (legacy || legacyBrAct){
|
|
if (((pbUp > ((pitchbMaxVal + pitchbThrVal)/2)) && (pbDn > ((pitchbMaxVal + pitchbThrVal)/2)) && legacy) || ((analogRead(0) < (breathCalZero - 800)) && legacyBrAct) && (pbUp > ((pitchbMaxVal + pitchbThrVal)/2)) && (pbDn < ((pitchbMaxVal + pitchbThrVal)/2))) { // both pb pads touched or br suck
|
|
readSwitches();
|
|
fingeredNoteUntransposed=patchLimit(fingeredNoteUntransposed+1);
|
|
if (exSensor >= ((extracThrVal+extracMaxVal)/2)){ // instant midi setting
|
|
if ((fingeredNoteUntransposed >= 73) && (fingeredNoteUntransposed <= 88)) {
|
|
MIDIchannel = fingeredNoteUntransposed - 72; // Mid C and up
|
|
digitalWrite(statusLedPin, LOW);
|
|
delay(150);
|
|
digitalWrite(statusLedPin, HIGH);
|
|
}
|
|
} else {
|
|
if (!pinkyKey){ // note number to patch number
|
|
if (patch != fingeredNoteUntransposed){
|
|
patch = fingeredNoteUntransposed;
|
|
doPatchUpdate = 1;
|
|
digitalWrite(statusLedPin, LOW);
|
|
delay(150);
|
|
digitalWrite(statusLedPin, HIGH);
|
|
}
|
|
} 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;
|
|
digitalWrite(statusLedPin, LOW);
|
|
delay(150);
|
|
digitalWrite(statusLedPin, HIGH);
|
|
}
|
|
} else {
|
|
if (patch != patchLimit(fingeredNoteUntransposed - 36)){
|
|
patch = patchLimit(fingeredNoteUntransposed - 36); // subtract 36 to get low numbers 0 to 36
|
|
doPatchUpdate = 1;
|
|
digitalWrite(statusLedPin, LOW);
|
|
delay(150);
|
|
digitalWrite(statusLedPin, HIGH);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (pbDn > (pitchbMaxVal + pitchbThrVal)/2 && (analogRead(0) < (breathCalZero - 800)) && programonce == false) { // down bend for suck programming button
|
|
programonce = true;
|
|
readSwitches();
|
|
|
|
if (octaveR == 0) { //lowest octave position
|
|
if (K1 && K2 && !K3 &&K4) { patch = patch -10; doPatchUpdate = 1; } // e28 send patch change -10
|
|
else if (K1 && !K2 && !K3 && K4) { patch--; doPatchUpdate = 1; } //f29 decrement and send patch change
|
|
else if (!K1 && K2 && !K3 && K4) {patch = patch +10; doPatchUpdate = 1; } //f#30 send patch change +10
|
|
else if (!K1 && !K2 && !K3 &&K4) { patch++; doPatchUpdate = 1; } //g31 increment and send patch change
|
|
if (!K1 && !K2 && K3 &&!K4) { //send reverb pitchlatch value
|
|
reverb = ((pitchlatch - 36) * 2);
|
|
if (reverb > 127) {reverb = 127;}
|
|
if (reverb < 0) {reverb = 0;}
|
|
midiSendControlChange(91,reverb);
|
|
}
|
|
}
|
|
|
|
if (octaveR == 3) { //middle octave position to set breath parameters
|
|
// breathCC value is from cclist[] which assigns controller number
|
|
if (K1) { //turn on midi volume
|
|
breathCC = 3;
|
|
midiSendControlChange(7,0); //midi vol to 0
|
|
midiSendControlChange(11,127); //midi expression to 127
|
|
}
|
|
if (K3) { //turn on midi breath controller
|
|
breathCC = 2;
|
|
midiSendControlChange(7,127); //midi vol to 127
|
|
midiSendControlChange(11,127); //midi expression to 127
|
|
}
|
|
if (K4) { //sb turn on midi expression
|
|
breathCC = 4;
|
|
midiSendControlChange(7,127); //midi vol to 127
|
|
midiSendControlChange(11,0); //midi expression to 0
|
|
}
|
|
if (K2) { //2v turn on aftertouch
|
|
breathAT = 1;
|
|
midiSendControlChange(7,127); //midi vol to 127
|
|
midiSendControlChange(11,127); //midi expression to 0
|
|
}
|
|
else { breathAT = 0; }
|
|
if (K5) { //1tr turn on velocity
|
|
velocity = 0;
|
|
midiSendControlChange(7,127); //midi vol to 127
|
|
midiSendControlChange(11,127); //midi expression to 0
|
|
}
|
|
else { velocity = 127; }
|
|
if (!K1 && !K3 && !K4) {
|
|
breathCC = 0;
|
|
midiSendControlChange(7,127); //midi vol to 127
|
|
midiSendControlChange(11,127); //midi expression to 127
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (analogRead(0) > (breathCalZero - 800)) programonce = false;
|
|
|
|
|
|
specialKey=(touchRead(specialKeyPin) > touch_Thr); //S2 on pcb
|
|
if (lastSpecialKey != specialKey){
|
|
if (specialKey){
|
|
// special key just pressed, check other keys
|
|
readSwitches();
|
|
if (K4) {
|
|
if (!slurSustain) {
|
|
slurSustain = 1;
|
|
parallelChord = 0;
|
|
rotatorOn = 0;
|
|
} else slurSustain = 0;
|
|
}
|
|
if (K5) {
|
|
if (!parallelChord) {
|
|
parallelChord = 1;
|
|
slurSustain = 0;
|
|
rotatorOn = 0;
|
|
} else parallelChord = 0;
|
|
}
|
|
if (K1) {
|
|
if (!subOctaveDouble) {
|
|
subOctaveDouble = 1;
|
|
rotatorOn = 0;
|
|
} else subOctaveDouble = 0;
|
|
}
|
|
if (!K1 && !K4 && !K5){
|
|
slurSustain = 0;
|
|
parallelChord = 0;
|
|
subOctaveDouble = 0;
|
|
rotatorOn = 0;
|
|
}
|
|
if (pinkyKey){
|
|
if (!rotatorOn) {
|
|
rotatorOn = 1;
|
|
slurSustain = 0;
|
|
parallelChord = 0;
|
|
subOctaveDouble = 0;
|
|
} else rotatorOn = 0;
|
|
}
|
|
}
|
|
}
|
|
lastSpecialKey = specialKey;
|
|
} else if (mainState == RISE_WAIT) {
|
|
if ((pressureSensor > breathThrVal) || gateOpen) {
|
|
// Has enough time passed for us to collect our second
|
|
// sample?
|
|
if ((millis() - breath_on_time > velSmpDl) || (0 == velSmpDl)) {
|
|
// Yes, so calculate MIDI note and velocity, then send a note on event
|
|
readSwitches();
|
|
// We should be at tonguing peak, so set velocity based on current pressureSensor value unless fixed velocity is set
|
|
breathLevel=constrain(max(pressureSensor,initial_breath_value),breathThrVal,breathMaxVal);
|
|
if (!velocity) {
|
|
unsigned int breathValHires = breathCurve(map(constrain(breathLevel,breathThrVal,breathMaxVal),breathThrVal,breathMaxVal,0,16383));
|
|
velocitySend = (breathValHires >>7) & 0x007F;
|
|
velocitySend = constrain(velocitySend+velocitySend*.1*velBias,1,127);
|
|
//velocitySend = map(constrain(max(pressureSensor,initial_breath_value),breathThrVal,breathMaxVal),breathThrVal,breathMaxVal,1,127);
|
|
} else velocitySend = velocity;
|
|
breath(); // send breath data
|
|
fingeredNote=noteValueCheck(fingeredNote);
|
|
if (priority){ // mono prio to last chord note
|
|
midiSendNoteOn(fingeredNote, velocitySend); // send Note On message for new note
|
|
}
|
|
if (parallelChord){
|
|
for (int i=0; i < addedIntervals; i++){
|
|
midiSendNoteOn(noteValueCheck(fingeredNote+slurInterval[i]), velocitySend); // send Note On message for new note
|
|
}
|
|
}
|
|
if (slurSustain){
|
|
midiSendControlChange(64,127);
|
|
slurBase = fingeredNote;
|
|
addedIntervals = 0;
|
|
}
|
|
if (subOctaveDouble){
|
|
midiSendNoteOn(noteValueCheck(fingeredNote-12), velocitySend);
|
|
if (parallelChord){
|
|
for (int i=0; i < addedIntervals; i++){
|
|
midiSendNoteOn(noteValueCheck(fingeredNote+slurInterval[i]-12), velocitySend); // send Note On message for new note
|
|
}
|
|
}
|
|
}
|
|
if (rotatorOn){
|
|
midiSendNoteOn(noteValueCheck(fingeredNote+parallel), velocitySend); // send Note On message for new note
|
|
if (currentRotation < 3) currentRotation++; else currentRotation = 0;
|
|
midiSendNoteOn(noteValueCheck(fingeredNote+rotations[currentRotation]), velocitySend); // send Note On message for new note
|
|
}
|
|
if (!priority){ // mono prio to base note
|
|
midiSendNoteOn(fingeredNote, velocitySend); // send Note On message for new note
|
|
}
|
|
activeNote=fingeredNote;
|
|
mainState = NOTE_ON;
|
|
}
|
|
} else {
|
|
// Value fell below threshold before velocity sample delay time passed. Return to
|
|
// NOTE_OFF state (e.g. we're ignoring a short blip of breath)
|
|
mainState = NOTE_OFF;
|
|
}
|
|
} else if (mainState == NOTE_ON) {
|
|
if ((pressureSensor < breathThrVal) && !gateOpen) {
|
|
// Value has fallen below threshold - turn the note off
|
|
activeNote=noteValueCheck(activeNote);
|
|
if (priority){
|
|
midiSendNoteOff(activeNote); // send Note Off message
|
|
}
|
|
if (parallelChord){
|
|
for (int i=0; i < addedIntervals; i++){
|
|
midiSendNoteOff(noteValueCheck(activeNote+slurInterval[i])); // send Note On message for new note
|
|
}
|
|
}
|
|
if (subOctaveDouble){
|
|
midiSendNoteOff(noteValueCheck(activeNote-12));
|
|
if (parallelChord){
|
|
for (int i=0; i < addedIntervals; i++){
|
|
midiSendNoteOff(noteValueCheck(activeNote+slurInterval[i]-12)); // send Note On message for new note
|
|
}
|
|
}
|
|
}
|
|
if (rotatorOn){
|
|
midiSendNoteOff(noteValueCheck(activeNote+parallel)); // send Note Off message for old note
|
|
midiSendNoteOff(noteValueCheck(activeNote+rotations[currentRotation])); // send Note Off message for old note
|
|
}
|
|
if (!priority){
|
|
midiSendNoteOff(activeNote); // send Note Off message
|
|
}
|
|
if (slurSustain){
|
|
midiSendControlChange(64,0);
|
|
}
|
|
breathLevel=0;
|
|
mainState = NOTE_OFF;
|
|
} else {
|
|
readSwitches();
|
|
if (fingeredNote != lastFingering){ //
|
|
// reset the debouncing timer
|
|
lastDeglitchTime = millis();
|
|
}
|
|
if ((millis() - lastDeglitchTime) > deglitch) {
|
|
// whatever the reading is at, it's been there for longer
|
|
// than the debounce delay, so take it as the actual current state
|
|
if (noteValueCheck(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.
|
|
if (!velocity){
|
|
unsigned int breathValHires = breathCurve(map(constrain(breathLevel,breathThrVal,breathMaxVal),breathThrVal,breathMaxVal,0,16383));
|
|
velocitySend = (breathValHires >>7) & 0x007F;
|
|
velocitySend = constrain(velocitySend+velocitySend*.1*velBias,1,127);
|
|
//velocitySend = map(constrain(pressureSensor,breathThrVal,breathMaxVal),breathThrVal,breathMaxVal,7,127); // set new velocity value based on current pressure sensor level
|
|
}
|
|
activeNote=noteValueCheck(activeNote);
|
|
if ((parallelChord || subOctaveDouble || rotatorOn) && priority){ // poly playing, send old note off before new note on
|
|
midiSendNoteOff(activeNote); // send Note Off message for old note
|
|
}
|
|
|
|
if (parallelChord){
|
|
for (int i=0; i < addedIntervals; i++){
|
|
midiSendNoteOff(noteValueCheck(activeNote+slurInterval[i])); // send Note Off message for old note
|
|
}
|
|
}
|
|
if (subOctaveDouble){
|
|
midiSendNoteOff(noteValueCheck(activeNote-12)); // send Note Off message for old note
|
|
if (parallelChord){
|
|
for (int i=0; i < addedIntervals; i++){
|
|
midiSendNoteOff(noteValueCheck(activeNote+slurInterval[i]-12)); // send Note Off message for old note
|
|
}
|
|
}
|
|
}
|
|
if (rotatorOn){
|
|
midiSendNoteOff(noteValueCheck(activeNote+parallel)); // send Note Off message for old note
|
|
midiSendNoteOff(noteValueCheck(activeNote+rotations[currentRotation])); // send Note Off message for old note
|
|
}
|
|
if ((parallelChord || subOctaveDouble || rotatorOn) && !priority){ // poly playing, send old note off before new note on
|
|
midiSendNoteOff(activeNote); // send Note Off message for old note
|
|
}
|
|
|
|
|
|
fingeredNote=noteValueCheck(fingeredNote);
|
|
if (priority){
|
|
midiSendNoteOn(fingeredNote, velocitySend); // send Note On message for new note
|
|
}
|
|
if (parallelChord){
|
|
for (int i=0; i < addedIntervals; i++){
|
|
midiSendNoteOn(noteValueCheck(fingeredNote+slurInterval[i]), velocitySend); // send Note On message for new note
|
|
}
|
|
}
|
|
if (subOctaveDouble){
|
|
midiSendNoteOn(noteValueCheck(fingeredNote-12), velocitySend); // send Note On message for new note
|
|
if (parallelChord){
|
|
for (int i=0; i < addedIntervals; i++){
|
|
midiSendNoteOn(noteValueCheck(fingeredNote+slurInterval[i]-12), velocitySend); // send Note On message for new note
|
|
}
|
|
}
|
|
}
|
|
if (rotatorOn){
|
|
midiSendNoteOn(noteValueCheck(fingeredNote+parallel), velocitySend); // send Note On message for new note
|
|
if (currentRotation < 3) currentRotation++; else currentRotation = 0;
|
|
midiSendNoteOn(noteValueCheck(fingeredNote+rotations[currentRotation]), velocitySend); // send Note On message for new note
|
|
}
|
|
|
|
if (!priority){
|
|
midiSendNoteOn(fingeredNote, velocitySend); // send Note On message for new note
|
|
}
|
|
|
|
if (!parallelChord && !subOctaveDouble && !rotatorOn){ // mono playing, send old note off after new note on
|
|
midiSendNoteOff(activeNote); // send Note Off message
|
|
}
|
|
|
|
if (slurSustain){
|
|
if (addedIntervals < 9){
|
|
addedIntervals++;
|
|
slurInterval[addedIntervals-1] = fingeredNote - slurBase;
|
|
}
|
|
}
|
|
activeNote=fingeredNote;
|
|
}
|
|
}
|
|
}
|
|
if (pressureSensor > breathThrVal) cursorBlinkTime = millis(); // keep display from updating with cursor blinking if breath is over thr
|
|
}
|
|
// Is it time to send more CC data?
|
|
if (millis() - ccSendTime > CC_INTERVAL) {
|
|
// deal with Breath, Pitch Bend, Modulation, etc.
|
|
if (!slowMidi) breath();
|
|
halfTime = !halfTime;
|
|
if (halfTime){
|
|
pitch_bend();
|
|
portamento_();
|
|
} else {
|
|
if (slowMidi) breath();
|
|
extraController();
|
|
statusLEDs();
|
|
doorKnobCheck();
|
|
}
|
|
ccSendTime = millis();
|
|
}
|
|
if (millis() - pixelUpdateTime > pixelUpdateInterval){
|
|
// even if we just alter a pixel, the whole display is redrawn (35ms of MPU lockup) and we can't do that all the time
|
|
// 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);
|
|
}
|
|
pixelUpdateTime = millis();
|
|
}
|
|
lastFingering=fingeredNote;
|
|
//do menu stuff
|
|
menu();
|
|
}
|
|
|
|
//_______________________________________________________________________________________________ FUNCTIONS
|
|
|
|
// non linear mapping function (http://playground.arduino.cc/Main/MultiMap)
|
|
// note: the _in array should have increasing values
|
|
unsigned int multiMap(unsigned int val, unsigned int* _in, unsigned int* _out, uint8_t size)
|
|
{
|
|
// take care the value is within range
|
|
// val = constrain(val, _in[0], _in[size-1]);
|
|
if (val <= _in[0]) return _out[0];
|
|
if (val >= _in[size-1]) return _out[size-1];
|
|
|
|
// search right interval
|
|
uint8_t pos = 1; // _in[0] allready tested
|
|
while(val > _in[pos]) pos++;
|
|
|
|
// this will handle all exact "points" in the _in array
|
|
if (val == _in[pos]) return _out[pos];
|
|
|
|
// interpolate in the right segment for the rest
|
|
return (val - _in[pos-1]) * (_out[pos] - _out[pos-1]) / (_in[pos] - _in[pos-1]) + _out[pos-1];
|
|
}
|
|
|
|
//**************************************************************
|
|
|
|
// map breath values to selected curve
|
|
unsigned int breathCurve(unsigned int inputVal){
|
|
// 0 to 16383, moving mid value up or down
|
|
switch (curve){
|
|
case 0:
|
|
// -4
|
|
return multiMap(inputVal,curveIn,curveM4,17);
|
|
break;
|
|
case 1:
|
|
// -3
|
|
return multiMap(inputVal,curveIn,curveM3,17);
|
|
break;
|
|
case 2:
|
|
// -2
|
|
return multiMap(inputVal,curveIn,curveM2,17);
|
|
break;
|
|
case 3:
|
|
// -1
|
|
return multiMap(inputVal,curveIn,curveM1,17);
|
|
break;
|
|
case 4:
|
|
// 0, linear
|
|
return inputVal;
|
|
break;
|
|
case 5:
|
|
// +1
|
|
return multiMap(inputVal,curveIn,curveP1,17);
|
|
break;
|
|
case 6:
|
|
// +2
|
|
return multiMap(inputVal,curveIn,curveP2,17);
|
|
break;
|
|
case 7:
|
|
// +3
|
|
return multiMap(inputVal,curveIn,curveP3,17);
|
|
break;
|
|
case 8:
|
|
// +4
|
|
return multiMap(inputVal,curveIn,curveP4,17);
|
|
break;
|
|
case 9:
|
|
// S1
|
|
return multiMap(inputVal,curveIn,curveS1,17);
|
|
break;
|
|
case 10:
|
|
// S2
|
|
return multiMap(inputVal,curveIn,curveS2,17);
|
|
break;
|
|
case 11:
|
|
// Z1
|
|
return multiMap(inputVal,curveIn,curveZ1,17);
|
|
break;
|
|
case 12:
|
|
// Z2
|
|
return multiMap(inputVal,curveIn,curveZ2,17);
|
|
break;
|
|
default: //Fallback option that should never be reached, use linear
|
|
return inputVal;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//**************************************************************
|
|
/*
|
|
int smooth(int data, float filterVal, float smoothedVal){
|
|
|
|
|
|
if (filterVal > 1){ // check to make sure param's are within range
|
|
filterVal = .99;
|
|
}
|
|
else if (filterVal <= 0){
|
|
filterVal = 0;
|
|
}
|
|
|
|
smoothedVal = (data * (1 - filterVal)) + (smoothedVal * filterVal);
|
|
|
|
return (int)smoothedVal;
|
|
}
|
|
|
|
*/
|
|
//**************************************************************
|
|
|
|
// MIDI note value check with out of range octave repeat
|
|
int noteValueCheck(int note){
|
|
if (note > 127){
|
|
note = 115+(note-127)%12;
|
|
} else if (note < 0) {
|
|
note = 12-abs(note)%12;
|
|
}
|
|
return note;
|
|
}
|
|
|
|
//**************************************************************
|
|
|
|
int patchLimit(int value){
|
|
if (value < 1) return 1; else if (value > 128) return 128; else return 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 (biteSensor > portamThrVal){ // portamento indicator LED, labeled "P" on PCB
|
|
//analogWrite(pLedPin, map(biteSensor,0,4096,5,portamLedBrightness));
|
|
analogWrite(pLedPin, map(constrain(biteSensor,portamThrVal,portamMaxVal),portamThrVal,portamMaxVal,5,portamLedBrightness));
|
|
} else {
|
|
analogWrite(pLedPin, 0);
|
|
}
|
|
}
|
|
|
|
//**************************************************************
|
|
|
|
void breath(){
|
|
int breathCCval,breathCCvalFine;
|
|
unsigned int breathCCvalHires;
|
|
breathLevel = constrain(pressureSensor,breathThrVal,breathMaxVal);
|
|
//breathLevel = breathLevel*0.6+pressureSensor*0.4; // smoothing of breathLevel value
|
|
////////breathCCval = map(constrain(breathLevel,breathThrVal,breathMaxVal),breathThrVal,breathMaxVal,0,127);
|
|
breathCCvalHires = breathCurve(map(constrain(breathLevel,breathThrVal,breathMaxVal),breathThrVal,breathMaxVal,0,16383));
|
|
breathCCval = (breathCCvalHires >>7) & 0x007F;
|
|
breathCCvalFine = breathCCvalHires & 0x007F;
|
|
|
|
if (breathCCval != oldbreath){ // only send midi data if breath has changed from previous value
|
|
if (breathCC){
|
|
// send midi cc
|
|
midiSendControlChange(ccList[breathCC], breathCCval);
|
|
}
|
|
if (breathAT){
|
|
// send aftertouch
|
|
midiSendAfterTouch(breathCCval);
|
|
}
|
|
oldbreath = breathCCval;
|
|
}
|
|
|
|
if (breathCCvalHires != oldbreathhires){
|
|
if ((breathCC > 4) && (breathCC < 9)){ // send high resolution midi
|
|
midiSendControlChange(ccList[breathCC]+32, breathCCvalFine);
|
|
}
|
|
oldbreathhires = breathCCvalHires;
|
|
}
|
|
}
|
|
|
|
//**************************************************************
|
|
|
|
void pitch_bend(){
|
|
// handle input from pitchbend touchpads and
|
|
// on-pcb variable capacitor for vibrato.
|
|
int vibMax;
|
|
int calculatedPBdepth;
|
|
byte pbTouched = 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
|
|
int vibRead = touchRead(vibratoPin); // SENSOR PIN 15 - built in var cap
|
|
calculatedPBdepth = pbDepthList[PBdepth];
|
|
if (halfPitchBendKey) calculatedPBdepth = calculatedPBdepth*0.5;
|
|
|
|
vibMax = vibMaxList[vibSens-1];
|
|
|
|
if (vibRead < vibThr){
|
|
if (UPWD == vibDirection){
|
|
vibSignal=vibSignal*0.5+0.5*map(constrain(vibRead,(vibZero-vibMax),vibThr),vibThr,(vibZero-vibMax),0,calculatedPBdepth*vibDepth[vibrato]);
|
|
} else {
|
|
vibSignal=vibSignal*0.5+0.5*map(constrain(vibRead,(vibZero-vibMax),vibThr),vibThr,(vibZero-vibMax),0,(0 - calculatedPBdepth*vibDepth[vibrato]));
|
|
}
|
|
} else if (vibRead > vibThrLo){
|
|
if (UPWD == vibDirection){
|
|
vibSignal=vibSignal*0.5+0.5*map(constrain(vibRead,vibThrLo,(vibZero+vibMax)),vibThrLo,(vibZero+vibMax),0,(0 - calculatedPBdepth*vibDepth[vibrato]));
|
|
} else {
|
|
vibSignal=vibSignal*0.5+0.5*map(constrain(vibRead,vibThrLo,(vibZero+vibMax)),vibThrLo,(vibZero+vibMax),0,calculatedPBdepth*vibDepth[vibrato]);
|
|
}
|
|
} else {
|
|
vibSignal = vibSignal*0.5;
|
|
}
|
|
|
|
switch(vibRetn){ // moving baseline
|
|
case 0:
|
|
//keep vibZero value
|
|
break;
|
|
case 1:
|
|
vibZero = vibZero*0.95+vibRead*0.05;
|
|
break;
|
|
case 2:
|
|
vibZero = vibZero*0.9+vibRead*0.1;
|
|
break;
|
|
case 3:
|
|
vibZero = vibZero*0.8+vibRead*0.2;
|
|
break;
|
|
case 4:
|
|
vibZero = vibZero*0.6+vibRead*0.4;
|
|
}
|
|
vibThr=vibZero-vibSquelch;
|
|
vibThrLo=vibZero+vibSquelch;
|
|
int pbPos = map(constrain(pbUp,pitchbThrVal,pitchbMaxVal),pitchbThrVal,pitchbMaxVal,0,calculatedPBdepth);
|
|
int pbNeg = map(constrain(pbDn,pitchbThrVal,pitchbMaxVal),pitchbThrVal,pitchbMaxVal,0,calculatedPBdepth);
|
|
int pbSum = 8193 + pbPos - pbNeg;
|
|
int pbDif = abs(pbPos - pbNeg);
|
|
/*
|
|
if ((pbUp > pitchbThrVal) && PBdepth){
|
|
pitchBend=pitchBend*0.6+0.4*map(constrain(pbUp,pitchbThrVal,pitchbMaxVal),pitchbThrVal,pitchbMaxVal,8192,(8193 + calculatedPBdepth));
|
|
pbTouched++;
|
|
}
|
|
if ((pbDn > pitchbThrVal) && PBdepth){
|
|
pitchBend=pitchBend*0.6+0.4*map(constrain(pbDn,pitchbThrVal,pitchbMaxVal),pitchbThrVal,pitchbMaxVal,8192,(8192 - calculatedPBdepth));
|
|
pbTouched++;
|
|
}
|
|
*/
|
|
if (((pbUp > pitchbThrVal) && PBdepth) || ((pbDn > pitchbThrVal) && PBdepth)){
|
|
if (pbDif < 10){
|
|
pitchBend = 8192;
|
|
} else {
|
|
pitchBend=pitchBend*0.6+0.4*pbSum;
|
|
}
|
|
pbTouched = 1;
|
|
}
|
|
if (!pbTouched) {
|
|
pitchBend = pitchBend*0.6+8192*0.4; // 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=pitchBend+vibSignal;
|
|
|
|
pitchBend=constrain(pitchBend, 0, 16383);
|
|
|
|
if (subVibSquelch && (8192 != pitchBend)){
|
|
digitalWrite(statusLedPin,LOW);
|
|
vibLedOff = 1;
|
|
} else if (vibLedOff){
|
|
digitalWrite(statusLedPin,HIGH);
|
|
vibLedOff = 0;
|
|
}
|
|
|
|
//Serial.print(pitchBend);
|
|
//Serial.print(" - ");
|
|
//Serial.println(oldpb);
|
|
|
|
if (pitchBend != oldpb){// only send midi data if pitch bend has changed from previous value
|
|
midiSendPitchBend(pitchBend);
|
|
oldpb=pitchBend;
|
|
}
|
|
}
|
|
|
|
//***********************************************************
|
|
|
|
void doorKnobCheck(){
|
|
int touchValue[12];
|
|
for (byte i=0; i<12; i++){
|
|
touchValue[i]=touchSensor.filteredData(i);
|
|
}
|
|
if ((touchValue[K4Pin] < ctouchThrVal) && (touchValue[R1Pin] < ctouchThrVal) && (touchValue[R2Pin] < ctouchThrVal) && (touchValue[R3Pin] < ctouchThrVal)){ // doorknob grip on canister
|
|
if (pbUp > ((pitchbMaxVal + pitchbThrVal)/2)) {
|
|
gateOpen = 1;
|
|
digitalWrite(statusLedPin,LOW);
|
|
delay(50);
|
|
digitalWrite(statusLedPin,HIGH);
|
|
delay(50);
|
|
}
|
|
else if (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);
|
|
}
|
|
}
|
|
}
|
|
|
|
//***********************************************************
|
|
|
|
void extraController(){
|
|
// Extra Controller is the lip touch sensor (proportional) in front of the mouthpiece
|
|
exSensor=exSensor*0.6+0.4*touchRead(extraPin); // get sensor data, do some smoothing - SENSOR PIN 16 - PCB PIN "EC" (marked K4 on some prototype boards)
|
|
if (extraCT && (exSensor >= extracThrVal)) { // if we are enabled and over the threshold, send data
|
|
if (!extracIsOn) {
|
|
extracIsOn=1;
|
|
if (extraCT == 4){ //Sustain ON
|
|
midiSendControlChange(64, 127);
|
|
}
|
|
}
|
|
if (extraCT == 1){ //Send modulation
|
|
int extracCC = map(constrain(exSensor,extracThrVal,extracMaxVal),extracThrVal,extracMaxVal,1,127);
|
|
if (extracCC != oldextrac){
|
|
midiSendControlChange(1, extracCC);
|
|
}
|
|
oldextrac = extracCC;
|
|
}
|
|
if (extraCT == 2){ //Send foot pedal (CC#4)
|
|
int extracCC = map(constrain(exSensor,extracThrVal,extracMaxVal),extracThrVal,extracMaxVal,1,127);
|
|
if (extracCC != oldextrac){
|
|
midiSendControlChange(4, extracCC);
|
|
}
|
|
oldextrac = extracCC;
|
|
}
|
|
if ((extraCT == 3) && (breathCC != 9)){ //Send filter cutoff (CC#74)
|
|
int extracCC = map(constrain(exSensor,extracThrVal,extracMaxVal),extracThrVal,extracMaxVal,1,127);
|
|
if (extracCC != oldextrac){
|
|
midiSendControlChange(74, extracCC);
|
|
}
|
|
oldextrac = extracCC;
|
|
}
|
|
} else if (extracIsOn) { // we have just gone below threshold, so send zero value
|
|
extracIsOn=0;
|
|
if (extraCT == 1){ //MW
|
|
if (oldextrac != 0){
|
|
//send modulation 0
|
|
midiSendControlChange(1, 0);
|
|
oldextrac = 0;
|
|
}
|
|
} else if (extraCT == 2){ //FP
|
|
if (oldextrac != 0){
|
|
//send foot pedal 0
|
|
midiSendControlChange(4, 0);
|
|
oldextrac = 0;
|
|
}
|
|
} else if ((extraCT == 3) && (breathCC != 9)){ //CF
|
|
if (oldextrac != 0){
|
|
//send filter cutoff 0
|
|
midiSendControlChange(74, 0);
|
|
oldextrac = 0;
|
|
}
|
|
} else if (extraCT == 4){ //SP
|
|
//send sustain off
|
|
midiSendControlChange(64, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
//***********************************************************
|
|
|
|
void portamento_(){
|
|
// Portamento is controlled with the bite sensor (variable capacitor) in the mouthpiece
|
|
biteSensor=touchRead(bitePin); // get sensor data, do some smoothing - SENSOR PIN 17 - PCB PINS LABELED "BITE" (GND left, sensor pin right)
|
|
if (portamento && (biteSensor >= portamThrVal)) { // if we are enabled and 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(){
|
|
if (portamento == 2){ // if portamento midi switching is enabled
|
|
midiSendControlChange(CCN_PortOnOff, 127);
|
|
}
|
|
portIsOn=1;
|
|
}
|
|
|
|
//***********************************************************
|
|
|
|
void port(){
|
|
int portCC;
|
|
portCC = map(constrain(biteSensor,portamThrVal,portamMaxVal),portamThrVal,portamMaxVal,0,127);
|
|
if (portCC!=oldport){
|
|
midiSendControlChange(CCN_Port, portCC);
|
|
}
|
|
oldport = portCC;
|
|
}
|
|
|
|
//***********************************************************
|
|
|
|
void portOff(){
|
|
if (oldport != 0){ //did a zero get sent? if not, then send one
|
|
midiSendControlChange(CCN_Port, 0);
|
|
}
|
|
if (portamento == 2){ // if portamento midi switching is enabled
|
|
midiSendControlChange(CCN_PortOnOff, 0);
|
|
}
|
|
portIsOn=0;
|
|
oldport = 0;
|
|
}
|
|
|
|
//***********************************************************
|
|
|
|
void readSwitches(){
|
|
int qTransp;
|
|
// Read touch pads (MPR121) and put value in variables
|
|
int touchValue[12];
|
|
for (byte i=0; i<12; i++){
|
|
touchValue[i]=touchSensor.filteredData(i);
|
|
}
|
|
|
|
// 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
|
|
|
|
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);
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
// Calculate midi note number from pressed keys
|
|
#if defined(CASSIDY)
|
|
fingeredNote=startNote-2*K1-K2-3*K3-5*K4+2*K5+K6+3*K7+octaveR*12+(octave-3)*12+transpose-12+qTransp;
|
|
fingeredNoteUntransposed=startNote-2*K1-K2-3*K3-5*K4+2*K5+K6+3*K7+octaveR*12;
|
|
#else
|
|
fingeredNote=startNote-2*K1-K2-3*K3-5*K4+2*K5+K6+4*K7+octaveR*12+(octave-3)*12+transpose-12+qTransp;
|
|
fingeredNoteUntransposed=startNote-2*K1-K2-3*K3-5*K4+2*K5+K6+4*K7+octaveR*12;
|
|
#endif
|
|
//}
|
|
if (pinkyKey) pitchlatch = fingeredNoteUntransposed; //use pitchlatch to make settings based on note fingered
|
|
}
|
|
|
|
//***********************************************************
|
|
|
|
int readTrills(){
|
|
readSwitches();
|
|
return K5+2*K6+4*K7;
|
|
}
|
|
|
|
//***********************************************************
|
|
|
|
void setFPS(int trills){
|
|
fastPatch[trills-1] = patch;
|
|
writeSetting(FP1_ADDR+2*(trills-1),patch);
|
|
FPD = 2;
|
|
}
|
|
|
|
//***********************************************************
|
|
|
|
void clearFPS(int trills){
|
|
fastPatch[trills-1] = 0;
|
|
writeSetting(FP1_ADDR+2*(trills-1),0);
|
|
FPD = 3;
|
|
}
|