60 lines
2.3 KiB
Python
Executable file
60 lines
2.3 KiB
Python
Executable file
#!/usr/bin/env python
|
|
import argparse
|
|
import csv
|
|
import sys
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("input", nargs="+", help="input CSV (pitch, duration, pause)")
|
|
parser.add_argument("-o", "--output", default="/dev/stdout", help="output file (gcode)")
|
|
parser.add_argument(
|
|
"--steps-per-mm",
|
|
default=5,
|
|
type=float,
|
|
help="motor steps per mm (actually an arbitrary scaling factor)",
|
|
)
|
|
parser.add_argument("--bpm", default=60, type=float, help="beats per minute")
|
|
args = parser.parse_args()
|
|
|
|
# starting from C3 = 130.81 Hz
|
|
pitches = ["c", "c#", "d", "d#", "e", "f", "f#", "g", "g#", "a", "a#", "b"]
|
|
frequencies = {
|
|
f"{pitches[i % 12]}{i // 12 + 3}": 2 ** (i / 12) * 130.81 for i in range(36)
|
|
}
|
|
# duration of 1/16th beat in seconds
|
|
base_duration = 60 / args.bpm / 16
|
|
|
|
with open(args.output, "w") as f:
|
|
f.write("G21 ; set units to millimeters\n")
|
|
f.write("G90 ; use absolute coordinates\n")
|
|
f.write("G0 Z10 F300\n")
|
|
f.write("G0 X0 Y0 F3000\n")
|
|
f.write("G4 S2 ; wait 2 seconds\n")
|
|
position = 0
|
|
for filename in args.input:
|
|
with open(filename, "r") as notes:
|
|
for pitch, duration, pause in csv.reader(notes):
|
|
if pitch == "":
|
|
# skip comment
|
|
continue
|
|
try:
|
|
frequency = frequencies[pitch]
|
|
except KeyError:
|
|
sys.exit("pitch goes from c3 to b5")
|
|
duration = int(duration)
|
|
pause = int(pause)
|
|
if duration < 1 or pause < -1:
|
|
sys.exit("duration/pause are integer multiples of 1/16th beat")
|
|
# feedrate in mm per minute
|
|
feedrate = int(frequency * 60 / args.steps_per_mm)
|
|
# length in mm
|
|
length = duration * base_duration / 60 * feedrate
|
|
position += -length if position > 100 else length
|
|
if position < 0 or position > 200:
|
|
sys.exit("does not fit in 200x200")
|
|
f.write(f"; {pitch},{duration},{pause}\n")
|
|
f.write(f"G0 X{position:.6f} Y{position:.6f} F{feedrate}\n")
|
|
if pause != -1:
|
|
pause_ms = int(pause * base_duration * 1000)
|
|
f.write(f"G4 P{pause_ms}\n")
|
|
f.write("G4 S2 ; wait 2 seconds\n")
|