generate MIDI pitch lookup table

This commit is contained in:
Peter Cai 2021-06-10 13:26:08 +08:00
parent 636f18d70d
commit adb9d5444b
6 changed files with 60 additions and 17 deletions

3
.gitignore vendored
View File

@ -1,2 +1,3 @@
.vscode .vscode
out out
pitch_table.h

View File

@ -88,7 +88,7 @@ endef
mkdir -p .vscode mkdir -p .vscode
sed 's@__includePaths__@$(call to-json-array, ${AVR_INC} ${VPATH} ${VARIANTS})@' c_cpp_properties.json.template > .vscode/c_cpp_properties.json 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 all: ${BUILD_DIR}/${PROGRAM}.hex
@ -103,4 +103,7 @@ endif
vscode: vscode:
rm -rf .vscode/c_cpp_properties.json rm -rf .vscode/c_cpp_properties.json
make .vscode/c_cpp_properties.json make .vscode/c_cpp_properties.json
pitch_table:
python gen_pitch_table.py > pitch_table.h

37
gen_pitch_table.py Normal file
View File

@ -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("};")

View File

@ -33,25 +33,15 @@ int main() {
motors[i].Init(); motors[i].Init();
} }
// Test: play A4 (440 Hz) on each successive motor every second // Test: play a C chord
int cur_motor = -1; motors[0].TickAtPitch(60); // C4
motors[1].TickAtPitch(64); // E4
motors[2].TickAtPitch(67); // G4
while (true) { while (true) {
unsigned long cur_micros = micros(); unsigned long cur_micros = micros();
handle_tick(cur_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; return 0;

View File

@ -1,5 +1,6 @@
#include <Arduino.h> #include <Arduino.h>
#include "motor_control.h" #include "motor_control.h"
#include "pitch_table.h"
MotorControl::MotorControl(int pin_dir, int pin_step) : MotorControl::MotorControl(int pin_dir, int pin_step) :
pin_dir(pin_dir), pin_step(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; 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() { void MotorControl::TickOff() {
tick_period_micros = 0; tick_period_micros = 0;
} }

View File

@ -17,6 +17,8 @@ class MotorControl {
void Init(); void Init();
// Set the motor to tick at a given interval (inverse of frequency) // Set the motor to tick at a given interval (inverse of frequency)
void TickOn(unsigned long period_micros); 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 // Turn off the motor
void TickOff(); void TickOff();
// Perform a tick if necessary // Perform a tick if necessary