Files
2023-06-05 15:53:44 +02:00

120 lines
3.0 KiB
Python

from __future__ import print_function
import logging
import sys
import time
from string import Template
from random import randrange, choice
import subprocess
import tkinter as tk
from PIL import Image, ImageTk
from rtmidi.midiutil import open_midiinput
log = logging.getLogger('midiin_callback')
logging.basicConfig(level=logging.INFO)
notes_sharp = ["c", "cis", "d", "dis", "e", "f", "fis", "g", "gis", "a", "ais", "b"]
lilypond = Template(
'''\\version "2.24.1"
\paper {
#(set-paper-size "a10landscape")
indent = 0\mm
line-width = 110\mm
oddHeaderMarkup = ""
evenHeaderMarkup = ""
oddFooterMarkup = ""
evenFooterMarkup = ""
}
\\relative {
\clef ${CLEF}
${NOTE}
}
''')
window = tk.Tk()
class Trainer():
def __init__(self):
self.secret = 0
self.lb = 36 # middle C is 60
self.ub = 84 # 108
self.image = None
self.image_label = tk.Label(window)
self.image_label.pack()
self.newNote()
# Gets a new random note, outputs it to file
def newNote(self):
self.secret = randrange(self.lb, self.ub)
self.print()
if abs(60 - self.secret) <= 4:
clef = choice(["bass", "treble"])
else:
clef = "bass" if self.secret < 60 else "treble"
ly = lilypond.substitute(CLEF=clef, NOTE=f"{{{self.convertNote(self.secret)}}}")
with open("lily.txt", "w") as f:
f.write(ly)
f.close()
wait_for = subprocess.Popen(["lilypond", "-dpreview", "-dbackend=eps", "-dresolution=600", "--png", "lily.txt"])
self.refreshImage(wait_for)
def refreshImage(self, wait_for=None):
if wait_for:
wait_for.wait()
if self.image:
self.image.close()
self.image = Image.open("lily.png")
photo = ImageTk.PhotoImage(self.image)
self.image_label.configure(image=photo)
self.image_label.image = photo
def guess(self, note):
guess = (note == self.secret)
if guess:
self.newNote()
return guess
# Convert midi pitch to lilypond notation
def convertNote(self, pitch):
octave = int(pitch / 12) - 4
oct_str = -octave*"," if octave < 0 else octave*"'"
note = notes_sharp[(pitch % 12)]
return f"{note}{oct_str}"
def print(self):
print(f"Current midi note: {self.secret}")
print(self.convertNote(self.secret))
t = Trainer()
class MidiInputHandler(object):
def __init__(self, port):
self.port = port
def __call__(self, event, data=None):
message, deltatime = event
(ev, note, vel) = message
if ev == 144: # note on
print(f"Pressed note {note}")
print(t.convertNote(note))
t.guess(note)
# Prompts user for MIDI input port, unless a valid port number or name given
port = sys.argv[1] if len(sys.argv) > 1 else None
try:
midiin, port_name = open_midiinput(port)
except (EOFError, KeyboardInterrupt):
sys.exit()
midiin.set_callback(MidiInputHandler(port_name))
window.mainloop()
midiin.close_port()
del midiin