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. 408
      Open_Theremin_V3/application.cpp
  3. 4
      Open_Theremin_V3/application.h
  4. 109
      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,6 +93,7 @@ initialiseInterrupts();
EEPROM.get(4,pitchCalibrationBase); EEPROM.get(4,pitchCalibrationBase);
EEPROM.get(8,volCalibrationBase); EEPROM.get(8,volCalibrationBase);
init_parameters();
midi_setup(); midi_setup();
} }
@ -169,24 +198,13 @@ 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;
if (((wavePotValue-wavePotValueL) >= HYST_VAL) || ((wavePotValueL-wavePotValue) >= HYST_VAL)) wavePotValueL=wavePotValue;
vWavetableSelector=wavePotValueL>>7; set_parameters ();
registerValue=4-(registerPotValueL>>8);
if (_state == PLAYING && HW_BUTTON_PRESSED) if (_state == PLAYING && HW_BUTTON_PRESSED)
{ {
@ -222,8 +240,6 @@ void Application::loop() {
playStartupSound(); playStartupSound();
if (registerPotValue < 512) // if register pot turned CCW
{
// calibrate heterodyne parameters // calibrate heterodyne parameters
calibrate_pitch(); calibrate_pitch();
calibrate_volume(); calibrate_volume();
@ -234,12 +250,6 @@ void Application::loop() {
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,101 +537,91 @@ 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;
uint8_t midi_bend_high;
double double_log_freq; // Calculate loop antena cc value for midi
double double_log_bend; new_midi_loop_cc_val = vScaledVolume >> 1;
new_midi_loop_cc_val = min (new_midi_loop_cc_val, 127);
// Calculate volume for midi delta_loop_cc_val = (double)new_midi_loop_cc_val - (double)old_midi_loop_cc_val;
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);
// State machine for MIDI
switch (_midistate)
{ {
new_midi_note = round (double_log_freq); // Select the new midi chromatic note case MIDI_SILENT:
double_log_bend = double_log_freq - new_midi_note; // calculate bend to reach precise note played // 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 else
{ {
new_midi_note = old_midi_note; // No change // do nothing
} }
// If pitch bend range greater than 1 // Always refresh midi rod antena cc if applicable.
if (midi_bend_range > 1) if ((rod_midi_cc != 255) && (new_midi_rod_cc_val != old_midi_rod_cc_val))
{ {
// use it to reach precise note played midi_msg_send(midi_channel, 0xB0, rod_midi_cc, new_midi_rod_cc_val);
new_midi_bend = 8192 + (8191 * double_log_bend / midi_bend_range); // Calculate midi pitch bend old_midi_rod_cc_val = new_midi_rod_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 // If player's hand moves away from volume antenna
midi_bend_low = (int8_t) (new_midi_bend & 0x007F); if (new_midi_loop_cc_val > midi_volume_trigger)
midi_bend_high = (int8_t) ((new_midi_bend & 0x3F80)>> 7);
// State machine for MIDI
switch (_midistate)
{ {
case MIDI_SILENT: // Set key follow to the minimum in order to use closest note played as the center note
// Synth sound could be in Release phase of ADSR or may have some delay or reverb effect so... midi_key_follow = 0.5;
// ... don't refresh pitch bend: // Calculate note and associated pitch bend
// Instruction "midi_key_follow = 0.5;" and unrefreshed notes would make pitch bend verry messy. calculate_note_bend ();
// ... but always refresh midi volume value. // Send pitch bend to reach precise played note (send 8192 (no pitch bend) in case of midi_bend_range == 1)
if (new_midi_volume != old_midi_volume) midi_msg_send(midi_channel, 0xE0, midi_bend_low, midi_bend_high);
old_midi_bend = new_midi_bend;
// Calculate velocity
if (midi_timer != 0)
{ {
midi_msg_send(midi_channel, 0xB0, 0x07, new_midi_volume); calculated_velocity = (64 * (127 - (double)midi_volume_trigger) / 127) + (VELOCITY_SENS * (double)midi_volume_trigger * delta_loop_cc_val / (double)midi_timer);
old_midi_volume = new_midi_volume; midi_velocity = min (round (abs (calculated_velocity)), 127);
} }
else else
{ {
// do nothing // should not happen
midi_velocity = 64;
} }
// If player's hand moves away from volume antenna
if (new_midi_volume > midi_volume_trigger)
{
// 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;
// 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, 0x07, new_midi_volume); midi_msg_send(midi_channel, 0xB0, loop_midi_cc, new_midi_loop_cc_val);
old_midi_volume = new_midi_volume; 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, rod_midi_cc, new_midi_rod_cc_val);
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,31 +718,124 @@ 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 double double_log_bend;
void Application::midi_calibrate () double double_norm_log_bend;
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) && (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
}
// If pitch bend activated
if (flag_pitch_bend_on == 1)
{
// 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;
}
// 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 ()
{ {
uint16_t pot_channel; // init data pot value to avoid 1st position to be taken into account
uint16_t pot_bend_range; data_pot_value = analogRead(WAVE_SELECT_POT);
uint16_t pot_volume_trigger; old_data_pot_value = data_pot_value;
uint16_t bend_range_scale; }
// Midi channel uses "Timbre" pot. void Application::set_parameters ()
// 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); uint16_t param_pot_value = 0;
midi_channel = (uint8_t)((pot_channel >> 6) & 0x000F);
EEPROM.put(12,midi_channel);
param_pot_value = analogRead(REGISTER_SELECT_POT);
data_pot_value = analogRead(WAVE_SELECT_POT);
// Pitch bend range and associated distance between notes jumps use "Pitch" pot. // If data pot moved
// The user shall set synth's pitch bend range acordingly to the selected Theremin's pitch bend range: if (abs((int32_t)data_pot_value - (int32_t)old_data_pot_value) >= 8)
// 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) // Modify selected parameter
pot_bend_range = analogRead(PITCH_POT); switch (param_pot_value >> 7)
bend_range_scale = pot_bend_range >> 7; {
switch (bend_range_scale) 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: case 0:
midi_bend_range = 1; midi_bend_range = 1;
@ -750,14 +856,72 @@ void Application::midi_calibrate ()
midi_bend_range = 24; midi_bend_range = 24;
break; break;
} }
break;
EEPROM.put(13,midi_bend_range); 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;
// 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);
}
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 works ? ### How does it work ?
PITCH : PITCH ANTENNA (ROD):
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. It uses first note detected as hand moves away from volume loop antenna to generate a NOTE ON.
Beyond it generates a new NOTEON followed by a NOTEOFF for the previous note (legato). 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. 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.
CONFIGURATION: VOLUME ANTENNA (LOOP):
There is two calibration mode:
1. If REGISTER POT turned counter clockwise at entering in calibration mode
-> Runs normal calibration of antennas.
2. If REGISTER POT turned clockwise at entering in calibration mode It generates selected MIDI Continuous Controler changes (MIDI CC), starting NOTE ON and ending NOTE OFF (when playing staccato).
-> Records midi settings as per pot position BEFORE entering in calibration mode:
VOLUME POT : sets volume trigger level 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.
PITCH POT : sets pitch bend range (1, 2, 7, 12 or 24 semitones) CALIBRATION:
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 This device runs normal calibration of antennas after pushing button for 3 seconds as per initial project
(Wave Form 1 low = CH1, WF 1 High = CH2, WF 2 Low = CH3, etc...)
MUTE BUTTON: SETTINGS:
Sends ALL NOTE OFF on selected channel and stay in mute until it's pushed again.
AUDIO: "Register" pot becomes "Selected Parameter" pot and have 8 positions.
Audio processing from antennas to output jack, including pots, LEDs and button functions, is exactly the same as in open theremin V3. "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)
### What can I do to get a theremin like glissando? Select a Parameter and move "Parameter's Value" to change corresponding setting.
Set pitch bend range of the theremin with a high value (12 semitones or 24 semitones). 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.
Set pitch bend range of the synth with the same value 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.
Closest to real theremin settings (pitch bend range = 24 semitones): Volume trigger = 127 (Maximum) won't generate any NOTE ON. It can be used to generate MIDI CC only.
1. Set pots like this: Volume = Min, Pitch = Max, Register = Max, Timbre = Midi channel. 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.
2. Push button for two seconds.
3. Then set pots as for audio (Example : Volume = Mid, Pitch = Mid, Register = Wanted octave, Timbre = any)
4. Play (you can mix synth and audio if you want) MUTE BUTTON:
Sends ALL NOTE OFF on selected channel and stay in mute until it is pushed again.
AUDIO:
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