diff --git a/MIDI Open Theremin V3 HMI.bmp b/MIDI Open Theremin V3 HMI.bmp new file mode 100644 index 0000000..a00eb12 Binary files /dev/null and b/MIDI Open Theremin V3 HMI.bmp differ diff --git a/Open_Theremin_V3/application.cpp b/Open_Theremin_V3/application.cpp index 42cb400..bed0f5c 100644 --- a/Open_Theremin_V3/application.cpp +++ b/Open_Theremin_V3/application.cpp @@ -24,15 +24,43 @@ static float qMeasurement = 0; static int32_t volCalibrationBase = 0; -static uint16_t old_midi_note =0; -static uint16_t old_midi_volume =0; +static uint8_t new_midi_note =0; +static uint8_t old_midi_note =0; + +static uint8_t new_midi_loop_cc_val =0; +static uint8_t old_midi_loop_cc_val =0; + +static uint8_t midi_velocity = 0; + +static uint8_t new_midi_rod_cc_val =0; +static uint8_t old_midi_rod_cc_val =0; + +static uint16_t new_midi_bend =0; static uint16_t old_midi_bend = 0; +static uint8_t midi_bend_low; +static uint8_t midi_bend_high; +static double double_log_freq = 0; static double midi_key_follow = 0.5; + +// Configuration parameters +static uint8_t registerValue = 4; + // wavetable selector is defined and initialized in ihandlers.cpp static uint8_t midi_channel = 0; -static uint8_t midi_bend_range = 0; +static uint8_t old_midi_channel = 0; +static uint8_t midi_bend_range = 2; static uint8_t midi_volume_trigger = 0; - +static uint8_t flag_legato_on = 1; +static uint8_t flag_pitch_bend_on = 1; +static uint8_t loop_midi_cc = 7; +static uint8_t rod_midi_cc = 255; + +// tweakable paramameters +#define VELOCITY_SENS 9 // How easy it is to reach highest velocity (127). Something betwen 5 and 12. +#define PLAYER_ACCURACY 0.2 // between 0 (very accurate players) and 0.5 (not accurate at all) + +static uint16_t data_pot_value = 0; +static uint16_t old_data_pot_value = 0; Application::Application() : _state(PLAYING), @@ -65,8 +93,9 @@ initialiseInterrupts(); EEPROM.get(4,pitchCalibrationBase); EEPROM.get(8,volCalibrationBase); + init_parameters(); midi_setup(); - + } void Application::initialiseTimer() { @@ -169,25 +198,14 @@ void Application::loop() { uint16_t volumePotValue = 0; uint16_t pitchPotValue = 0; - int registerPotValue,registerPotValueL = 0; - int wavePotValue,wavePotValueL = 0; - uint8_t registerValue = 0; - - mloop: // Main loop avoiding the GCC "optimization" pitchPotValue = analogRead(PITCH_POT); volumePotValue = analogRead(VOLUME_POT); - registerPotValue = analogRead(REGISTER_SELECT_POT); - wavePotValue = analogRead(WAVE_SELECT_POT); - if ((registerPotValue-registerPotValueL) >= HYST_VAL || (registerPotValueL-registerPotValue) >= HYST_VAL) registerPotValueL=registerPotValue; - if (((wavePotValue-wavePotValueL) >= HYST_VAL) || ((wavePotValueL-wavePotValue) >= HYST_VAL)) wavePotValueL=wavePotValue; - - vWavetableSelector=wavePotValueL>>7; - registerValue=4-(registerPotValueL>>8); - + set_parameters (); + if (_state == PLAYING && HW_BUTTON_PRESSED) { _state = CALIBRATING; @@ -222,24 +240,16 @@ void Application::loop() { playStartupSound(); - if (registerPotValue < 512) // if register pot turned CCW - { - // calibrate heterodyne parameters - calibrate_pitch(); - calibrate_volume(); + // calibrate heterodyne parameters + calibrate_pitch(); + calibrate_volume(); - initialiseTimer(); - initialiseInterrupts(); + initialiseTimer(); + initialiseInterrupts(); - playCalibratingCountdownSound(); - calibrate(); - } - else // if register turned CW - { - // calibrate midi parameters - midi_calibrate (); - }; + playCalibratingCountdownSound(); + calibrate(); _mode=NORMAL; HW_LED1_ON;HW_LED2_OFF; @@ -501,11 +511,6 @@ void Application::delay_NOP(unsigned long time) { void Application::midi_setup() { - - EEPROM.get(12,midi_channel); - EEPROM.get(13,midi_bend_range); - EEPROM.get(14,midi_volume_trigger); - // Set MIDI baud rate: Serial.begin(115200); // Baudrate for midi to serial. Use a serial to midi router http://projectgus.github.com/hairless-midiserial/ //Serial.begin(31250); // Baudrate for real midi. Use din connection https://www.arduino.cc/en/Tutorial/Midi or HIDUINO https://github.com/ddiakopoulos/hiduino @@ -532,81 +537,55 @@ void Application::midi_msg_send(uint8_t channel, uint8_t midi_cmd1, uint8_t midi // If pitch bend range = 1 no picth bend is generated (portamento will do a better job) void Application::midi_application () { - uint16_t new_midi_note; - uint16_t new_midi_volume; + double delta_loop_cc_val = 0; + double calculated_velocity = 0; - uint16_t new_midi_bend; - uint8_t midi_bend_low; - uint8_t midi_bend_high; - - double double_log_freq; - double double_log_bend; - - // Calculate volume for midi - new_midi_volume = vScaledVolume >> 1; - new_midi_volume = min (new_midi_volume, 127); + + // Calculate loop antena cc value for midi + new_midi_loop_cc_val = vScaledVolume >> 1; + new_midi_loop_cc_val = min (new_midi_loop_cc_val, 127); + delta_loop_cc_val = (double)new_midi_loop_cc_val - (double)old_midi_loop_cc_val; - // Calculate note and pitch bend for midi + // Calculate log freq if (vPointerIncrement < 18) { // Highest note - new_midi_note = 0; - new_midi_bend = 8192; + double_log_freq = 0; } else if (vPointerIncrement > 26315) { // Lowest note - new_midi_note = 127; - new_midi_bend = 8192; + double_log_freq = 127; } else { // Find note in the playing range double_log_freq = (log (vPointerIncrement/17.152) / 0.057762265); // Precise note played in the logaritmic scale - double_log_bend = double_log_freq - old_midi_note; // How far from last played midi chromatic note we are - - // If too much far from last midi chromatic note played (midi_key_follow depends on pitch bend range) - if (abs (double_log_bend) >= midi_key_follow) - { - new_midi_note = round (double_log_freq); // Select the new midi chromatic note - double_log_bend = double_log_freq - new_midi_note; // calculate bend to reach precise note played - } - else - { - new_midi_note = old_midi_note; // No change - } + } + + // Calculate rod antena cc value for midi + new_midi_rod_cc_val = round (double_log_freq); - // If pitch bend range greater than 1 - if (midi_bend_range > 1) + // State machine for MIDI + switch (_midistate) + { + case MIDI_SILENT: + // Always refresh midi loop antena cc. + if (new_midi_loop_cc_val != old_midi_loop_cc_val) { - // use it to reach precise note played - new_midi_bend = 8192 + (8191 * double_log_bend / midi_bend_range); // Calculate midi pitch bend + midi_msg_send(midi_channel, 0xB0, loop_midi_cc, new_midi_loop_cc_val); + old_midi_loop_cc_val = new_midi_loop_cc_val; } else { - // Don't use pitch bend (portamento would do a beter job) - new_midi_bend = 8192; + // do nothing } - } - // Prepare the 2 bites of picth bend midi message - midi_bend_low = (int8_t) (new_midi_bend & 0x007F); - midi_bend_high = (int8_t) ((new_midi_bend & 0x3F80)>> 7); - - // State machine for MIDI - switch (_midistate) - { - case MIDI_SILENT: - // Synth sound could be in Release phase of ADSR or may have some delay or reverb effect so... - - // ... don't refresh pitch bend: - // Instruction "midi_key_follow = 0.5;" and unrefreshed notes would make pitch bend verry messy. - - // ... but always refresh midi volume value. - if (new_midi_volume != old_midi_volume) + // Always refresh midi rod antena cc if applicable. + if ((rod_midi_cc != 255) && (new_midi_rod_cc_val != old_midi_rod_cc_val)) { - midi_msg_send(midi_channel, 0xB0, 0x07, new_midi_volume); - old_midi_volume = new_midi_volume; + midi_msg_send(midi_channel, 0xB0, rod_midi_cc, new_midi_rod_cc_val); + old_midi_rod_cc_val = new_midi_rod_cc_val; } else { @@ -614,19 +593,35 @@ void Application::midi_application () } // If player's hand moves away from volume antenna - if (new_midi_volume > midi_volume_trigger) + if (new_midi_loop_cc_val > midi_volume_trigger) { + // Set key follow to the minimum in order to use closest note played as the center note + midi_key_follow = 0.5; + + // Calculate note and associated pitch bend + calculate_note_bend (); + // Send pitch bend to reach precise played note (send 8192 (no pitch bend) in case of midi_bend_range == 1) midi_msg_send(midi_channel, 0xE0, midi_bend_low, midi_bend_high); old_midi_bend = new_midi_bend; + + // Calculate velocity + if (midi_timer != 0) + { + calculated_velocity = (64 * (127 - (double)midi_volume_trigger) / 127) + (VELOCITY_SENS * (double)midi_volume_trigger * delta_loop_cc_val / (double)midi_timer); + midi_velocity = min (round (abs (calculated_velocity)), 127); + } + else + { + // should not happen + midi_velocity = 64; + } + // Play the note - midi_msg_send(midi_channel, 0x90, new_midi_note, 0x45); + midi_msg_send(midi_channel, 0x90, new_midi_note, midi_velocity); old_midi_note = new_midi_note; - // Set key follow so as next played note will be at limit of pitch bend range - midi_key_follow = (double)(midi_bend_range) - 0.2; - _midistate = MIDI_PLAYING; } else @@ -636,11 +631,22 @@ void Application::midi_application () break; case MIDI_PLAYING: - // Always refresh midi volume value - if (new_midi_volume != old_midi_volume) + // Always refresh midi loop antena cc. + if (new_midi_loop_cc_val != old_midi_loop_cc_val) + { + midi_msg_send(midi_channel, 0xB0, loop_midi_cc, new_midi_loop_cc_val); + old_midi_loop_cc_val = new_midi_loop_cc_val; + } + else + { + // do nothing + } + + // Always refresh midi rod antena cc if applicable. + if ((rod_midi_cc != 255) && (new_midi_rod_cc_val != old_midi_rod_cc_val)) { - midi_msg_send(midi_channel, 0xB0, 0x07, new_midi_volume); - old_midi_volume = new_midi_volume; + midi_msg_send(midi_channel, 0xB0, rod_midi_cc, new_midi_rod_cc_val); + old_midi_rod_cc_val = new_midi_rod_cc_val; } else { @@ -648,8 +654,22 @@ void Application::midi_application () } // If player's hand is far from volume antenna - if (new_midi_volume > midi_volume_trigger) + if (new_midi_loop_cc_val > midi_volume_trigger) { + if ( flag_legato_on == 1) + { + // Set key follow so as next played note will be at limit of pitch bend range + midi_key_follow = (double)(midi_bend_range) - PLAYER_ACCURACY; + } + else + { + // Set key follow to max so as no key follows + midi_key_follow = 127; + } + + // Calculate note and associated pitch bend + calculate_note_bend (); + // Refresh midi pitch bend value if (new_midi_bend != old_midi_bend) { @@ -666,7 +686,7 @@ void Application::midi_application () { // Play new note before muting old one to play legato on monophonic synth // (pitch pend management tends to break expected effect here) - midi_msg_send(midi_channel, 0x90, new_midi_note, 0x45); + midi_msg_send(midi_channel, 0x90, new_midi_note, midi_velocity); midi_msg_send(midi_channel, 0x90, old_midi_note, 0); old_midi_note = new_midi_note; } @@ -680,9 +700,6 @@ void Application::midi_application () // Send note off midi_msg_send(midi_channel, 0x90, old_midi_note, 0); - // Set key follow to the minimum in order to use closest note played as the center note for pitch bend next time - midi_key_follow = 0.5; - _midistate = MIDI_SILENT; } break; @@ -691,10 +708,6 @@ void Application::midi_application () // Send all note off midi_msg_send(midi_channel, 0xB0, 0x7B, 0x00); - // Mute long release notes - midi_msg_send(midi_channel, 0xB0, 0x07, 0); - old_midi_volume = 0; - _midistate = MIDI_MUTE; break; @@ -705,59 +718,210 @@ void Application::midi_application () } } -// midi_calibrate allows the user to set some midi parameters -// Set potentiometer accordingly to comments bellow BEFORE entering in midi calibration mode. -// Hear may help somewhat to determine entered values -void Application::midi_calibrate () +void Application::calculate_note_bend () { - uint16_t pot_channel; - uint16_t pot_bend_range; - uint16_t pot_volume_trigger; + double double_log_bend; + double double_norm_log_bend; + + double_log_bend = double_log_freq - old_midi_note; // How far from last played midi chromatic note we are - uint16_t bend_range_scale; + // If too much far from last midi chromatic note played (midi_key_follow depends on pitch bend range) + if ((abs (double_log_bend) >= midi_key_follow) && (midi_key_follow != 127)) + { + new_midi_note = round (double_log_freq); // Select the new midi chromatic note + double_log_bend = double_log_freq - new_midi_note; // calculate bend to reach precise note played + } + else + { + new_midi_note = old_midi_note; // No change + } - // Midi channel uses "Timbre" pot. - // Waveform may help user do determine which couple of channel is chosen (WF 1 Lo -> Ch1, WF 1 Hi -> Ch2, WF 2 Lo -> Ch3, etc...) - pot_channel = analogRead(WAVE_SELECT_POT); - midi_channel = (uint8_t)((pot_channel >> 6) & 0x000F); - EEPROM.put(12,midi_channel); - - - // Pitch bend range and associated distance between notes jumps use "Pitch" pot. - // The user shall set synth's pitch bend range acordingly to the selected Theremin's pitch bend range: - // 1 semitone, 2 semitones (standard), 7 semitones (a fifth), 12 semitones (an octave) or 24 semitones (two octaves). - // The "1 semitone" setting blocks pitch bend generation (use portamento on the synth) - pot_bend_range = analogRead(PITCH_POT); - bend_range_scale = pot_bend_range >> 7; - switch (bend_range_scale) + // If pitch bend activated + if (flag_pitch_bend_on == 1) { - case 0: - midi_bend_range = 1; - break; - case 1: - case 2: - midi_bend_range = 2; - break; - case 3: - case 4: - midi_bend_range = 7; - break; - case 5: - case 6: - midi_bend_range = 12; - break; - default: - midi_bend_range = 24; - break; + // use it to reach precise note played + double_norm_log_bend = (double_log_bend / midi_bend_range); + if (double_norm_log_bend > 1) + { + double_norm_log_bend = 1; + } + else if (double_norm_log_bend < -1) + { + double_norm_log_bend = -1; + } + new_midi_bend = 8192 + (8191 * double_norm_log_bend); // Calculate midi pitch bend + } + else + { + // Don't use pitch bend + new_midi_bend = 8192; } - EEPROM.put(13,midi_bend_range); + + // Prepare the 2 bites of picth bend midi message + midi_bend_low = (int8_t) (new_midi_bend & 0x007F); + midi_bend_high = (int8_t) ((new_midi_bend & 0x3F80)>> 7); +} + + + +void Application::init_parameters () +{ + // init data pot value to avoid 1st position to be taken into account + data_pot_value = analogRead(WAVE_SELECT_POT); + old_data_pot_value = data_pot_value; - // Volume trigger uses "Volume" pot - // Select a high value if some percussive sounds are played (so as it is heard when volume is not null) - pot_volume_trigger = analogRead(VOLUME_POT); - midi_volume_trigger = (uint8_t)((pot_volume_trigger >> 3) & 0x007F); - EEPROM.put(14,midi_volume_trigger); } - +void Application::set_parameters () +{ + uint16_t param_pot_value = 0; + + param_pot_value = analogRead(REGISTER_SELECT_POT); + data_pot_value = analogRead(WAVE_SELECT_POT); + + // If data pot moved + if (abs((int32_t)data_pot_value - (int32_t)old_data_pot_value) >= 8) + { + // Modify selected parameter + switch (param_pot_value >> 7) + { + case 0: + // Transpose + registerValue=4-(data_pot_value>>8); + break; + + case 1: + // Waveform + vWavetableSelector=data_pot_value>>7; + break; + + case 2: + // Channel + midi_channel = (uint8_t)((data_pot_value >> 6) & 0x000F); + if (old_midi_channel != midi_channel) + { + // Send all note off to avoid stuck notes + midi_msg_send(old_midi_channel, 0xB0, 0x7B, 0x00); + old_midi_channel = midi_channel; + } + break; + + case 3: + // Rod antenna mode + switch (data_pot_value >> 7) + { + case 0: + case 1: + flag_legato_on = 0; + flag_pitch_bend_on = 0; + break; + case 2: + case 3: + flag_legato_on = 0; + flag_pitch_bend_on = 1; + break; + case 4: + case 5: + flag_legato_on = 1; + flag_pitch_bend_on = 0; + break; + default: + flag_legato_on = 1; + flag_pitch_bend_on = 1; + break; + } + break; + + case 4: + // Pitch bend range + switch (data_pot_value >> 7) + { + case 0: + midi_bend_range = 1; + break; + case 1: + case 2: + midi_bend_range = 2; + break; + case 3: + case 4: + midi_bend_range = 7; + break; + case 5: + case 6: + midi_bend_range = 12; + break; + default: + midi_bend_range = 24; + break; + } + break; + + case 5: + // Volume trigger + midi_volume_trigger = (uint8_t)((data_pot_value >> 3) & 0x007F); + break; + + case 6: + //Rod antenna cc + switch (data_pot_value >> 7) + { + case 0: + rod_midi_cc = 255; // Nothing + break; + case 1: + case 2: + rod_midi_cc = 8; // Balance + break; + case 3: + case 4: + rod_midi_cc = 10; // Pan + break; + case 5: + case 6: + rod_midi_cc = 16; // Ribbon Controler + break; + default: + rod_midi_cc = 74; // Cutoff (exists of both loop and rod) + break; + } + break; + + + default: + // Loop antenna cc + switch (data_pot_value >> 7) + { + case 0: + loop_midi_cc = 1; // Modulation + break; + case 1: + loop_midi_cc = 7; // Volume + break; + case 2: + loop_midi_cc = 11; // Expression + break; + case 3: + loop_midi_cc = 71; // Resonnance + break; + case 4: + loop_midi_cc = 74; // Cutoff (exists of both loop and rod) + break; + case 5: + loop_midi_cc = 91; // Reverb + break; + case 6: + loop_midi_cc = 93; // Chorus + break; + default: + loop_midi_cc = 95; // Phaser + break; + } + break; + } + + // Memorize data pot value to monitor changes + old_data_pot_value = data_pot_value; + } +} diff --git a/Open_Theremin_V3/application.h b/Open_Theremin_V3/application.h index 659576d..23cae07 100644 --- a/Open_Theremin_V3/application.h +++ b/Open_Theremin_V3/application.h @@ -67,7 +67,9 @@ class Application { void midi_setup(); void midi_msg_send(uint8_t channel, uint8_t midi_cmd1, uint8_t midi_cmd2, uint8_t midi_value); void midi_application (); - void midi_calibrate (); + void calculate_note_bend (); + void init_parameters (); + void set_parameters (); }; diff --git a/README.md b/README.md index 4d1a009..a80fe84 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,5 @@ -This is V1.0. If you are interested, a much enhanced V2.0 is close to delivery under "V2-Project" branch : +## Open Theremin V3 with MIDI interface control software V2.0 for Arduino UNO -https://github.com/MrDham/OpenTheremin_V3_with_MIDI/tree/V2-Project - -It will be pulled soon to this "master" branch. - - -## Open Theremin V3 with MIDI interface control software V1.0 for Arduino UNO Based on Arduino UNO Software for the Open.Theremin version 3.0 Copyright (C) 2010-2016 by Urs Gaudenz https://github.com/GaudiLabs/OpenTheremin_V3 @@ -14,8 +8,7 @@ Urs makes also a very clear presentation of this MIDI feature on his website (ma http://www.gaudi.ch/OpenTheremin/index.php?option=com_content&view=article&id=200&Itemid=121 ### Don't click on the files! -Click on the "Download ZIP" Button to the right or [Click here](https://github.com/GaudiLabs/OpenTheremin_V3/archive/master.zip) -Then unpack the archive. +Click on the "clone or download" Button to the right. Then unpack the archive. ### Open Source Theremin based on the Arduino Platform @@ -34,7 +27,7 @@ http://www.gaudi.ch/OpenTheremin/ //Serial.begin(31250); // Baudrate for real midi. Use din connection https://www.arduino.cc/en/Tutorial/Midi or HIDUINO https://github.com/ddiakopoulos/hiduino - I tested "Hiduino" and "midi to serial" modes, both are OK. + I tested "Hiduino" and "MIDI to serial" modes, both are OK. 4. Selecting the correct usb port on Tools -> Serial Port 5. Select the correct arduino board from Tools -> Board @@ -45,75 +38,91 @@ Serial communication implemented for program monitoring purpose was removed (Par If you need to monitor calibration for antenna problem fixing, please use original master branch from https://github.com/GaudiLabs/OpenTheremin_V3. -Serial port is used to send midi messages now. +Serial port is used to send MIDI messages now. + +### How does it work ? -### How does it works ? +PITCH ANTENNA (ROD): -PITCH : -It uses first note detected at volume rise to generate a NOTEON. -Then it uses PITCHBEND to reach pitch as long as pitch bend range will do. -Beyond it generates a new NOTEON followed by a NOTEOFF for the previous note (legato). +It uses first note detected as hand moves away from volume loop antenna to generate a NOTE ON. +Then, it can use PITCH BEND messages (if activated) to reach exact pitch as long as pitch bend range will do. +Beyond, it can generate a new NOTE ON followed by a NOTE OFF for the previous note if legato mode activated. Pitch bend range can be configured (1, 2, 7, 12 or 24 semitones) to align with synth's maximum capabilities. -One exception is that I desactivated pitch bend in 1 semitone mode because portamento does a better job then. -VOLUME: -It generates VOLUME continuous controler, starting NOTEON and ending NOTE OFF (when playing staccato). -The trigger volume can be configured so as we have some volume at note attack on percussive sounds. +It can also generate selected MIDI Continuous Controler changes (MIDI CC). + +VOLUME ANTENNA (LOOP): + +It generates selected MIDI Continuous Controler changes (MIDI CC), starting NOTE ON and ending NOTE OFF (when playing staccato). + +The volume trigger can be configured so as we have some volume at note attack on percussive sounds. +The volume trigger setting is also used to set sensitivity for velocity (how fast volume loop hand is moving when note is triggered). +Matter of fact, the higher is this setting, less margin we have for volume variation. It is compensated by increase of velocity sensitivity. -CONFIGURATION: -There is two calibration mode: - 1. If REGISTER POT turned counter clockwise at entering in calibration mode - -> Runs normal calibration of antennas. +CALIBRATION: + +This device runs normal calibration of antennas after pushing button for 3 seconds as per initial project - 2. If REGISTER POT turned clockwise at entering in calibration mode - -> Records midi settings as per pot position BEFORE entering in calibration mode: - - VOLUME POT : sets volume trigger level - - PITCH POT : sets pitch bend range (1, 2, 7, 12 or 24 semitones) - Use exactly same pitch bend range on your synth. Maximum setting possible is recomended. - - TIMBRE POT : sets Channel. In the absence of graduation, timbre variation may help - (Wave Form 1 low = CH1, WF 1 High = CH2, WF 2 Low = CH3, etc...) - -MUTE BUTTON: -Sends ALL NOTE OFF on selected channel and stay in mute until it's pushed again. + SETTINGS: + + "Register" pot becomes "Selected Parameter" pot and have 8 positions. + "Timbre" pot becomes "Parameter's Value" and have a variable number of positions depending on selected parameter: + + 1. Register: 4 positions as in original Open Theremin V3 + 2. Timbre: 8 positions as in original Open Theremin V3 + 3. Channel: 16 positions (channel 1 to 16) + 4. Rod antenna mode: 4 positions + (Legato off/Pitch Bend off, Legato off/Pitch Bend on, Legato on/Pitch Bend off, Legato on/Pitch Bend on) + 5. Pitch bend range: 5 positions (1, 2, 7, 12, 24 Semitones). + For classical glissando and in order to have same note on audio and MIDI, use exactly same pitch bend range on your synth. + Maximum setting possible is recomended. + 6. Volume trigger / Velocity sensitivity (how fast moves the volume loop's hand): 128 positions (0 to 127) + 7. Rod antenna MIDI CC: 5 positions + (None, 8-Balance, 10-Pan, 16-Ribbon controler, 74-cutoff) + 8. Loop antenna MIDI CC: 8 positions + (1-Modulation, 7-Volume, 11-Expression, 71-Resonnance, 74-Cutoff, 91-Reverb, 93-Chorus, 95-Phaser) -AUDIO: -Audio processing from antennas to output jack, including pots, LEDs and button functions, is exactly the same as in open theremin V3. +Select a Parameter and move "Parameter's Value" to change corresponding setting. +The picture at https://github.com/MrDham/OpenTheremin_V3_with_MIDI/blob/V2-Project/MIDI%20Open%20Theremin%20V3%20HMI.bmp gives an example of possible HMI: on "Value" pot, red lines have 4 positions, grey lines have 5 positions and yellow lines have 8 positions. On "Parameter" pot you see coloured lines indicating which colour to follow for the "Value" pot. -### What can I do to get a theremin like glissando? +Manipulation of "Rod antenna MIDI CC" and "Loop antenna MIDI CC" is not error proof. MIDI newbies should be advised to change their value in MUTE mode. + +Volume trigger = 127 (Maximum) won't generate any NOTE ON. It can be used to generate MIDI CC only. -Set pitch bend range of the theremin with a high value (12 semitones or 24 semitones). +Default configuration is: Register = Lowest Register, Timbre = 1st Waveform, Channel = MIDI Channel 1, Rod antenna mode = Legato on/Pitch Bend on, Pitch bend range = 2 Semitones, Volume trigger = 0, Rod antenna MIDI CC = None, Loop antenna MIDI CC = 7-Volume. -Set pitch bend range of the synth with the same value -Closest to real theremin settings (pitch bend range = 24 semitones): - 1. Set pots like this: Volume = Min, Pitch = Max, Register = Max, Timbre = Midi channel. +MUTE BUTTON: - 2. Push button for two seconds. +Sends ALL NOTE OFF on selected channel and stay in mute until it is pushed again. - 3. Then set pots as for audio (Example : Volume = Mid, Pitch = Mid, Register = Wanted octave, Timbre = any) +AUDIO: - 4. Play (you can mix synth and audio if you want) +Audio processing from antennas to output jack, including volume and pitch pots, LEDs and button functions, is exactly the same as in open theremin V3. You can play the Audio and the MIDI side by side. + + +### What can I do to get a theremin like glissando? + +Activate picth bend and set pitch bend range of the theremin with a high value (12 semitones or 24 semitones). +Set pitch bend range of the synth with the same value. - ### If I do not trigger with the volume hand it also seems to trigger a new tone with the pitch antenna. Guess this is how MIDI works. -Yes, with settings above, if you trigger a note (with volume loop) and go in one direction (with pitch antenna) a new note will be triggered after two octaves. +When legato mode is activated, if you trigger a note (with volume loop) and go in one direction (with pitch antenna) a new note will be triggered at the limit of pitch bend range. + +Legato mode is used as a workaround for a limitation of MIDI (max 24 semitones pitch bend). Maybe some synth can perform pitch bend on more that 2 octaves but none of mine does... + -This is a limitation of midi. Maybe some synth can perform pitch bend on more that 2 octaves but none of mine does... +### Tweakable parameters (in application.cpp): +Changing this to your taste may require some test and trial. -### Tweak -In the following lines of application.cpp: +"#define VELOCITY_SENS 9" -> How easy it is to reach highest velocity (127). Something betwen 5 and 12. - // Set key follow so as next played note will be at limit of pitch bend range - midi_key_follow = (double)(midi_bend_range) - 0.2; +"#define PLAYER_ACCURACY 0.2" -> Pitch accuracy of player. Tolerance on note center for changing notes when playing legato. From 0 (very accurate players) to 0.5 (may generate note toggling). -The "-0.2" could be changed into another value from "0" to "-0.5" depending on how good you are to reach center of the note that you play. "0" is for very good players. "-0.5" is very permissive and generates note toggling in 1 semitone mode. "-0.2" is the limit where my favourite chromatic tuner's green LED turns off (and it is OK for me). ### LICENSE