From af0b16f051213973a74b36b0de1a00096e00f019 Mon Sep 17 00:00:00 2001 From: MrDham Date: Sun, 7 Jul 2024 16:06:01 +0200 Subject: [PATCH 1/4] Update application.cpp --- Open_Theremin_V3/application.cpp | 147 +++++++++++++++++++++++-------- 1 file changed, 109 insertions(+), 38 deletions(-) diff --git a/Open_Theremin_V3/application.cpp b/Open_Theremin_V3/application.cpp index 73d6108..0a70930 100644 --- a/Open_Theremin_V3/application.cpp +++ b/Open_Theremin_V3/application.cpp @@ -42,8 +42,8 @@ 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 uint32_t long_log_note = 0; +static uint32_t midi_key_follow = 2048; ; // Configuration parameters static uint8_t registerValue = 2; @@ -57,12 +57,12 @@ static uint8_t flag_pitch_bend_on = 1; static uint8_t loop_midi_cc = 7; static uint8_t rod_midi_cc = 255; static uint8_t rod_midi_cc_lo = 255; -static double rod_cc_scale = 1; +static uint32_t rod_cc_scale = 128; // 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) +#define PLAYER_ACCURACY 819 // between 0 (very accurate players) and 2048 (not accurate at all) static uint16_t data_pot_value = 0; static uint16_t old_data_pot_value = 0; @@ -496,6 +496,76 @@ EEPROM.put(2,volumeXn0); } +// calculate log2 of an unsigned from 1 to 65535 into a 4.12 fixed point unsigned +// To avoid use of log (double) function +uint16_t Application::log2U16 (uint16_t lin_input) +{ + uint32_t long_lin; // To turn input into a 16.16 fixed point + //unsigned long bit_pos; + uint32_t log_output; // 4.12 fixed point log calculation + + int32_t long_x1; + int32_t long_x2; + int32_t long_x3; + + const int32_t POLY_A0 = 37; + const int32_t POLY_A1 = 46390; + const int32_t POLY_A2 = -18778; + const int32_t POLY_A3 = 5155; + + + if (lin_input != 0) + { + long_lin = (uint32_t) (lin_input) << 16; + log_output = 0; + + // Calculate integer part of log2 and reduce long_lin into 16.16 between 1 and 2 + if (long_lin >= 16777216) // 2^(8 + 16) + { + log_output += 8 << 12; + long_lin = long_lin >> 8; + } + + if (long_lin >= 1048576) // 2^(4 + 16) + { + log_output += 4 << 12; + long_lin = long_lin >> 4; + } + + if (long_lin >= 262144) // 2^(2 + 16) + { + log_output += 2 << 12; + long_lin = long_lin >> 2; + } + + if (long_lin >= 131072) // 2^(1 + 16) + { + log_output += 1 << 12; + long_lin = long_lin >> 1; + } + + // long_lin is between 1 and 2 now (16.16 fixed point) + // Calculate 3rd degree polynomial approximation log(x)=Polynomial(x-1) in signed long s15.16 and reduce to unsigned 4.12 at the very end. + + long_lin = long_lin >> 1; // reduce to 17.15 bit to support signed operations here after + + long_x1 = long_lin-(32768); //(x-1) we have the decimal part in s15 now + long_x2 = (long_x1 * long_x1) >> 15 ; // (x-1)^2 + long_x3 = (long_x2 * long_x1) >> 15 ; // (x-1)^3 + + log_output += ( (POLY_A0) + + ((POLY_A1 * long_x1) >> 15) + + ((POLY_A2 * long_x2) >> 15) + + ((POLY_A3 * long_x3) >> 15) ) >> 3; + } + else + { + log_output=0; + } + + return log_output; +} + void Application::hzToAddVal(float hz) { setWavetableSampleAdvance((uint16_t)(hz * HZ_ADDVAL_FACTOR)); } @@ -561,41 +631,42 @@ void Application::midi_msg_send(uint8_t channel, uint8_t midi_cmd1, uint8_t midi // The bigger is synth's pitch bend range the beter is the effect. void Application::midi_application () { - double delta_loop_cc_val = 0; - double calculated_velocity = 0; - + int16_t delta_loop_cc_val = 0; + int16_t calculated_velocity = 0; // Calculate loop antena cc value for midi new_midi_loop_cc_val = loop_hand_pos >> 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; + delta_loop_cc_val = (int16_t)new_midi_loop_cc_val - (int16_t)old_midi_loop_cc_val; // Calculate log freq if ((vPointerIncrement < 18) || (vPointerIncrement > 65518)) { // Lowest note - double_log_freq = 0; + long_log_note = 0; } else if ((vPointerIncrement > 26315) && (vPointerIncrement < 39221)) { // Highest note - double_log_freq = 127; + long_log_note = 127; } else if (vPointerIncrement < 32768) { // Positive frequencies // Find note in the playing range - double_log_freq = (log (vPointerIncrement/17.152) / 0.057762265); // Precise note played in the logaritmic scale + // 16795 = log2U16 (C0 [8.1758] * HZ_ADDVAL_FACTOR [2.09785]) + long_log_note = 12 * ((uint32_t) log2U16(vPointerIncrement) - 16795); // Precise note played in the logaritmic scale } else { // Negative frequencies // Find note in the playing range - double_log_freq = (log ((65535-vPointerIncrement+1)/17.152) / 0.057762265); // Precise note played in the logaritmic scale + // 16795 = log2U16 (C0 [8.1758] * HZ_ADDVAL_FACTOR [2.09785]) + long_log_note = 12 * ((uint32_t) log2U16(65535-vPointerIncrement+1) - 16795); // Precise note played in the logaritmic scale } // Calculate rod antena cc value for midi - new_midi_rod_cc_val = round (min(((double_log_freq * 128) * rod_cc_scale), 16383)); // 14 bit value ! + new_midi_rod_cc_val = (uint16_t) min((long_log_note * rod_cc_scale) >> 12, 16383); // 14 bit value // State machine for MIDI switch (_midistate) @@ -637,7 +708,7 @@ void Application::midi_application () 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; + midi_key_follow = 2048; // Calculate note and associated pitch bend calculate_note_bend (); @@ -649,8 +720,8 @@ void Application::midi_application () // 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); + calculated_velocity = ((127 - midi_volume_trigger) >> 1 ) + (VELOCITY_SENS * midi_volume_trigger * delta_loop_cc_val / midi_timer); + midi_velocity = min (abs (calculated_velocity), 127); } else { @@ -710,12 +781,12 @@ void Application::midi_application () 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; + midi_key_follow = ((uint32_t) midi_bend_range * 4096) - PLAYER_ACCURACY; } else { // Set key follow to max so as no key follows - midi_key_follow = 127; + midi_key_follow = 520192; // 127*2^12 } // Calculate note and associated pitch bend @@ -771,16 +842,16 @@ void Application::midi_application () void Application::calculate_note_bend () { - double double_log_bend; - double double_norm_log_bend; + int32_t long_log_bend; + int32_t long_norm_log_bend; - double_log_bend = double_log_freq - old_midi_note; // How far from last played midi chromatic note we are + long_log_bend = ((int32_t)long_log_note) - (((int32_t) old_midi_note) * 4096); // 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)) + if ((abs (long_log_bend) >= midi_key_follow) && (midi_key_follow != 520192)) { - 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 + new_midi_note = (uint8_t) ((long_log_note + 2048) >> 12); // Select the new midi chromatic note - round to integer part by adding 1/2 before shifting + long_log_bend = ((int32_t) long_log_note) - (((int32_t) new_midi_note) * 4096); // calculate bend to reach precise note played } else { @@ -791,16 +862,16 @@ void Application::calculate_note_bend () 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) + long_norm_log_bend = (long_log_bend / midi_bend_range); + if (long_norm_log_bend > 4096) { - double_norm_log_bend = 1; + long_norm_log_bend = 4096; } - else if (double_norm_log_bend < -1) + else if (long_norm_log_bend < -4096) { - double_norm_log_bend = -1; + long_norm_log_bend = -4096; } - new_midi_bend = 8192 + (8191 * double_norm_log_bend); // Calculate midi pitch bend + new_midi_bend = 8192 + ((long_norm_log_bend * 8191) >> 12); // Calculate midi pitch bend } else { @@ -968,42 +1039,42 @@ void Application::set_parameters () case 0: rod_midi_cc = 255; // Nothing rod_midi_cc_lo = 255; // Nothing - rod_cc_scale = 1.0; + rod_cc_scale = 128; break; case 1: rod_midi_cc = 8; // Balance rod_midi_cc_lo = 255; // No least significant bits - rod_cc_scale = 1.0; + rod_cc_scale = 128; break; case 2: rod_midi_cc = 10; // Pan rod_midi_cc_lo = 255; // No least significant bits - rod_cc_scale = 1.0; + rod_cc_scale = 128; break; case 3: rod_midi_cc = 16; // General Purpose 1 (14 Bits) rod_midi_cc_lo = 48; // General Purpose 1 least significant bits - rod_cc_scale = 1.0; + rod_cc_scale = 128; break; case 4: rod_midi_cc = 17; // General Purpose 2 (14 Bits) rod_midi_cc_lo = 49; // General Purpose 2 least significant bits - rod_cc_scale = 1.0; + rod_cc_scale = 128; break; case 5: rod_midi_cc = 18; // General Purpose 3 (7 Bits) rod_midi_cc_lo = 255; // No least significant bits - rod_cc_scale = 1.0; + rod_cc_scale = 128; break; case 6: rod_midi_cc = 19; // General Purpose 4 (7 Bits) rod_midi_cc_lo = 255; // No least significant bits - rod_cc_scale = 1.0; + rod_cc_scale = 128; break; default: rod_midi_cc = 74; // Cutoff (exists of both loop and rod) rod_midi_cc_lo = 255; // No least significant bits - rod_cc_scale = 1.0; + rod_cc_scale = 128; break; } break; From 557dd326fb7c5af7e32d7c15e795346c90a2416b Mon Sep 17 00:00:00 2001 From: MrDham Date: Sun, 7 Jul 2024 16:14:41 +0200 Subject: [PATCH 2/4] Update application.h --- Open_Theremin_V3/application.h | 151 +++++++++++++++++---------------- 1 file changed, 76 insertions(+), 75 deletions(-) diff --git a/Open_Theremin_V3/application.h b/Open_Theremin_V3/application.h index 23cae07..481cc21 100644 --- a/Open_Theremin_V3/application.h +++ b/Open_Theremin_V3/application.h @@ -1,76 +1,77 @@ -#ifndef _APPLICATION_H -#define _APPLICATION_H - -#include - -#include "build.h" - -enum AppState {CALIBRATING = 0, PLAYING}; -enum AppMode {MUTE = 0, NORMAL}; -enum AppMidiState {MIDI_SILENT = 0, MIDI_PLAYING, MIDI_STOP, MIDI_MUTE}; - -class Application { - public: - Application(); - - void setup(); - void loop(); - - private: - static const uint16_t MAX_VOLUME = 4095; - static const uint32_t TRIM_PITCH_FACTOR = 33554432; - static const uint32_t FREQ_FACTOR = 1600000000; - - static const int16_t BUTTON_PIN = 6; - static const int16_t LED_PIN_1 = 18; - static const int16_t LED_PIN_2 = 19; - - - static const int16_t PITCH_POT = 0; - static const int16_t VOLUME_POT = 1; - static const int16_t WAVE_SELECT_POT = 2; - static const int16_t REGISTER_SELECT_POT = 3; - - - - - AppState _state; - AppMode _mode; - AppMidiState _midistate; - - void calibrate(); - void calibrate_pitch(); - void calibrate_volume(); - - - AppMode nextMode(); - - void initialiseTimer(); - void initialiseInterrupts(); - void InitialisePitchMeasurement(); - void InitialiseVolumeMeasurement(); - unsigned long GetPitchMeasurement(); - unsigned long GetVolumeMeasurement(); - unsigned long GetQMeasurement(); - - - const float HZ_ADDVAL_FACTOR = 2.09785; - const float MIDDLE_C = 261.6; - - void playNote(float hz, uint16_t milliseconds, uint8_t volume); - void hzToAddVal(float hz); - void playStartupSound(); - void playCalibratingCountdownSound(); - void playModeSettingSound(); - void delay_NOP(unsigned long time); - - 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 calculate_note_bend (); - void init_parameters (); - void set_parameters (); - -}; - +#ifndef _APPLICATION_H +#define _APPLICATION_H + +#include + +#include "build.h" + +enum AppState {CALIBRATING = 0, PLAYING}; +enum AppMode {MUTE = 0, NORMAL}; +enum AppMidiState {MIDI_SILENT = 0, MIDI_PLAYING, MIDI_STOP, MIDI_MUTE}; + +class Application { + public: + Application(); + + void setup(); + void loop(); + + private: + static const uint16_t MAX_VOLUME = 4095; + static const uint32_t TRIM_PITCH_FACTOR = 33554432; + static const uint32_t FREQ_FACTOR = 1600000000; + + static const int16_t BUTTON_PIN = 6; + static const int16_t LED_PIN_1 = 18; + static const int16_t LED_PIN_2 = 19; + + + static const int16_t PITCH_POT = 0; + static const int16_t VOLUME_POT = 1; + static const int16_t WAVE_SELECT_POT = 2; + static const int16_t REGISTER_SELECT_POT = 3; + + + + + AppState _state; + AppMode _mode; + AppMidiState _midistate; + + void calibrate(); + void calibrate_pitch(); + void calibrate_volume(); + + + AppMode nextMode(); + + void initialiseTimer(); + void initialiseInterrupts(); + void InitialisePitchMeasurement(); + void InitialiseVolumeMeasurement(); + unsigned long GetPitchMeasurement(); + unsigned long GetVolumeMeasurement(); + unsigned long GetQMeasurement(); + + + const float HZ_ADDVAL_FACTOR = 2.09785; + const float MIDDLE_C = 261.6; + + void playNote(float hz, uint16_t milliseconds, uint8_t volume); + uint16_t log2U16 (uint16_t lin_input); + void hzToAddVal(float hz); + void playStartupSound(); + void playCalibratingCountdownSound(); + void playModeSettingSound(); + void delay_NOP(unsigned long time); + + 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 calculate_note_bend (); + void init_parameters (); + void set_parameters (); + +}; + #endif // _APPLICATION_H From 9f8e5daa111a0a80026226d53aaf5c13df6d7026 Mon Sep 17 00:00:00 2001 From: MrDham Date: Sun, 7 Jul 2024 19:32:54 +0200 Subject: [PATCH 3/4] Update README.md --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index ddf23c7..5d75253 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -## Open Theremin V3 with MIDI interface control software V2.8 for Arduino UNO +## Open Theremin V3 with MIDI interface control software V2.9 for Arduino UNO (For Open Theremin V4 with MIDI, follow this link: https://github.com/MrDham/OpenTheremin_V4_with_MIDI) @@ -7,7 +7,7 @@ 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 -This Open Theremin V3 with MIDI version V2.6 also takes into account +This Open Theremin V3 with MIDI, since version V2.6, also takes into account Changes added in Open.Theremin version 3.1 (all by @Theremingenieur): Fix a wavetable addressing issue (found by @miguelfreitas) @@ -65,7 +65,7 @@ The MIDI open theremin generates NOTE ON/OFF messages and Continuous Controler MIDI CC: -It is possible to affect independant MIDI CCs to the PITCH ANTENNA (ROD) and to the VOLUME ANTENNA (LOOP). +It is possible to assign independant MIDI CCs to the PITCH ANTENNA (ROD) and to the VOLUME ANTENNA (LOOP). NOTE ON/OFF: @@ -165,7 +165,7 @@ Changing this to your taste may require some test and trial. "#define VELOCITY_SENS 9" -> How easy it is to reach highest velocity (127). Something betwen 5 and 12. -"#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). +"#define PLAYER_ACCURACY 819" -> Pitch accuracy of player. Tolerance on note center for changing notes when playing legato. From 0 (very accurate players) to 2048 (may generate note toggling). ### NEED SUPPORT ? Please log bugs, requests and questions at https://github.com/MrDham/OpenTheremin_V3_with_MIDI/issues From d534197b9f2334cb1e885137e25af797ef7f00b8 Mon Sep 17 00:00:00 2001 From: MrDham Date: Thu, 25 Jul 2024 20:51:23 +0200 Subject: [PATCH 4/4] Update application.cpp --- Open_Theremin_V3/application.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Open_Theremin_V3/application.cpp b/Open_Theremin_V3/application.cpp index 0a70930..87afd2b 100644 --- a/Open_Theremin_V3/application.cpp +++ b/Open_Theremin_V3/application.cpp @@ -9,7 +9,8 @@ #include "EEPROM.h" const AppMode AppModeValues[] = {MUTE,NORMAL}; -const int16_t CalibrationTolerance = 15; +const int16_t PitchCalibrationTolerance = 15; +const int16_t VolumeCalibrationTolerance = 21; const int16_t PitchFreqOffset = 700; const int16_t VolumeFreqOffset = 700; const int8_t HYST_VAL = 40; @@ -399,7 +400,7 @@ pitchfn1 = GetPitchMeasurement(); l_iteration_pitch = 0; -while ((abs(pitchfn0 - pitchfn1) > CalibrationTolerance) && (l_iteration_pitch < 12)) +while ((abs(pitchfn0 - pitchfn1) > PitchCalibrationTolerance) && (l_iteration_pitch < 12)) { SPImcpDAC2Asend(pitchXn0); @@ -469,7 +470,7 @@ volumefn1 = GetVolumeMeasurement(); l_iteration_volume = 0; -while ((abs(volumefn0 - volumefn1) > CalibrationTolerance) && (l_iteration_volume < 12)) +while ((abs(volumefn0 - volumefn1) > VolumeCalibrationTolerance) && (l_iteration_volume < 12)) { SPImcpDAC2Bsend(volumeXn0);