From 6f573d753bdf27d297c364c554ee4f4b371fdd40 Mon Sep 17 00:00:00 2001 From: Pierre-Loup Martin Date: Tue, 17 Mar 2020 01:23:53 +0100 Subject: [PATCH] Hardware finished, softare running ok with some things to fix, like init values. --- README.MD | 8 + minimoog_mega_1/minimoog_mega_1.ino | 381 +++++++++++--- minimoog_mega_2/minimoog_mega_2.ino | 25 +- minimoog_teensy/audio_setup.h | 88 ++-- minimoog_teensy/minimoog_teensy.ino | 262 +++++++--- misc/lateral panel pinout.txt | 9 + panels/svg/minimoog - panneau lateral.svg | 606 ++++++++++++++++++++++ 7 files changed, 1177 insertions(+), 202 deletions(-) create mode 100644 README.MD create mode 100644 misc/lateral panel pinout.txt create mode 100644 panels/svg/minimoog - panneau lateral.svg diff --git a/README.MD b/README.MD new file mode 100644 index 0000000..a5d4782 --- /dev/null +++ b/README.MD @@ -0,0 +1,8 @@ +# TeensyMoog +## What is it ? +_Teensymoog_ is a synth, inspired by the legendary minimoog. + +It aims at looking and functionning as close as possible of the orinal, but using digital tools, namely Teensy and Arduino boards. + +It uses three boards, one Teensy 4.0 that handles the heavy computing (sound generation), and two Arduino Mega (chinese copies in a small form factor), that are used as "port expander", which read keys, switches and potentiometers and send their value on change to the Teensy. + diff --git a/minimoog_mega_1/minimoog_mega_1.ino b/minimoog_mega_1/minimoog_mega_1.ino index e239aaa..31c7f40 100644 --- a/minimoog_mega_1/minimoog_mega_1.ino +++ b/minimoog_mega_1/minimoog_mega_1.ino @@ -36,17 +36,17 @@ * glide pot A2 CC 5 * pitchbend wheel pot A3 pitchbend change * modulation wheel pot A4 CC 1 - * modulation mix 1 switch 2 CC 117 - * modulation mix 2 switch 3 CC 118 + * modulation mix 1 switch 3 CC 117 + * modulation mix 2 switch 2 CC 118 * osc modulation switch 4 CC 115 * glide switch 5 CC 65 * LFO waveform switch 6 CC 119 * Oscillators * osc 1 range switch A5 CC 102 - * osc 1 waveform switch A6 CC 103 - * osc 2 range switch A7 CC 104 - * osc 2 waveform switch A8 CC 105 - * osc 3 range switch A9 CC 106 + * osc 1 waveform switch A8 CC 103 + * osc 2 range switch A6 CC 104 + * osc 2 waveform switch A9 CC 105 + * osc 3 range switch A7 CC 106 * osc 3 waveform switch A10 CC 107 * osc 3 control switch 7 CC 108 * Mixer @@ -54,31 +54,39 @@ * osc 2 mix pot A12 CC 86 * osc 3 mix pot A13 CC 87 * noise mix pot A14 CC 88 - * external mix pot A15 CC 89 + * feedback mix pot A15 CC 89 * osc 1 switch 8 Directly added to mix * osc 2 switch 9 ditto * osc 3 switch 10 ditto * noise switch 11 ditto - * external switch 12 ditto - * noise color switch 13 CC 114 + * feedback switch 12 ditto + * noise color switch 17 CC 114 // led on 13 lowers tension * Other * function switch 14 CC 113 - * transpose + switch 52 CC 112 - * transpose - switch 53 CC 112 + * transpose + switch 15 CC 112 + * transpose - switch 16 CC 112 * Communication - * TX1 to mega 2 18 - * RX1 from mega 2 19 + * TX1 to teensy 18 + * RX1 from teensy 19 */ // includes #include "MIDI.h" #include "PushButton.h" +#include "ExpFilter.h" +#include "defs.h" // Constants const uint8_t NUM_KEYS = 30; -const uint8_t MIDI_OFFSET = 23; // to be modified -// Digital pin definition +const uint8_t MIDI_OFFSET = 47; // to be maybe modified + +const uint8_t NUM_SWITCHES = 15; +const uint8_t NUM_POTS = 16; +const uint8_t NUM_SELECTORS = 6; + +const uint8_t POT_FILTER_COEF = 15; +// Digital pin definition const uint8_t KEYS[NUM_KEYS] = { 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, @@ -86,25 +94,27 @@ const uint8_t KEYS[NUM_KEYS] = { }; PushButton keys[NUM_KEYS]; - +/* const uint8_t PIN_MOD_MIX_1 = 2; const uint8_t PIN_MOD_MIX_2 = 3; const uint8_t PIN_OSC_MOD = 4; const uint8_t PIN_GLIDE = 5; -const uint8_t PIN_DECAY = 6; -const uint8_t PIN_LFO_WAVEFORM = 7; +const uint8_t PIN_LFO_WAVEFORM = 6; +const uint8_t PIN_OSC3_CTRL = 7; -const uint8_t PIN_MIX_OSC_1 = 9; -const uint8_t PIN_MIX_OSC_2 = 10; -const uint8_t PIN_MIX_OSC_3 = 11; -const uint8_t PIN_MIX_OSC_NOISE = 12; -const uint8_t PIN_MIX_OSC_EXT = 13; -const uint8_t PIN_MIX_OSC_NOISE_COLOR = 14; +const uint8_t PIN_MIX_OSC_1 = 8; +const uint8_t PIN_MIX_OSC_2 = 9; +const uint8_t PIN_MIX_OSC_3 = 10; +const uint8_t PIN_MIX_OSC_NOISE = 11; +const uint8_t PIN_MIX_OSC_EXT = 12; +const uint8_t PIN_MIX_OSC_NOISE_COLOR = 13; +const uint8_t PIN_FUNCTION = 14; const uint8_t PIN_TRANSPOSE_PLUS = 52; const uint8_t PIN_TRANSPOSE_MINUS = 53; - +*/ // Analog pin definition +/* const uint8_t APIN_LFO_RATE = A0; const uint8_t APIN_MOD_MIX = A1; const uint8_t APIN_GLIDE = A2; @@ -123,49 +133,27 @@ const uint8_t APIN_MIX_OSC_2 = A12; const uint8_t APIN_MIX_OSC_3 = A13; const uint8_t APIN_MIX_NOISE = A14; const uint8_t APIN_MIX_EXT = A15; +*/ +const uint8_t APIN[NUM_POTS] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14, A15}; // Vars +uint16_t potState[NUM_POTS]; + +const uint8_t PIN[NUM_SWITCHES] = {3, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 17, 14, 15, 16}; + +PushButton switches[NUM_SWITCHES]; +ExpFilter pots[NUM_POTS]; +uint8_t selectors[NUM_SELECTORS]; + // Keyboard -bool keyState[NUM_KEYS] = {}; - -// Modulation -uint16_t lfoRate = 0; -uint16_t modMix = 0; -uint16_t glide = 0; -uint16_t pitchWheel = 0; -uint16_t modWheel = 0; -bool modMix1 = 0; -bool modMix2 = 0; -bool oscMod = 0; -bool glideEn = 0; -bool decayEn = 0; -bool lfoWaveform = 0; - -// Oscillators -uint8_t osc1Range = 0; -uint8_t osc1Waveform = 0; -uint8_t osc2Range = 0; -uint8_t osc2Waveform = 0; -uint8_t osc3Range = 0; -uint8_t osc3Waveform = 0; -bool osc3Control = 0; +bool keyState[NUM_KEYS]; // Mixer -uint16_t osc1Mix = 0; -uint16_t osc2Mix = 0; -uint16_t osc3Mix = 0; -uint16_t feedbackMix = 0; -uint16_t noiseMix = 0; -bool osc1MixSw = 0; -bool osc2MixSw = 0; -bool osc3MixSw = 0; -bool feedbackMixSw = 0; -bool noiseMixSw = 0; - -// Miscellaneous -bool function = 0; +uint16_t mix[5]; +bool mixSw[5]; +// Misc uint8_t defaultVelocity = 96; bool update = 0; @@ -176,46 +164,62 @@ struct midiSettings : public midi::DefaultSettings{ }; // The one we use on synth -//MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial1, midi1, midiSettings); +MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial1, midi1, midiSettings); // For debug purposes -MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial, midi1, midiSettings); +//MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial, midi1, midiSettings); void setup(){ // initialisation - // Serial is for debug - //Serial.begin(115200); midi1.setHandleControlChange(handleControlChange); midi1.begin(1); - // Serial 1 is for sending and receiving messages to and from Teensy -// Serial1.begin(115200); - // Serial 2 is for sending and receiving messages to and from Mega 2 -// Serial2.begin(115200); - // keys - /* - for(uint8_t i = 0; i < NUM_KEYS; ++i){ - keyState[i] = 0; - pinMode(KEYS[i], INPUT_PULLUP); - } - */ + midi1.turnThruOff(); + + // Key initialisation for(uint8_t i = 0; i < NUM_KEYS; ++i){ keys[i].begin(KEYS[i], INPUT_PULLUP); - keys[i].setDebounceDelay(1); + keys[i].setDebounceDelay(12); } + // Switches initialisation + for(uint8_t i = 0; i < NUM_SWITCHES; ++i){ + switches[i].begin(PIN[i], INPUT_PULLUP); + switches[i].setDebounceDelay(1); + } + // potentiometers initialisation + for (uint8_t i = 0; i < NUM_POTS; ++i){ + pots[i].begin(analogRead(APIN[i])); + pots[i].setCoef(POT_FILTER_COEF); + } } void loop(){ midi1.read(); updateKeys(); - updateControls(); updateSwitches(); + updateControls(); update = 0; } +void sendLongControlChange(uint8_t controlChange, uint16_t value, uint8_t channel = 1){ + uint8_t valueHigh = value >> 7; + uint8_t valueLow = value & 0x7F; + midi1.sendControlChange(controlChange, valueHigh, channel); + midi1.sendControlChange(controlChange + 32, valueLow, channel); +} + +void updateMix(uint8_t ch, bool fromSw = 0){ + uint16_t value = 0; + if(mixSw[ch]){ + value = mix[ch]; + } + if(value || fromSw) sendLongControlChange(CC_OSC1_MIX + ch, value, 1); + +} + void updateKeys(){ // reading keys for(uint8_t i = 0; i < NUM_KEYS; ++i){ @@ -248,13 +252,228 @@ void updateKeys(){ } -void updateControls(){ +void updateSwitches(){ + for(uint8_t i = 0; i < NUM_SWITCHES; ++i){ + uint8_t change = 0; + switches[i].update(); + + if(switches[i].justPressed()){ + change = 127; + } else if(switches[i].justReleased()){ + change = 0; + } else if(update){ + change = (uint8_t)switches[i].isPressed(); + change *= 127; + } else { + // If no change, skip midi update. + continue; + } + int8_t controlChange = -1; + + switch(i){ + case 0: + // pin 2 + controlChange = CC_MOD_MIX_1; + break; + case 1: + // pin 3 + controlChange = CC_MOD_MIX_2; + break; + case 2: + // pin 4 + controlChange = CC_OSC_MOD; + break; + case 3: + // pin 5 + controlChange = CC_PORTAMENTO_ON_OFF; + break; + case 4: + // pin 6 + controlChange = CC_LFO_SHAPE; + break; + case 5: + // pin 7 + controlChange = CC_OSC3_CTRL; + break; + case 6: + // pin 8 + mixSw[0] = (bool)change; + updateMix(0, 1); + continue; + case 7: + // pin 9 + mixSw[1] = (bool)change; + updateMix(1, 1); + continue; + case 8: + // pin 10 + mixSw[2] = (bool)change; + updateMix(2, 1); + continue; + case 9: + // pin 11 + mixSw[3] = (bool)change; + updateMix(3, 1); + continue; + case 10: + // pin 12 + mixSw[4] = (bool)change; + updateMix(4, 1); + continue; + case 11: + // pin 13 + controlChange = CC_NOISE_COLOR; + break; + case 12: + // pin 14 + controlChange = CC_FUNCTION; + break; + case 13: + // pin 15 + controlChange = CC_TRANSPOSE; + if(change == 0) continue; + change = 127; + break; + case 14: + // pin 16 + controlChange = CC_TRANSPOSE; + if(change == 127) continue; + change = 0; + break; + default: + continue; + } + midi1.sendControlChange(controlChange, change, 1); + } } -void updateSwitches(){ - +void updateControls(){ + for(uint8_t i = 0; i < NUM_POTS; ++i){ + uint16_t value = 0; + + value = pots[i].filter(analogRead(APIN[i])); + if((value != potState[i]) || update){ + potState[i] = value; + } else { + // If not change, skip midi update + continue; + } + + int8_t controlChange = -1; + + switch(i){ + case 0: + controlChange = CC_LFO_RATE; + break; + case 1: + controlChange = CC_MODULATION_MIX; + break; + case 2: + controlChange = CC_PORTAMENTO_TIME; + break; + case 3: + midi1.sendPitchBend((int16_t)value - 512, 1); + continue; + case 4: + controlChange = CC_MOD_WHEEL; + break; + case 5: + // rotary selector : value must be divided by ~170 + controlChange = CC_OSC1_RANGE; + value /= 170; + value = 5 - value; + // We have to check if the value after dividing is different from the previous one ! + if(value == selectors[0]){ + continue; + } else { + selectors[0] = value; + } + break; + case 6: + controlChange = CC_OSC2_RANGE; + value /= 170; + value = 5 - value; + if(value == selectors[1]){ + continue; + } else { + selectors[1] = value; + } + break; + case 7: + controlChange = CC_OSC3_RANGE; + value /= 170; + value = 5 - value; + if(value == selectors[2]){ + continue; + } else { + selectors[2] = value; + } + break; + case 8: + controlChange = CC_OSC1_WAVEFORM; + value /= 170; + if(value == selectors[3]){ + continue; + } else { + selectors[3] = value; + } + break; + case 9: + controlChange = CC_OSC2_WAVEFORM; + value /= 170; + if(value == selectors[4]){ + continue; + } else { + selectors[4] = value; + } + break; + case 10: + controlChange = CC_OSC3_WAVEFORM; + value /= 170; + if(value == selectors[5]){ + continue; + } else { + selectors[5] = value; + } + break; + case 11: + // mix is to be sent only if switch is on. +// controlChange = CC_OSC1_MIX; + mix[0] = value; + updateMix(0); + continue; + case 12: +// controlChange = CC_OSC2_MIX; + mix[1] = value; + updateMix(1); + continue; + case 13: +// controlChange = CC_OSC3_MIX; + mix[2] = value; + updateMix(2); + continue; + case 14: +// controlChange = CC_NOISE_MIX; + mix[3] = value; + updateMix(3); + continue; + case 15: +// controlChange = CC_FEEDBACK_MIX; + mix[4] = value; + updateMix(4); + continue; + default: + continue; + } + + if( controlChange < 32){ + sendLongControlChange(controlChange, value, 1); + } else { + midi1.sendControlChange(controlChange, value, 1); + } + } } void handleControlChange(uint8_t channel, uint8_t command, uint8_t value){ diff --git a/minimoog_mega_2/minimoog_mega_2.ino b/minimoog_mega_2/minimoog_mega_2.ino index 38f93b1..c19721c 100644 --- a/minimoog_mega_2/minimoog_mega_2.ino +++ b/minimoog_mega_2/minimoog_mega_2.ino @@ -52,8 +52,6 @@ * Communication * TX1 to teensy 18 * RX1 from teensy 19 - * TX2 to mega 1 16 - * RX2 from mega 1 17 */ // includes @@ -68,12 +66,16 @@ const uint8_t NUM_SWITCHES = 3; const uint8_t NUM_POTS = 15; const uint8_t POT_FILTER_COEF = 10; +// Note : pins are defined via tables, to improve code efficiency. // Digital pin definition + const uint8_t PIN_FILTER_MOD = 2; +/* const uint8_t PIN_KEYBOARD_CTRL_1 = 3; const uint8_t PIN_KEYBOARD_CTRL_2 = 4; - +*/ // Analog pin definition +/* const uint8_t APIN_GLOBAL_TUNE = A0; const uint8_t APIN_OSC2_TUNE = A1; const uint8_t APIN_OSC3_TUNE = A2; @@ -89,11 +91,12 @@ const uint8_t APIN_ATTACK = A11; const uint8_t APIN_DECAY = A12; const uint8_t APIN_SUSTAIN = A13; const uint8_t APIN_RELEASE = A14; +*/ -const uint8_t apin[NUM_POTS] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14}; +const uint8_t APIN[NUM_POTS] = {A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, A14}; // Variables -uint16_t potState[NUM_POTS] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +uint16_t potState[NUM_POTS]; PushButton switches[NUM_SWITCHES]; ExpFilter pots[NUM_POTS]; @@ -118,7 +121,7 @@ void setup(){ } for (uint8_t i = 0; i < NUM_POTS; ++i){ - pots[i].begin(analogRead(apin[i])); + pots[i].begin(analogRead(APIN[i])); pots[i].setCoef(POT_FILTER_COEF); } @@ -139,7 +142,7 @@ void updateControls(){ for(uint8_t i = 0; i < NUM_POTS; ++i){ uint16_t value = 0; - value = pots[i].filter(analogRead(apin[i])); + value = pots[i].filter(analogRead(APIN[i])); if((value != potState[i]) || update){ potState[i] = value; } else { @@ -147,7 +150,7 @@ void updateControls(){ continue; } - uint8_t controlChange = 0; + int8_t controlChange = -1; switch(i){ case 0: @@ -157,6 +160,9 @@ void updateControls(){ controlChange = CC_OSC2_TUNE; break; case 2: + // This one has a problem : sends CC10 (instead of CC13) then CC45 as should be. + // or maybe pure data has a bug that shifts bits. + // It seems it's a bug of pure data : 13 are replaced by 10 also for values... controlChange = CC_OSC3_TUNE; break; case 3: @@ -199,6 +205,7 @@ void updateControls(){ continue; } + uint8_t valueHigh = value >> 7; uint8_t valueLow = value & 0x7F; midi1.sendControlChange(controlChange, valueHigh, 1); @@ -223,7 +230,7 @@ void updateSwitches(){ continue; } - uint8_t controlChange = 0; + int8_t controlChange = -1; switch(i){ case 0: diff --git a/minimoog_teensy/audio_setup.h b/minimoog_teensy/audio_setup.h index 8c13350..bc2570c 100644 --- a/minimoog_teensy/audio_setup.h +++ b/minimoog_teensy/audio_setup.h @@ -5,53 +5,53 @@ #include // GUItool: begin automatically generated code -AudioSynthWaveformDc dcFilterEnvelope; //xy=169.76666259765625,540.0000305175781 -AudioSynthWaveformDc dcOscTune; //xy=228.76666259765625,149 -AudioSynthWaveformDc dcKeyTrack; //xy=231.76666259765625,113 -AudioSynthWaveformDc dcPitchBend; //xy=234.7666778564453,184.00001525878906 -AudioSynthWaveformDc dcFilter; //xy=336.76666259765625,595.0000305175781 -AudioSynthNoisePink pinkNoise; //xy=358.76666259765625,320 -AudioSynthNoiseWhite whiteNoise; //xy=361.76666259765625,284 -AudioSynthWaveformDc dcLfoFreq; //xy=365.76666259765625,376 -AudioEffectEnvelope filterEnvelope; //xy=367.76666259765625,540.0000305175781 -AudioSynthWaveformDc dcFilterKeyTrack; //xy=369.76666259765625,629.0000305175781 -AudioAmplifier ampPitchBend; //xy=407.7666778564453,184.00001525878906 -AudioMixer4 noiseMixer; //xy=544.7666625976562,317 -AudioSynthWaveformModulated lfoWaveform; //xy=548.7666625976562,374 -AudioAmplifier ampOsc3Mod; //xy=549.7666625976562,437 -AudioAmplifier ampModEg; //xy=559.7666625976562,475 -AudioMixer4 mainTuneMixer; //xy=631.7666625976562,133 -AudioMixer4 modMix2; //xy=755.7666015625,470 -AudioMixer4 modMix1; //xy=756.7666015625,399 -AudioSynthWaveformDc dcOsc3; //xy=758.7667541503906,193.00001525878906 -AudioMixer4 osc3ControlMixer; //xy=933.7667236328125,199 -AudioMixer4 modMixer; //xy=945.7666015625,448 -AudioSynthWaveformDc dcOsc2Tune; //xy=1105.566650390625,155.56666564941406 -AudioSynthWaveformDc dcOsc3Tune; //xy=1106.36669921875,221.56666564941406 -AudioAmplifier ampModWheel; //xy=1159.7666015625,448 -AudioMixer4 osc3TuneMixer; //xy=1289.36669921875,217.56666564941406 -AudioMixer4 osc2TuneMixer; //xy=1290.566650390625,153.56666564941406 -AudioSynthWaveformDc dcPulse; //xy=1306.566665649414,65.56666564941406 -AudioSynthWaveformModulated osc1Waveform; //xy=1523.7666015625,114 -AudioSynthWaveformModulated osc2Waveform; //xy=1524.7666015625,151 -AudioSynthWaveformModulated osc3Waveform; //xy=1524.7666015625,188 -AudioMixer4 oscMixer; //xy=1710.7666015625,157 -AudioMixer4 globalMixer; //xy=1919.7666015625,204 -AudioAmplifier ampPreFilter; //xy=2083.7666015625,203 -AudioMixer4 filterMixer; //xy=2101.7666625976562,446 -AudioFilterStateVariable vcf; //xy=2270.7666625976562,440 -AudioMixer4 bandMixer; //xy=2441.566665649414,435.56666564941406 -AudioEffectEnvelope mainEnvelope; //xy=2620.7664794921875,436 -AudioEffectBitcrusher bitCrushOutput; //xy=2856.5662841796875,433.566650390625 -AudioOutputI2S i2s; //xy=3046.7659912109375,435 +AudioSynthWaveformDc dcFilterEnvelope; //xy=169,540 +AudioSynthWaveformDc dcOscTune; //xy=228,149 +AudioSynthWaveformDc dcKeyTrack; //xy=231,113 +AudioSynthWaveformDc dcPitchBend; //xy=234,184 +AudioSynthWaveformDc dcFilter; //xy=336,595 +AudioSynthNoisePink pinkNoise; //xy=358,320 +AudioSynthWaveformDc dcLfoFreq; //xy=360,369 +AudioSynthNoiseWhite whiteNoise; //xy=361,284 +AudioEffectEnvelope filterEnvelope; //xy=367,540 +AudioSynthWaveformDc dcFilterKeyTrack; //xy=369,629 +AudioAmplifier ampPitchBend; //xy=407,184 +AudioMixer4 noiseMixer; //xy=544,317 +AudioSynthWaveformModulated lfoWaveform; //xy=549,369 +AudioAmplifier ampOsc3Mod; //xy=549,437 +AudioAmplifier ampModEg; //xy=559,475 +AudioMixer4 mainTuneMixer; //xy=631,133 +AudioMixer4 modMix2; //xy=755,470 +AudioMixer4 modMix1; //xy=756,399 +AudioSynthWaveformDc dcOsc3; //xy=758,193 +AudioMixer4 osc3ControlMixer; //xy=933,199 +AudioMixer4 modMixer; //xy=945,448 +AudioSynthWaveformDc dcOsc2Tune; //xy=1105,155 +AudioSynthWaveformDc dcOsc3Tune; //xy=1106,221 +AudioAmplifier ampModWheel; //xy=1159,448 +AudioMixer4 osc3TuneMixer; //xy=1289,217 +AudioMixer4 osc2TuneMixer; //xy=1290,153 +AudioSynthWaveformDc dcPulse; //xy=1306,65 +AudioSynthWaveformModulated osc1Waveform; //xy=1523,114 +AudioSynthWaveformModulated osc2Waveform; //xy=1524,151 +AudioSynthWaveformModulated osc3Waveform; //xy=1524,188 +AudioMixer4 oscMixer; //xy=1710,157 +AudioMixer4 globalMixer; //xy=1919,204 +AudioAmplifier ampPreFilter; //xy=2083,203 +AudioMixer4 filterMixer; //xy=2101,446 +AudioFilterStateVariable vcf; //xy=2270,440 +AudioMixer4 bandMixer; //xy=2441,435 +AudioEffectEnvelope mainEnvelope; //xy=2620,436 +AudioEffectBitcrusher bitCrushOutput; //xy=2856,433 +AudioOutputI2S i2s; //xy=3046,435 AudioConnection patchCord1(dcFilterEnvelope, filterEnvelope); AudioConnection patchCord2(dcOscTune, 0, mainTuneMixer, 1); AudioConnection patchCord3(dcKeyTrack, 0, mainTuneMixer, 0); AudioConnection patchCord4(dcPitchBend, ampPitchBend); AudioConnection patchCord5(dcFilter, 0, filterMixer, 2); AudioConnection patchCord6(pinkNoise, 0, noiseMixer, 1); -AudioConnection patchCord7(whiteNoise, 0, noiseMixer, 0); -AudioConnection patchCord8(dcLfoFreq, 0, lfoWaveform, 0); +AudioConnection patchCord7(dcLfoFreq, 0, lfoWaveform, 0); +AudioConnection patchCord8(whiteNoise, 0, noiseMixer, 0); AudioConnection patchCord9(filterEnvelope, 0, filterMixer, 1); AudioConnection patchCord10(filterEnvelope, ampModEg); AudioConnection patchCord11(dcFilterKeyTrack, 0, filterMixer, 3); @@ -87,10 +87,10 @@ AudioConnection patchCord40(globalMixer, ampPreFilter); AudioConnection patchCord41(ampPreFilter, 0, vcf, 0); AudioConnection patchCord42(filterMixer, 0, vcf, 1); AudioConnection patchCord43(vcf, 0, bandMixer, 0); -AudioConnection patchCord44(vcf, 2, bandMixer, 1); +AudioConnection patchCord44(vcf, 1, bandMixer, 1); AudioConnection patchCord45(bandMixer, mainEnvelope); -AudioConnection patchCord46(mainEnvelope, 0, globalMixer, 1); -AudioConnection patchCord47(mainEnvelope, bitCrushOutput); +AudioConnection patchCord46(mainEnvelope, bitCrushOutput); +AudioConnection patchCord47(mainEnvelope, 0, globalMixer, 1); AudioConnection patchCord48(bitCrushOutput, 0, i2s, 0); AudioConnection patchCord49(bitCrushOutput, 0, i2s, 1); // GUItool: end automatically generated code diff --git a/minimoog_teensy/minimoog_teensy.ino b/minimoog_teensy/minimoog_teensy.ino index e320d20..61a854a 100644 --- a/minimoog_teensy/minimoog_teensy.ino +++ b/minimoog_teensy/minimoog_teensy.ino @@ -24,6 +24,24 @@ * All user inputs are handled and send to the teensy board using midi commands */ + /* + * Pinout + * + * RX from mega 1 (through tension divider) 0 + * TX to mega 1 (serial 1) 1 + * mega 1 reset 2 + * + * RX from mega 2 (through tension divider) 16 + * TX to mega 2 (serial 4) 17 + * mega 2 reset 18 + * + * I2S OUT1A 7 + * I2S LRCLK1 20 + * I2S BCLK1 21 + * + * D+ & D- are also used to break the USB port to the rear panel + */ + #include #include #include @@ -33,7 +51,7 @@ #include "audio_setup.h" #include "defs.h" -//#include "MIDI.h" +#include "MIDI.h" // constants const uint8_t KEYTRACK_MAX = 10; @@ -47,14 +65,28 @@ const float NOTE_RATIO = 1.0594630943593; const float HALFTONE_TO_DC = (float)1 / (MAX_OCTAVE * 12); const float FILTER_HALFTONE_TO_DC = (float)1 / (FILTER_MAX_OCTAVE * 12); -const float MAX_MIX = 0.8; +const float MAX_MIX = 0.9; + +const uint16_t RESO = 1024; +const uint16_t HALF_RESO = RESO / 2; + +const int16_t PITCH_BEND_MIN = -168; +const int16_t PITCH_BEND_MAX = 134; +const int16_t PITCH_BEND_NEUTRAL = PITCH_BEND_MIN + (PITCH_BEND_MAX - PITCH_BEND_MIN) / 2; +const int16_t PITCH_BEND_COURSE = PITCH_BEND_MAX - PITCH_BEND_MIN; + +const uint16_t MOD_WHEEL_MIN = 360; +const uint16_t MOD_WHEEL_MAX = 666; +const uint16_t MOD_WHEEL_NEUTRAL = MOD_WHEEL_MIN + (MOD_WHEEL_MAX - MOD_WHEEL_MIN) / 2; +const uint16_t MOD_WHEEL_COURSE = MOD_WHEEL_MAX - MOD_WHEEL_MIN; + +const uint8_t MEGA1_RST = 2; +const uint8_t MEGA2_RST = 18; // variables uint8_t midiChannel = 1; -int8_t transposeOffset = 5; - uint16_t glide = 0; bool glideEn = 0; @@ -80,6 +112,9 @@ uint8_t waveforms[6] = {WAVEFORM_SINE, WAVEFORM_TRIANGLE, WAVEFORM_SAWTOOTH, uint8_t keyTrackIndex = 0; uint8_t keyTrack[KEYTRACK_MAX] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; +// double CC track +uint8_t ccTempValue[32]; + enum keyMode_t{ KEY_FIRST = 0, KEY_LAST, @@ -89,25 +124,49 @@ enum keyMode_t{ keyMode_t keyMode = KEY_LAST; +struct midiSettings : public midi::DefaultSettings{ +// static const bool UseRunningStatus = true; + static const long BaudRate = 115200; +}; -//MIDI_CREATE_DEFAULT_INSTANCE(); -//MIDI_CREATE_INSTANCE(UsbTransport, sUsbTransport, usbMIDI); +// The one we use on synth +MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial1, midi1, midiSettings); +MIDI_CREATE_CUSTOM_INSTANCE(HardwareSerial, Serial4, midi2, midiSettings); void setup() { - // midi settings, start and callback usbMIDI.begin(1); usbMIDI.setHandleNoteOn(handleNoteOn) usbMIDI.setHandleNoteOff(handleNoteOff) usbMIDI.setHandlePitchBend(handlePitchBend) usbMIDI.setHandleControlChange(handleControlChange); + // Mega resets + pinMode(MEGA1_RST, OUTPUT); + pinMode(MEGA2_RST, OUTPUT); + digitalWrite(MEGA1_RST, 0); + digitalWrite(MEGA2_RST, 0); - usbMIDI.setHandleNoteOn(handleNoteOn); - usbMIDI.setHandleNoteOff(handleNoteOff); - usbMIDI.setHandlePitchChange(handlePitchBend); - usbMIDI.setHandleControlChange(handleControlChange); + // midi settings, start and callback + midi1.begin(1); + midi1.turnThruOff(); + midi1.setHandleNoteOn(handleNoteOn); + midi1.setHandleNoteOff(handleNoteOff); + midi1.setHandlePitchBend(handlePitchBend); + midi1.setHandleControlChange(handleControlChange); - Serial.begin(115200); -// Serial.println("started..."); + midi2.begin(1); + midi2.turnThruOff(); + midi2.setHandleControlChange(handleControlChange); +/* + Serial.begin(115200); + Serial.println("started..."); +*/ AudioMemory(200); + digitalWrite(MEGA1_RST, 1); + digitalWrite(MEGA2_RST, 1); + delay(500); + + midi1.sendControlChange(CC_ASK_FOR_DATA, 127, 1); + midi2.sendControlChange(CC_ASK_FOR_DATA, 127, 1); + // audio settings // dc dcKeyTrack.amplitude(0.0); @@ -122,11 +181,10 @@ void setup() { dcOsc3Tune.amplitude(0.0); dcPulse.amplitude(-0.95); - // amp ampPitchBend.gain(3 * HALFTONE_TO_DC * 2); ampModWheel.gain(0); - ampPreFilter.gain(0.7); + ampPreFilter.gain(1.0); ampModEg.gain(0.01); ampOsc3Mod.gain(1); @@ -207,18 +265,22 @@ void setup() { bitCrushOutput.bits(16); bitCrushOutput.sampleRate(44100.0); - usbMIDI.sendControlChange(CC_ASK_FOR_DATA, 127, 1); + delay(500); + + midi1.sendControlChange(CC_ASK_FOR_DATA, 127, 1); + midi2.sendControlChange(CC_ASK_FOR_DATA, 127, 1); } void loop() { - usbMIDI.read(); + midi1.read(); + midi2.read(); } void noteOn(uint8_t note, uint8_t velocity, bool trigger = 1){ - float duration = (float)glideEn * (float)glide * 30.0; - float level = (float)note * HALFTONE_TO_DC; - float filterLevel = (float)note * FILTER_HALFTONE_TO_DC; + float duration = (float)glideEn * (float)glide * 3.75; + float level = ((float)note + 12 * transpose) * HALFTONE_TO_DC; + float filterLevel = ((float)note + 12 * transpose) * FILTER_HALFTONE_TO_DC; AudioNoInterrupts(); dcKeyTrack.amplitude(level, duration); @@ -265,6 +327,11 @@ int8_t keyTrackRemoveNote(uint8_t note){ } void handleNoteOn(uint8_t channel, uint8_t note, uint8_t velocity){ +/* + Serial.print("note "); + Serial.print(note); + Serial.println(" on"); +*/ switch(keyMode){ // When KEY_FIRST, we play the note only if there is not one already playing case KEY_FIRST: @@ -294,6 +361,11 @@ void handleNoteOn(uint8_t channel, uint8_t note, uint8_t velocity){ } void handleNoteOff(uint8_t channel, uint8_t note, uint8_t velocity){ +/* + Serial.print("note "); + Serial.print(note); + Serial.println(" off"); +*/ switch(keyMode){ case KEY_FIRST: /* if(keyTrackRemoveNote(note) == 0){ @@ -318,185 +390,228 @@ void handleNoteOff(uint8_t channel, uint8_t note, uint8_t velocity){ } void handlePitchBend(uint8_t channel, int16_t bend){ - dcPitchBend.amplitude(((float)bend - 64) / 128); + dcPitchBend.amplitude(((float)bend - PITCH_BEND_NEUTRAL) / PITCH_BEND_COURSE); + // Pitch bend goes from -168 to 134. + // neutral at -11 from up, -24 from down. :/ +/* + Serial.print("pitch bend :"); + Serial.println(bend); +*/ } void handleControlChange(uint8_t channel, uint8_t command, uint8_t value){ - // Serial.println("control change"); +/* + Serial.print("control change "); + Serial.println(command); +*/ + uint16_t longValue = 0; + if(command < 32){ + ccTempValue[command] = value; +/* + Serial.print("value : "); + Serial.print(value << 7); + Serial.print(" (sent : "); + Serial.print(value); + Serial.println(')'); +*/ + } else if(command < 64){ + longValue = (uint16_t)ccTempValue[command - 32]; + longValue <<= 7; + longValue += value; +/* + Serial.print("value : "); + Serial.print(longValue); + Serial.print(" (sent : "); + Serial.print(value); + Serial.println(')'); +*/ + } else { +/* + Serial.print("value : "); + Serial.println(value); +*/ + } switch(command){ - case CC_MOD_WHEEL: // CC_1 - ampModWheel.gain((float)value / 12 / 127); + case CC_MOD_WHEEL: + // CC_1 break; - case CC_MODULATION_MIX: // CC_3 - AudioNoInterrupts(); - modMixer.gain(0, (float)value / 127); - modMixer.gain(1, (127 - (float)value) / 127); - AudioInterrupts(); + case CC_MODULATION_MIX: + // CC_3 break; case CC_PORTAMENTO_TIME: // CC_5 - glide = value; break; case CC_OSC_TUNE: // CC_9 - dcOscTune.amplitude(HALFTONE_TO_DC * 2 * ((float)value - 64) / 127); break; case CC_OSC2_TUNE: // CC_12 - dcOsc2Tune.amplitude(HALFTONE_TO_DC * 12 * 2 * ((float)value - 64) / 127); break; case CC_OSC3_TUNE: // CC_13 - dcOsc3Tune.amplitude(HALFTONE_TO_DC * 12 * 2 * ((float)value - 64) / 127); break; case CC_OSC1_MIX: // CC_14 - oscMixer.gain(0, MAX_MIX * (float)value / 127); break; case CC_OSC2_MIX: // CC_15 - oscMixer.gain(1, MAX_MIX * (float)value / 127); break; case CC_OSC3_MIX: // CC_16 - oscMixer.gain(2, MAX_MIX * (float)value / 127); break; case CC_NOISE_MIX: // CC_17 - oscMixer.gain(3, MAX_MIX * (float)value / 127); break; case CC_FEEDBACK_MIX: // CC_18 - globalMixer.gain(1, MAX_MIX * (float)value / 127); break; case CC_FILTER_BAND: // CC_19 - AudioNoInterrupts(); - bandMixer.gain(0, ((float)value - 127) / 127); - bandMixer.gain(1, (float)value / 127); - AudioInterrupts(); break; case CC_FILTER_CUTOFF_FREQ: // CC_20 // vcf.frequency((float)value * 32); - dcFilter.amplitude(((float)value - 64) / 127); break; case CC_FILTER_EMPHASIS: // CC_21 - vcf.resonance(0.7 + (float)value / 29.53); break; case CC_FILTER_CONTOUR: // CC_22 - filterMixer.gain(1, (float)value / 127); + break; case CC_FILTER_ATTACK: // CC_23 - filterEnvelope.attack((float)value * 40); break; case CC_FILTER_DECAY: // CC_24 - filterDecay = value * 40; - AudioNoInterrupts(); - filterEnvelope.decay(filterDecay); - if(decay) filterEnvelope.release(filterDecay); - AudioInterrupts(); break; case CC_FILTER_SUSTAIN: // CC_25 - filterEnvelope.sustain((float)value / 127); break; case CC_FILTER_RELEASE: // CC_26 - filterEnvelope.release((float)value / 127); break; case CC_EG_ATTACK: // CC_27 - mainEnvelope.attack((float)value * 40); break; case CC_EG_DECAY: // CC_28 - egDecay = value * 40; - AudioNoInterrupts(); - mainEnvelope.decay(egDecay); - if(decay) mainEnvelope.release(egDecay); - AudioInterrupts(); break; case CC_EG_SUSTAIN: // CC_29 - mainEnvelope.sustain((float)value / 127); break; case CC_LFO_RATE: // CC_31 - dcLfoFreq.amplitude((float)value / 127); break; case CC_MOD_WHEEL_LSB: // CC_33 + ampModWheel.gain(((float)longValue - 1 - MOD_WHEEL_MIN) / 12 / MOD_WHEEL_COURSE); + // Mod wheel goes from 360 to 666. +/* + Serial.print("mod wheel : "); + Serial.println(longValue); +*/ break; case CC_MODULATION_MIX_LSB: // CC_35 + AudioNoInterrupts(); + modMixer.gain(0, (float)longValue / RESO); + modMixer.gain(1, (RESO - (float)longValue) / RESO); + AudioInterrupts(); break; case CC_PORTAMENTO_TIME_LSB: // CC_37 + glide = longValue; break; case CC_OSC_TUNE_LSB: // CC_41 + dcOscTune.amplitude(HALFTONE_TO_DC * 2 * ((float)longValue - HALF_RESO) / RESO); break; case CC_OSC2_TUNE_LSB: // CC_44 + dcOsc2Tune.amplitude(HALFTONE_TO_DC * 12 * 2 * ((float)longValue - HALF_RESO) / RESO); break; case CC_OSC3_TUNE_LSB: // CC_45 + dcOsc3Tune.amplitude(HALFTONE_TO_DC * 12 * 2 * ((float)longValue - HALF_RESO) / RESO); break; case CC_OSC1_MIX_LSB: // CC_46 + oscMixer.gain(0, MAX_MIX * (float)longValue / RESO); break; case CC_OSC2_MIX_LSB: // CC_47 + oscMixer.gain(1, MAX_MIX * (float)longValue / RESO); break; case CC_OSC3_MIX_LSB: // CC_48 + oscMixer.gain(2, MAX_MIX * (float)longValue / RESO); break; case CC_NOISE_MIX_LSB: // CC_49 + oscMixer.gain(3, MAX_MIX * (float)longValue / RESO); break; case CC_FEEDBACK_MIX_LSB: // CC_50 + globalMixer.gain(1, MAX_MIX * (float)longValue / RESO); break; case CC_FILTER_BAND_LSB: // CC_51 + AudioNoInterrupts(); + bandMixer.gain(0, ((float)longValue - RESO) / RESO); + bandMixer.gain(1, (float)longValue / RESO); + AudioInterrupts(); break; case CC_FILTER_CUTOFF_FREQ_LSB: // CC_52 + dcFilter.amplitude(((float)longValue - HALF_RESO) / RESO); break; case CC_FILTER_EMPHASIS_LSB: // CC_53 + vcf.resonance(0.7 + (float)longValue / 237.90); break; case CC_FILTER_CONTOUR_LSB: // CC_54 + filterMixer.gain(1, (float)longValue / RESO); break; case CC_FILTER_ATTACK_LSB: // CC_55 + filterEnvelope.attack((float)longValue * 5.0); break; case CC_FILTER_DECAY_LSB: // CC_56 + filterEnvelope.decay((float)longValue * 5.0); break; case CC_FILTER_SUSTAIN_LSB: // CC_57 + filterEnvelope.sustain((float)longValue / RESO); break; - case CC_EG_ATTACK_LSB: + case CC_FILTER_RELEASE_LSB: // CC_58 + filterEnvelope.release((float)longValue * 5.0); break; - case CC_EG_DECAY_LSB: + case CC_EG_ATTACK_LSB: // CC_59 + mainEnvelope.attack((float)longValue * 5.0); break; - case CC_EG_SUSTAIN_LSB: + case CC_EG_DECAY_LSB: // CC_60 + mainEnvelope.decay((float)longValue * 5.0); break; - case CC_LFO_RATE_LSB: + case CC_EG_SUSTAIN_LSB: + // CC_61 + mainEnvelope.sustain((float)longValue / RESO); + break; + case CC_EG_RELEASE_LSB: // CC_62 + mainEnvelope.release((float)longValue * 5.0); + break; + case CC_LFO_RATE_LSB: + // CC_63 + dcLfoFreq.amplitude((float)longValue / RESO); break; case CC_PORTAMENTO_ON_OFF: // CC_65 - if(value > 63){ + if(value < 64){ glideEn = 1; } else { glideEn = 0; @@ -608,6 +723,7 @@ void handleControlChange(uint8_t channel, uint8_t command, uint8_t value){ mainTuneMixer.gain(3, 0); } break; +/* case CC_DECAY_SW: // CC_116 AudioNoInterrupts(); @@ -622,6 +738,7 @@ void handleControlChange(uint8_t channel, uint8_t command, uint8_t value){ } AudioInterrupts(); break; +*/ case CC_MOD_MIX_1: // CC_117 AudioNoInterrupts(); @@ -646,12 +763,21 @@ void handleControlChange(uint8_t channel, uint8_t command, uint8_t value){ } AudioInterrupts(); break; -/* case CC_LFO_SHAPE: // CC_119 + case CC_LFO_SHAPE: + // CC_119 + AudioNoInterrupts(); if(value > 63){ + lfoWaveform.begin(WAVEFORM_TRIANGLE); + lfoWaveform.offset(0.0); + lfoWaveform.amplitude(1.0); } else { + lfoWaveform.begin(WAVEFORM_SQUARE); + lfoWaveform.offset(0.5); + lfoWaveform.amplitude(0.5); } + AudioInterrupts(); break; -*/ default: + default: break; } } diff --git a/misc/lateral panel pinout.txt b/misc/lateral panel pinout.txt new file mode 100644 index 0000000..907b07f --- /dev/null +++ b/misc/lateral panel pinout.txt @@ -0,0 +1,9 @@ +0 GND +1 5V +2 pitch bend +3 mod wheel +4 LFO rate +5 glide +6 LFO shape +7 function +8 not used \ No newline at end of file diff --git a/panels/svg/minimoog - panneau lateral.svg b/panels/svg/minimoog - panneau lateral.svg new file mode 100644 index 0000000..87f7812 --- /dev/null +++ b/panels/svg/minimoog - panneau lateral.svg @@ -0,0 +1,606 @@ + + + + + + + image/svg+xml + + + + + + /home/pierreloupm/Documents/Projets/Synthesizer/minimoog - panneau lateral.dxf - scale = 1.000000, origin = (0.000000, 0.000000), method = manual + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +