Merge pull request #11 from MrDham/V2-Project

V2 project
pull/12/head
MrDham 7 years ago committed by GitHub
commit 52255b6300
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. BIN
      MIDI Open Theremin V3 HMI.bmp
  2. 476
      Open_Theremin_V3/application.cpp
  3. 4
      Open_Theremin_V3/application.h
  4. 119
      README.md

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 KiB

@ -24,15 +24,43 @@ static float qMeasurement = 0;
static int32_t volCalibrationBase = 0; static int32_t volCalibrationBase = 0;
static uint16_t old_midi_note =0; static uint8_t new_midi_note =0;
static uint16_t old_midi_volume =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 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; 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_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 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() Application::Application()
: _state(PLAYING), : _state(PLAYING),
@ -65,8 +93,9 @@ initialiseInterrupts();
EEPROM.get(4,pitchCalibrationBase); EEPROM.get(4,pitchCalibrationBase);
EEPROM.get(8,volCalibrationBase); EEPROM.get(8,volCalibrationBase);
init_parameters();
midi_setup(); midi_setup();
} }
void Application::initialiseTimer() { void Application::initialiseTimer() {
@ -169,25 +198,14 @@ void Application::loop() {
uint16_t volumePotValue = 0; uint16_t volumePotValue = 0;
uint16_t pitchPotValue = 0; uint16_t pitchPotValue = 0;
int registerPotValue,registerPotValueL = 0;
int wavePotValue,wavePotValueL = 0;
uint8_t registerValue = 0;
mloop: // Main loop avoiding the GCC "optimization" mloop: // Main loop avoiding the GCC "optimization"
pitchPotValue = analogRead(PITCH_POT); pitchPotValue = analogRead(PITCH_POT);
volumePotValue = analogRead(VOLUME_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; set_parameters ();
if (((wavePotValue-wavePotValueL) >= HYST_VAL) || ((wavePotValueL-wavePotValue) >= HYST_VAL)) wavePotValueL=wavePotValue;
vWavetableSelector=wavePotValueL>>7;
registerValue=4-(registerPotValueL>>8);
if (_state == PLAYING && HW_BUTTON_PRESSED) if (_state == PLAYING && HW_BUTTON_PRESSED)
{ {
_state = CALIBRATING; _state = CALIBRATING;
@ -222,24 +240,16 @@ void Application::loop() {
playStartupSound(); playStartupSound();
if (registerPotValue < 512) // if register pot turned CCW // calibrate heterodyne parameters
{ calibrate_pitch();
// calibrate heterodyne parameters calibrate_volume();
calibrate_pitch();
calibrate_volume();
initialiseTimer(); initialiseTimer();
initialiseInterrupts(); initialiseInterrupts();
playCalibratingCountdownSound(); playCalibratingCountdownSound();
calibrate(); calibrate();
}
else // if register turned CW
{
// calibrate midi parameters
midi_calibrate ();
};
_mode=NORMAL; _mode=NORMAL;
HW_LED1_ON;HW_LED2_OFF; HW_LED1_ON;HW_LED2_OFF;
@ -501,11 +511,6 @@ void Application::delay_NOP(unsigned long time) {
void Application::midi_setup() 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: // 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(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 //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) // If pitch bend range = 1 no picth bend is generated (portamento will do a better job)
void Application::midi_application () void Application::midi_application ()
{ {
uint16_t new_midi_note; double delta_loop_cc_val = 0;
uint16_t new_midi_volume; double calculated_velocity = 0;
uint16_t new_midi_bend;
uint8_t midi_bend_low; // Calculate loop antena cc value for midi
uint8_t midi_bend_high; new_midi_loop_cc_val = vScaledVolume >> 1;
new_midi_loop_cc_val = min (new_midi_loop_cc_val, 127);
double double_log_freq; delta_loop_cc_val = (double)new_midi_loop_cc_val - (double)old_midi_loop_cc_val;
double double_log_bend;
// Calculate volume for midi
new_midi_volume = vScaledVolume >> 1;
new_midi_volume = min (new_midi_volume, 127);
// Calculate note and pitch bend for midi // Calculate log freq
if (vPointerIncrement < 18) if (vPointerIncrement < 18)
{ {
// Highest note // Highest note
new_midi_note = 0; double_log_freq = 0;
new_midi_bend = 8192;
} }
else if (vPointerIncrement > 26315) else if (vPointerIncrement > 26315)
{ {
// Lowest note // Lowest note
new_midi_note = 127; double_log_freq = 127;
new_midi_bend = 8192;
} }
else else
{ {
// Find note in the playing range // Find note in the playing range
double_log_freq = (log (vPointerIncrement/17.152) / 0.057762265); // Precise note played in the logaritmic scale 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) // Calculate rod antena cc value for midi
if (abs (double_log_bend) >= midi_key_follow) new_midi_rod_cc_val = round (double_log_freq);
{
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
}
// If pitch bend range greater than 1 // State machine for MIDI
if (midi_bend_range > 1) 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 midi_msg_send(midi_channel, 0xB0, loop_midi_cc, new_midi_loop_cc_val);
new_midi_bend = 8192 + (8191 * double_log_bend / midi_bend_range); // Calculate midi pitch bend old_midi_loop_cc_val = new_midi_loop_cc_val;
} }
else else
{ {
// Don't use pitch bend (portamento would do a beter job) // do nothing
new_midi_bend = 8192;
} }
}
// Prepare the 2 bites of picth bend midi message // Always refresh midi rod antena cc if applicable.
midi_bend_low = (int8_t) (new_midi_bend & 0x007F); if ((rod_midi_cc != 255) && (new_midi_rod_cc_val != old_midi_rod_cc_val))
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)
{ {
midi_msg_send(midi_channel, 0xB0, 0x07, new_midi_volume); midi_msg_send(midi_channel, 0xB0, rod_midi_cc, new_midi_rod_cc_val);
old_midi_volume = new_midi_volume; old_midi_rod_cc_val = new_midi_rod_cc_val;
} }
else else
{ {
@ -614,19 +593,35 @@ void Application::midi_application ()
} }
// If player's hand moves away from volume antenna // 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) // 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); midi_msg_send(midi_channel, 0xE0, midi_bend_low, midi_bend_high);
old_midi_bend = new_midi_bend; 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 // 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; 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; _midistate = MIDI_PLAYING;
} }
else else
@ -636,11 +631,22 @@ void Application::midi_application ()
break; break;
case MIDI_PLAYING: case MIDI_PLAYING:
// Always refresh midi volume value // Always refresh midi loop antena cc.
if (new_midi_volume != old_midi_volume) 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); midi_msg_send(midi_channel, 0xB0, rod_midi_cc, new_midi_rod_cc_val);
old_midi_volume = new_midi_volume; old_midi_rod_cc_val = new_midi_rod_cc_val;
} }
else else
{ {
@ -648,8 +654,22 @@ void Application::midi_application ()
} }
// If player's hand is far from volume antenna // 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 // Refresh midi pitch bend value
if (new_midi_bend != old_midi_bend) 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 // Play new note before muting old one to play legato on monophonic synth
// (pitch pend management tends to break expected effect here) // (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); midi_msg_send(midi_channel, 0x90, old_midi_note, 0);
old_midi_note = new_midi_note; old_midi_note = new_midi_note;
} }
@ -680,9 +700,6 @@ void Application::midi_application ()
// Send note off // Send note off
midi_msg_send(midi_channel, 0x90, old_midi_note, 0); 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; _midistate = MIDI_SILENT;
} }
break; break;
@ -691,10 +708,6 @@ void Application::midi_application ()
// Send all note off // Send all note off
midi_msg_send(midi_channel, 0xB0, 0x7B, 0x00); 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; _midistate = MIDI_MUTE;
break; break;
@ -705,59 +718,210 @@ void Application::midi_application ()
} }
} }
// midi_calibrate allows the user to set some midi parameters void Application::calculate_note_bend ()
// Set potentiometer accordingly to comments bellow BEFORE entering in midi calibration mode.
// Hear may help somewhat to determine entered values
void Application::midi_calibrate ()
{ {
uint16_t pot_channel; double double_log_bend;
uint16_t pot_bend_range; double double_norm_log_bend;
uint16_t pot_volume_trigger;
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. // If pitch bend activated
// 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...) if (flag_pitch_bend_on == 1)
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)
{ {
case 0: // use it to reach precise note played
midi_bend_range = 1; double_norm_log_bend = (double_log_bend / midi_bend_range);
break; if (double_norm_log_bend > 1)
case 1: {
case 2: double_norm_log_bend = 1;
midi_bend_range = 2; }
break; else if (double_norm_log_bend < -1)
case 3: {
case 4: double_norm_log_bend = -1;
midi_bend_range = 7; }
break; new_midi_bend = 8192 + (8191 * double_norm_log_bend); // Calculate midi pitch bend
case 5: }
case 6: else
midi_bend_range = 12; {
break; // Don't use pitch bend
default: new_midi_bend = 8192;
midi_bend_range = 24;
break;
} }
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;
}
}

@ -67,7 +67,9 @@ class Application {
void midi_setup(); void midi_setup();
void midi_msg_send(uint8_t channel, uint8_t midi_cmd1, uint8_t midi_cmd2, uint8_t midi_value); void midi_msg_send(uint8_t channel, uint8_t midi_cmd1, uint8_t midi_cmd2, uint8_t midi_value);
void midi_application (); void midi_application ();
void midi_calibrate (); void calculate_note_bend ();
void init_parameters ();
void set_parameters ();
}; };

@ -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 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 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 http://www.gaudi.ch/OpenTheremin/index.php?option=com_content&view=article&id=200&Itemid=121
### Don't click on the files! ### 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) Click on the "clone or download" Button to the right. Then unpack the archive.
Then unpack the archive.
### Open Source Theremin based on the Arduino Platform ### 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 //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 4. Selecting the correct usb port on Tools -> Serial Port
5. Select the correct arduino board from Tools -> Board 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 If you need to monitor calibration for antenna problem fixing, please use original master branch from
https://github.com/GaudiLabs/OpenTheremin_V3. 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 as hand moves away from volume loop antenna to generate a NOTE ON.
It uses first note detected at volume rise to generate a NOTEON. Then, it can use PITCH BEND messages (if activated) to reach exact pitch as long as pitch bend range will do.
Then it uses PITCHBEND to reach 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.
Beyond it generates a new NOTEON followed by a NOTEOFF for the previous note (legato).
Pitch bend range can be configured (1, 2, 7, 12 or 24 semitones) to align with synth's maximum capabilities. 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 can also generate selected MIDI Continuous Controler changes (MIDI CC).
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. 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: CALIBRATION:
There is two calibration mode:
1. If REGISTER POT turned counter clockwise at entering in calibration mode This device runs normal calibration of antennas after pushing button for 3 seconds as per initial project
-> Runs normal calibration of antennas.
2. If REGISTER POT turned clockwise at entering in calibration mode SETTINGS:
-> Records midi settings as per pot position BEFORE entering in calibration mode:
"Register" pot becomes "Selected Parameter" pot and have 8 positions.
VOLUME POT : sets volume trigger level "Timbre" pot becomes "Parameter's Value" and have a variable number of positions depending on selected parameter:
PITCH POT : sets pitch bend range (1, 2, 7, 12 or 24 semitones) 1. Register: 4 positions as in original Open Theremin V3
Use exactly same pitch bend range on your synth. Maximum setting possible is recomended. 2. Timbre: 8 positions as in original Open Theremin V3
3. Channel: 16 positions (channel 1 to 16)
TIMBRE POT : sets Channel. In the absence of graduation, timbre variation may help 4. Rod antenna mode: 4 positions
(Wave Form 1 low = CH1, WF 1 High = CH2, WF 2 Low = CH3, etc...) (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).
MUTE BUTTON: For classical glissando and in order to have same note on audio and MIDI, use exactly same pitch bend range on your synth.
Sends ALL NOTE OFF on selected channel and stay in mute until it's pushed again. 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: Select a Parameter and move "Parameter's Value" to change corresponding setting.
Audio processing from antennas to output jack, including pots, LEDs and button functions, is exactly the same as in open theremin V3.
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. ### 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 "#define VELOCITY_SENS 9" -> How easy it is to reach highest velocity (127). Something betwen 5 and 12.
In the following lines of application.cpp:
// Set key follow so as next played note will be at limit of pitch bend range "#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).
midi_key_follow = (double)(midi_bend_range) - 0.2;
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 ### LICENSE

Loading…
Cancel
Save