diff --git a/gen_pitch_table.py b/gen_pitch_table.py index d7698aa..208fe98 100644 --- a/gen_pitch_table.py +++ b/gen_pitch_table.py @@ -52,4 +52,16 @@ for i in range(start_pitch, end_pitch): period = int(period / 2) print(" " + str(period) + "ul,\t// " + name) +print("};") + +print("") +print("// scale factors for bend values from 0 to 255") +print("// We don't support the full 16385 levels of MIDI bend") +print("// MIDI bend values have to be converted first to the nearest supported one") +print("constexpr float midi_pitch_bend_scale[256] = {") + +for i in range(0, 256): + factor = pow(2, (8192 - i * 64) / 49152) + print(" " + str(factor) + "f,\t// " + str(i)) + print("};") \ No newline at end of file diff --git a/main.cpp b/main.cpp index 29cc2fe..e19f793 100644 --- a/main.cpp +++ b/main.cpp @@ -53,6 +53,11 @@ void midi_note_off(uint8_t channel, uint8_t note, uint8_t velocity) { motors[channel - 1].TickOff(); } +void midi_pitch_bend(uint8_t channel, int bend) { + ASSERT_CHANNEL(); + motors[channel - 1].TickPitchBend(bend); +} + int main() { // Arduino library initialization // Needed for some functions to work (like micros) @@ -76,6 +81,7 @@ int main() { // Set up MIDI callbacks MIDI.setHandleNoteOn(midi_note_on); MIDI.setHandleNoteOff(midi_note_off); + MIDI.setHandlePitchBend(midi_pitch_bend); unsigned long cur_micros; unsigned long cur_half_micros; diff --git a/motor_control.cpp b/motor_control.cpp index 9b90d0b..24acfdd 100644 --- a/motor_control.cpp +++ b/motor_control.cpp @@ -4,7 +4,8 @@ MotorControl::MotorControl(int pin_dir, int pin_step) : pin_dir(pin_dir), pin_step(pin_step), - last_tick_half_micros(0), tick_period_half_micros(0) + last_tick_half_micros(0), tick_period_half_micros(0), + tick_period_orig_half_micros(0) { // No actual constructor logic -- initialization is in Init() } @@ -19,6 +20,7 @@ void MotorControl::Init() { void MotorControl::TickOn(unsigned long period_half_micros) { tick_period_half_micros = period_half_micros; + tick_period_orig_half_micros = period_half_micros; // Force the next tick to happen last_tick_half_micros = 0; } @@ -32,8 +34,19 @@ void MotorControl::TickAtPitch(unsigned int midi_pitch) { TickOn(midi_pitch_period[midi_pitch - midi_pitch_offset]); } +void MotorControl::TickPitchBend(int bend) { + if (bend < 0 || bend >= 16384) return; + if (tick_period_orig_half_micros == 0) return; + + // Scale the MIDI bend value down to 0 - 255 + bend = bend / 64; + + tick_period_half_micros = (unsigned long) (((float) tick_period_orig_half_micros) * midi_pitch_bend_scale[bend]); +} + void MotorControl::TickOff() { tick_period_half_micros = 0; + tick_period_orig_half_micros = 0; } void MotorControl::Tick(unsigned long cur_half_micros) { diff --git a/motor_control.h b/motor_control.h index f6826d0..9f455e0 100644 --- a/motor_control.h +++ b/motor_control.h @@ -8,6 +8,8 @@ class MotorControl { unsigned long last_tick_half_micros; // Interval of motor ticking; 0 to disable the motor unsigned long tick_period_half_micros; + // Original period of motor ticking (without applying bend) + unsigned long tick_period_orig_half_micros; void DoTick(); public: @@ -19,6 +21,8 @@ class MotorControl { void TickOn(unsigned long period_half_micros); // Set the motor to tick with a given MIDI pitch void TickAtPitch(unsigned int midi_pitch); + // Set the pitch bend + void TickPitchBend(int bend); // Turn off the motor void TickOff(); // Perform a tick if necessary