Files
Chromacase/poc_hardware/tester.py
Louis Auzuret cc85121465 poc in folder
2022-05-05 11:26:57 +02:00

147 lines
4.0 KiB
Python
Executable File

#!/usr/bin/env python3
import sys
import os
from typing import List
import pygame as pg
from pygame.constants import KEYDOWN
import pygame.midi
from mido import MidiFile
# Status definitions
TOUCH_DOWN = 144
TOUCH_UP = 128
class Key:
def __init__(self, key: int, start: int, duration: int):
self.key = key
self.start = start
self.duration = duration
def __str__(self):
return f"{self.key} ({self.start} - {self.duration})"
def read_midi(file):
notes = []
notes_on = {}
s = 0
for msg in MidiFile(file):
d = msg.dict()
s += d['time'] * 1000
if d["type"] == "note_on":
notes_on[d["note"]] = s
if d["type"] == "note_off":
duration = s - notes_on[d["note"]]
notes_on[d["note"]] = s
notes.append(Key(d["note"], s, duration))
return notes
keys_to_play = read_midi(sys.argv[1])
for i in map(lambda x: str(x), keys_to_play):
print(str(i))
# List of keys currently holded. Format: (key, timestamp)
keys_down = []
points = 0
def print_device_info():
pygame.midi.init()
_print_device_info()
pygame.midi.quit()
def _print_device_info():
for i in range(pygame.midi.get_count()):
r = pygame.midi.get_device_info(i)
(interf, name, input, output, opened) = r
in_out = ""
if input:
in_out = "(input)"
if output:
in_out = "(output)"
print(
"%2i: interface :%s:, name :%s:, opened :%s: %s"
% (i, interf, name, opened, in_out)
)
def poll(midi):
if midi.poll():
[((status, key, intensity, data3), timestamp)] = midi.read(1)
# For status, see STATUS DEFINITIONS up there (either TOUCH_DOWN, TOUCH_UP or others for pedals)
# The key is between 21 and 108, C5 is 60
# The itensity is how strong the key got struck (between 1 and 130ish)
# data3 seems to always be 0
# timestamp seems to be a unix timestamp since the midi has been oppened.
# Sometimes, status is always TOUCH_DOWN so if the key is already down, we consider it the same as a key up
is_down = any(x[0] == key for x in keys_down)
if status == TOUCH_DOWN and not is_down:
keys_down.append((key, timestamp))
# print(f"Midi: {status} - {key} - {intensity} - {data3} at {timestamp}")
elif status == TOUCH_UP or is_down:
down_since = next(since for (h_key, since) in keys_down if h_key == key)
keys_down.remove((key, down_since))
return Key(key, down_since, (timestamp - down_since))
def is_timing_close(key, i):
return abs(i.start - key.start) < 500
def run(midi):
global points
clock_now = 0
while sorted(keys_to_play, key=lambda x: x.start)[-1].start > clock_now:
key = poll(midi)
if key is None:
continue
clock_now = key.start
to_play = next((i for i in keys_to_play if i.key == key.key and is_timing_close(key, i)), None)
if to_play == None:
points -= 50
print(f"Invalid key.")
else:
tempo_percent = abs((key.duration / to_play.duration) - 1)
points += tempo_percent * 50
if tempo_percent < .3 :
print("Too short" if key.duration < to_play.duration else "Too long")
elif tempo_percent < .5:
print(f"GREAT.")
else:
print(f"EXCELLENT.")
points -= len(keys_to_play) * 20
def input_main(device_id=None):
pg.init()
pygame.midi.init()
_print_device_info()
if device_id is None:
input_id = pygame.midi.get_default_input_id()
else:
input_id = device_id
print("using input_id :%s:" % input_id)
i = pygame.midi.Input(input_id)
pg.display.set_mode((1, 1))
try:
run(i)
except KeyboardInterrupt:
pass
print(f"You got: {int(points)}pts")
pygame.midi.quit()
if __name__ == '__main__':
exit(input_main(int(sys.argv[2]) if len(sys.argv) == 3 else None))