Browse Source

generate MIDI pitch lookup table

master
Peter Cai 1 year ago
parent
commit
adb9d5444b
  1. 3
      .gitignore
  2. 7
      Makefile
  3. 37
      gen_pitch_table.py
  4. 18
      main.cpp
  5. 10
      motor_control.cpp
  6. 2
      motor_control.h

3
.gitignore vendored

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

7
Makefile

@ -88,7 +88,7 @@ endef
mkdir -p .vscode
sed '[email protected][email protected]$(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
make .vscode/c_cpp_properties.json
pitch_table:
python gen_pitch_table.py > pitch_table.h

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

18
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;

10
motor_control.cpp

@ -1,5 +1,6 @@
#include <Arduino.h>
#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;
}

2
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

Loading…
Cancel
Save