111 lines
2.7 KiB
Python
111 lines
2.7 KiB
Python
from __future__ import print_function
|
|
|
|
import logging
|
|
import sys
|
|
import time
|
|
from random import randrange
|
|
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 = '''
|
|
\paper {
|
|
#(set-paper-size "a10landscape")
|
|
indent = 0\mm
|
|
line-width = 110\mm
|
|
oddHeaderMarkup = ""
|
|
evenHeaderMarkup = ""
|
|
oddFooterMarkup = ""
|
|
evenFooterMarkup = ""
|
|
}
|
|
|
|
'''
|
|
|
|
window = tk.Tk()
|
|
|
|
class Trainer():
|
|
def __init__(self):
|
|
self.secret = 0
|
|
self.lb = 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()
|
|
ly = lilypond + 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
|