Commit 7407cd4c authored by Koen Martens's avatar Koen Martens

Initial commit

parents
Pipeline #23 failed with stages
.idea/*
env/*
*.pyc
This diff is collapsed.
# Midi Music Trainer
A simple script to help me practice intervals and chords.
Monitors a jack midi device for notes, and judges whether the
notes are correct according to certain rules. If correct, a
bell sounds. If incorrect, a buzzer sounds.
For example, can be used to practive major third intervals,
just press any note and the corresponding major third, and
the bell sounds.
## Sounds
* [Bell sound by InspectorJ (CC BY 3.0)](https://freesound.org/s/411089/)
* [Buzzer sound by hypocore (CC0 1.0)](https://freesound.org/s/164090/)
#!/usr/bin/env python3
"""JACK client that prints all received MIDI events."""
import jack
import binascii
import mido
import queue
import soundfile as sf
import numpy as np
client = jack.Client('MIDI-Monitor')
midi_port = client.midi_inports.register('input')
audio_port = client.outports.register('verdict')
parser = mido.Parser()
message_queue = queue.Queue()
audio_queue = queue.Queue()
block_size = client.blocksize
@client.set_process_callback
def process(frames):
for offset, data in midi_port.incoming_midi_events():
# print('{0}: 0x{1}'.format(client.last_frame_time + offset,
# binascii.hexlify(data).decode()))
# print(data[:])
parser.feed(data[:])
for message in parser:
# print(f'queue {message}')
message_queue.put(message)
try:
data = audio_queue.get_nowait()
audio_port.get_array()[:] = data
except queue.Empty:
pass
client.activate()
def describe_note(note):
notes = ['C', 'C♯/D♭', 'D', 'D♯/E♭', 'E', 'F', 'F♯/G♭', 'G', 'G♯/A♭', 'A', 'A♯/B♭', 'B']
return notes[note % len(notes)]
def judge_notes(notes):
if len(notes) < 2:
return None
if len(notes) > 2:
return False
return (max(notes) - min(notes)) == 4
def load_sound(file_name, blocksize):
sound_file = sf.SoundFile(file_name)
blocks = list(sound_file.blocks(blocksize=blocksize, dtype='float32', always_2d=True, fill_value=0))
blocks = [np.mean(block, axis=1) for block in blocks]
return blocks
def load_sounds(blocksize):
good = load_sound('audio/411089__inspectorj__bell-candle-damper-a-h1.wav', blocksize)
bad = load_sound('audio/164090__hypocore__buzzer.wav', blocksize)
return good, bad
def play_sound(sound):
while not audio_queue.empty():
try:
audio_queue.get_nowait()
except queue.Empty:
pass
for block in sound:
audio_queue.put(block)
good_sound, bad_sound = load_sounds(block_size)
with client:
notes_on = set()
while True:
message = message_queue.get()
print(f'get {message}')
if message.type == 'note_on':
notes_on.add(message.note)
else:
notes_on.remove(message.note)
print(notes_on)
print([describe_note(note) for note in notes_on])
verdict = judge_notes(notes_on)
if verdict is True:
print("GOOD")
play_sound(good_sound)
elif verdict is False:
print("BAD")
play_sound(bad_sound)
print('#' * 80)
print('press Return to quit')
print('#' * 80)
input()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment