From adb9d5444bfaa6507d9d35c94019b195a0593f54 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Thu, 10 Jun 2021 13:26:08 +0800 Subject: [PATCH] generate MIDI pitch lookup table --- .gitignore | 3 ++- Makefile | 7 +++++-- gen_pitch_table.py | 37 +++++++++++++++++++++++++++++++++++++ main.cpp | 18 ++++-------------- motor_control.cpp | 10 ++++++++++ motor_control.h | 2 ++ 6 files changed, 60 insertions(+), 17 deletions(-) create mode 100644 gen_pitch_table.py diff --git a/.gitignore b/.gitignore index eef5389..4565041 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .vscode -out \ No newline at end of file +out +pitch_table.h \ No newline at end of file diff --git a/Makefile b/Makefile index cde4b4a..c800c91 100644 --- a/Makefile +++ b/Makefile @@ -88,7 +88,7 @@ endef mkdir -p .vscode sed 's@__includePaths__@$(call to-json-array, ${AVR_INC} ${VPATH} ${VARIANTS})@' c_cpp_properties.json.template > .vscode/c_cpp_properties.json -.PHONY: all clean upload vscode +.PHONY: all clean upload vscode pitch_table all: ${BUILD_DIR}/${PROGRAM}.hex @@ -103,4 +103,7 @@ endif vscode: rm -rf .vscode/c_cpp_properties.json - make .vscode/c_cpp_properties.json \ No newline at end of file + make .vscode/c_cpp_properties.json + +pitch_table: + python gen_pitch_table.py > pitch_table.h \ No newline at end of file diff --git a/gen_pitch_table.py b/gen_pitch_table.py new file mode 100644 index 0000000..57350b3 --- /dev/null +++ b/gen_pitch_table.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 + +start_pitch = 36 # C2 +end_pitch = 84 # C6 + +pitch_A0 = 21 +pitch_C1 = 24 +pitch_A4 = 69 +freq_A4 = 440 + +def pitch_to_freq(pitch): + dist_A4 = pitch - pitch_A4 + factor = pow(2, abs(dist_A4) / 12) + + if dist_A4 > 0: + return freq_A4 * factor + else: + return freq_A4 / factor + +def pitch_to_period_micros(pitch): + return 1 / pitch_to_freq(pitch) * 1000 * 1000 + +pitch_names = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"] +def pitch_to_name(pitch): + return pitch_names[(pitch - pitch_A0) % 12] + str(int((pitch - pitch_C1) / 12) + 1) + +print("#pragma once") +print("") +print("constexpr unsigned int midi_pitch_offset = " + str(start_pitch) + ";") +print("constexpr unsigned int midi_pitch_max = " + str(end_pitch) + ";") +print("constexpr unsigned long midi_pitch_period[" + str(end_pitch - start_pitch) + "] = {") + +for i in range(start_pitch, end_pitch): + period = int(pitch_to_period_micros(i)) + print(" " + str(period) + ",\t// " + pitch_to_name(i)) + +print("};") \ No newline at end of file diff --git a/main.cpp b/main.cpp index 33be622..417a489 100644 --- a/main.cpp +++ b/main.cpp @@ -33,25 +33,15 @@ int main() { motors[i].Init(); } - // Test: play A4 (440 Hz) on each successive motor every second - int cur_motor = -1; + // Test: play a C chord + motors[0].TickAtPitch(60); // C4 + motors[1].TickAtPitch(64); // E4 + motors[2].TickAtPitch(67); // G4 while (true) { unsigned long cur_micros = micros(); handle_tick(cur_micros); - - int new_motor = (cur_micros / 1000 / 1000) % 4; - - if (new_motor != cur_motor) { - motors[new_motor].TickOn(2272); - - if (cur_motor != -1) { - motors[cur_motor].TickOff(); - } - - cur_motor = new_motor; - } } return 0; diff --git a/motor_control.cpp b/motor_control.cpp index dd1313d..ac1a339 100644 --- a/motor_control.cpp +++ b/motor_control.cpp @@ -1,5 +1,6 @@ #include #include "motor_control.h" +#include "pitch_table.h" MotorControl::MotorControl(int pin_dir, int pin_step) : pin_dir(pin_dir), pin_step(pin_step), @@ -22,6 +23,15 @@ void MotorControl::TickOn(unsigned long period_micros) { last_tick_micros = 0; } +void MotorControl::TickAtPitch(unsigned int midi_pitch) { + // Bounds check + if (midi_pitch < midi_pitch_offset || midi_pitch >= midi_pitch_max) { + return; + } + + TickOn(midi_pitch_period[midi_pitch - midi_pitch_offset]); +} + void MotorControl::TickOff() { tick_period_micros = 0; } diff --git a/motor_control.h b/motor_control.h index 75ae93a..70b6964 100644 --- a/motor_control.h +++ b/motor_control.h @@ -17,6 +17,8 @@ class MotorControl { void Init(); // Set the motor to tick at a given interval (inverse of frequency) void TickOn(unsigned long period_micros); + // Set the motor to tick with a given MIDI pitch + void TickAtPitch(unsigned int midi_pitch); // Turn off the motor void TickOff(); // Perform a tick if necessary