Merge pull request #32 from MrDham/MrDham-remove-float-calc

Mr dham remove float calc
master 2.9
MrDham 3 months ago committed by GitHub
commit 95ed81836f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 154
      Open_Theremin_V3/application.cpp
  2. 151
      Open_Theremin_V3/application.h
  3. 8
      README.md

@ -9,7 +9,8 @@
#include "EEPROM.h" #include "EEPROM.h"
const AppMode AppModeValues[] = {MUTE,NORMAL}; 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 PitchFreqOffset = 700;
const int16_t VolumeFreqOffset = 700; const int16_t VolumeFreqOffset = 700;
const int8_t HYST_VAL = 40; const int8_t HYST_VAL = 40;
@ -42,8 +43,8 @@ static uint16_t old_midi_bend = 0;
static uint8_t midi_bend_low; static uint8_t midi_bend_low;
static uint8_t midi_bend_high; static uint8_t midi_bend_high;
static double double_log_freq = 0; static uint32_t long_log_note = 0;
static double midi_key_follow = 0.5; static uint32_t midi_key_follow = 2048; ;
// Configuration parameters // Configuration parameters
static uint8_t registerValue = 2; static uint8_t registerValue = 2;
@ -57,12 +58,12 @@ static uint8_t flag_pitch_bend_on = 1;
static uint8_t loop_midi_cc = 7; static uint8_t loop_midi_cc = 7;
static uint8_t rod_midi_cc = 255; static uint8_t rod_midi_cc = 255;
static uint8_t rod_midi_cc_lo = 255; static uint8_t rod_midi_cc_lo = 255;
static double rod_cc_scale = 1; static uint32_t rod_cc_scale = 128;
// tweakable paramameters // tweakable paramameters
#define VELOCITY_SENS 9 // How easy it is to reach highest velocity (127). Something betwen 5 and 12. #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 data_pot_value = 0;
static uint16_t old_data_pot_value = 0; static uint16_t old_data_pot_value = 0;
@ -399,7 +400,7 @@ pitchfn1 = GetPitchMeasurement();
l_iteration_pitch = 0; l_iteration_pitch = 0;
while ((abs(pitchfn0 - pitchfn1) > CalibrationTolerance) && (l_iteration_pitch < 12)) while ((abs(pitchfn0 - pitchfn1) > PitchCalibrationTolerance) && (l_iteration_pitch < 12))
{ {
SPImcpDAC2Asend(pitchXn0); SPImcpDAC2Asend(pitchXn0);
@ -469,7 +470,7 @@ volumefn1 = GetVolumeMeasurement();
l_iteration_volume = 0; l_iteration_volume = 0;
while ((abs(volumefn0 - volumefn1) > CalibrationTolerance) && (l_iteration_volume < 12)) while ((abs(volumefn0 - volumefn1) > VolumeCalibrationTolerance) && (l_iteration_volume < 12))
{ {
SPImcpDAC2Bsend(volumeXn0); SPImcpDAC2Bsend(volumeXn0);
@ -496,6 +497,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) { void Application::hzToAddVal(float hz) {
setWavetableSampleAdvance((uint16_t)(hz * HZ_ADDVAL_FACTOR)); setWavetableSampleAdvance((uint16_t)(hz * HZ_ADDVAL_FACTOR));
} }
@ -561,41 +632,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. // The bigger is synth's pitch bend range the beter is the effect.
void Application::midi_application () void Application::midi_application ()
{ {
double delta_loop_cc_val = 0; int16_t delta_loop_cc_val = 0;
double calculated_velocity = 0; int16_t calculated_velocity = 0;
// Calculate loop antena cc value for midi // Calculate loop antena cc value for midi
new_midi_loop_cc_val = loop_hand_pos >> 1; new_midi_loop_cc_val = loop_hand_pos >> 1;
new_midi_loop_cc_val = min (new_midi_loop_cc_val, 127); 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 // Calculate log freq
if ((vPointerIncrement < 18) || (vPointerIncrement > 65518)) if ((vPointerIncrement < 18) || (vPointerIncrement > 65518))
{ {
// Lowest note // Lowest note
double_log_freq = 0; long_log_note = 0;
} }
else if ((vPointerIncrement > 26315) && (vPointerIncrement < 39221)) else if ((vPointerIncrement > 26315) && (vPointerIncrement < 39221))
{ {
// Highest note // Highest note
double_log_freq = 127; long_log_note = 127;
} }
else if (vPointerIncrement < 32768) else if (vPointerIncrement < 32768)
{ {
// Positive frequencies // Positive frequencies
// 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 // 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 else
{ {
// Negative frequencies // Negative frequencies
// Find note in the playing range // 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 // 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 // State machine for MIDI
switch (_midistate) switch (_midistate)
@ -637,7 +709,7 @@ void Application::midi_application ()
if (new_midi_loop_cc_val > 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 // 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 and associated pitch bend
calculate_note_bend (); calculate_note_bend ();
@ -649,8 +721,8 @@ void Application::midi_application ()
// Calculate velocity // Calculate velocity
if (midi_timer != 0) 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); calculated_velocity = ((127 - midi_volume_trigger) >> 1 ) + (VELOCITY_SENS * midi_volume_trigger * delta_loop_cc_val / midi_timer);
midi_velocity = min (round (abs (calculated_velocity)), 127); midi_velocity = min (abs (calculated_velocity), 127);
} }
else else
{ {
@ -710,12 +782,12 @@ void Application::midi_application ()
if ( flag_legato_on == 1) if ( flag_legato_on == 1)
{ {
// Set key follow so as next played note will be at limit of pitch bend range // 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 else
{ {
// Set key follow to max so as no key follows // 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 // Calculate note and associated pitch bend
@ -771,16 +843,16 @@ void Application::midi_application ()
void Application::calculate_note_bend () void Application::calculate_note_bend ()
{ {
double double_log_bend; int32_t long_log_bend;
double double_norm_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 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 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
double_log_bend = double_log_freq - new_midi_note; // calculate bend to reach precise note played long_log_bend = ((int32_t) long_log_note) - (((int32_t) new_midi_note) * 4096); // calculate bend to reach precise note played
} }
else else
{ {
@ -791,16 +863,16 @@ void Application::calculate_note_bend ()
if (flag_pitch_bend_on == 1) if (flag_pitch_bend_on == 1)
{ {
// use it to reach precise note played // use it to reach precise note played
double_norm_log_bend = (double_log_bend / midi_bend_range); long_norm_log_bend = (long_log_bend / midi_bend_range);
if (double_norm_log_bend > 1) 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 else
{ {
@ -968,42 +1040,42 @@ void Application::set_parameters ()
case 0: case 0:
rod_midi_cc = 255; // Nothing rod_midi_cc = 255; // Nothing
rod_midi_cc_lo = 255; // Nothing rod_midi_cc_lo = 255; // Nothing
rod_cc_scale = 1.0; rod_cc_scale = 128;
break; break;
case 1: case 1:
rod_midi_cc = 8; // Balance rod_midi_cc = 8; // Balance
rod_midi_cc_lo = 255; // No least significant bits rod_midi_cc_lo = 255; // No least significant bits
rod_cc_scale = 1.0; rod_cc_scale = 128;
break; break;
case 2: case 2:
rod_midi_cc = 10; // Pan rod_midi_cc = 10; // Pan
rod_midi_cc_lo = 255; // No least significant bits rod_midi_cc_lo = 255; // No least significant bits
rod_cc_scale = 1.0; rod_cc_scale = 128;
break; break;
case 3: case 3:
rod_midi_cc = 16; // General Purpose 1 (14 Bits) rod_midi_cc = 16; // General Purpose 1 (14 Bits)
rod_midi_cc_lo = 48; // General Purpose 1 least significant bits rod_midi_cc_lo = 48; // General Purpose 1 least significant bits
rod_cc_scale = 1.0; rod_cc_scale = 128;
break; break;
case 4: case 4:
rod_midi_cc = 17; // General Purpose 2 (14 Bits) rod_midi_cc = 17; // General Purpose 2 (14 Bits)
rod_midi_cc_lo = 49; // General Purpose 2 least significant bits rod_midi_cc_lo = 49; // General Purpose 2 least significant bits
rod_cc_scale = 1.0; rod_cc_scale = 128;
break; break;
case 5: case 5:
rod_midi_cc = 18; // General Purpose 3 (7 Bits) rod_midi_cc = 18; // General Purpose 3 (7 Bits)
rod_midi_cc_lo = 255; // No least significant bits rod_midi_cc_lo = 255; // No least significant bits
rod_cc_scale = 1.0; rod_cc_scale = 128;
break; break;
case 6: case 6:
rod_midi_cc = 19; // General Purpose 4 (7 Bits) rod_midi_cc = 19; // General Purpose 4 (7 Bits)
rod_midi_cc_lo = 255; // No least significant bits rod_midi_cc_lo = 255; // No least significant bits
rod_cc_scale = 1.0; rod_cc_scale = 128;
break; break;
default: default:
rod_midi_cc = 74; // Cutoff (exists of both loop and rod) rod_midi_cc = 74; // Cutoff (exists of both loop and rod)
rod_midi_cc_lo = 255; // No least significant bits rod_midi_cc_lo = 255; // No least significant bits
rod_cc_scale = 1.0; rod_cc_scale = 128;
break; break;
} }
break; break;

@ -1,76 +1,77 @@
#ifndef _APPLICATION_H #ifndef _APPLICATION_H
#define _APPLICATION_H #define _APPLICATION_H
#include <avr/io.h> #include <avr/io.h>
#include "build.h" #include "build.h"
enum AppState {CALIBRATING = 0, PLAYING}; enum AppState {CALIBRATING = 0, PLAYING};
enum AppMode {MUTE = 0, NORMAL}; enum AppMode {MUTE = 0, NORMAL};
enum AppMidiState {MIDI_SILENT = 0, MIDI_PLAYING, MIDI_STOP, MIDI_MUTE}; enum AppMidiState {MIDI_SILENT = 0, MIDI_PLAYING, MIDI_STOP, MIDI_MUTE};
class Application { class Application {
public: public:
Application(); Application();
void setup(); void setup();
void loop(); void loop();
private: private:
static const uint16_t MAX_VOLUME = 4095; static const uint16_t MAX_VOLUME = 4095;
static const uint32_t TRIM_PITCH_FACTOR = 33554432; static const uint32_t TRIM_PITCH_FACTOR = 33554432;
static const uint32_t FREQ_FACTOR = 1600000000; static const uint32_t FREQ_FACTOR = 1600000000;
static const int16_t BUTTON_PIN = 6; static const int16_t BUTTON_PIN = 6;
static const int16_t LED_PIN_1 = 18; static const int16_t LED_PIN_1 = 18;
static const int16_t LED_PIN_2 = 19; static const int16_t LED_PIN_2 = 19;
static const int16_t PITCH_POT = 0; static const int16_t PITCH_POT = 0;
static const int16_t VOLUME_POT = 1; static const int16_t VOLUME_POT = 1;
static const int16_t WAVE_SELECT_POT = 2; static const int16_t WAVE_SELECT_POT = 2;
static const int16_t REGISTER_SELECT_POT = 3; static const int16_t REGISTER_SELECT_POT = 3;
AppState _state; AppState _state;
AppMode _mode; AppMode _mode;
AppMidiState _midistate; AppMidiState _midistate;
void calibrate(); void calibrate();
void calibrate_pitch(); void calibrate_pitch();
void calibrate_volume(); void calibrate_volume();
AppMode nextMode(); AppMode nextMode();
void initialiseTimer(); void initialiseTimer();
void initialiseInterrupts(); void initialiseInterrupts();
void InitialisePitchMeasurement(); void InitialisePitchMeasurement();
void InitialiseVolumeMeasurement(); void InitialiseVolumeMeasurement();
unsigned long GetPitchMeasurement(); unsigned long GetPitchMeasurement();
unsigned long GetVolumeMeasurement(); unsigned long GetVolumeMeasurement();
unsigned long GetQMeasurement(); unsigned long GetQMeasurement();
const float HZ_ADDVAL_FACTOR = 2.09785; const float HZ_ADDVAL_FACTOR = 2.09785;
const float MIDDLE_C = 261.6; const float MIDDLE_C = 261.6;
void playNote(float hz, uint16_t milliseconds, uint8_t volume); void playNote(float hz, uint16_t milliseconds, uint8_t volume);
void hzToAddVal(float hz); uint16_t log2U16 (uint16_t lin_input);
void playStartupSound(); void hzToAddVal(float hz);
void playCalibratingCountdownSound(); void playStartupSound();
void playModeSettingSound(); void playCalibratingCountdownSound();
void delay_NOP(unsigned long time); 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_setup();
void midi_application (); void midi_msg_send(uint8_t channel, uint8_t midi_cmd1, uint8_t midi_cmd2, uint8_t midi_value);
void calculate_note_bend (); void midi_application ();
void init_parameters (); void calculate_note_bend ();
void set_parameters (); void init_parameters ();
void set_parameters ();
};
};
#endif // _APPLICATION_H #endif // _APPLICATION_H

@ -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) (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 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
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): Changes added in Open.Theremin version 3.1 (all by @Theremingenieur):
Fix a wavetable addressing issue (found by @miguelfreitas) 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: 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: 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 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 ? ### NEED SUPPORT ?
Please log bugs, requests and questions at https://github.com/MrDham/OpenTheremin_V3_with_MIDI/issues Please log bugs, requests and questions at https://github.com/MrDham/OpenTheremin_V3_with_MIDI/issues

Loading…
Cancel
Save