poc in folder

This commit is contained in:
Louis Auzuret
2022-05-05 11:26:57 +02:00
parent 4e9ad2a05f
commit cc85121465
20 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,13 @@
class Note:
def __init__(self, start_time, data) -> None:
self.__start_time = start_time
self.__data = data
def get_start_time(self):
return self.__start_time
def get_data(self):
return self.__data

View File

@@ -0,0 +1,34 @@
import asyncio, datetime
from typing import Callable
from .Note import Note
async def wait_until(dt):
# sleep until the specified datetime
now = datetime.datetime.now()
await asyncio.sleep((dt - now).total_seconds())
async def run_at(dt, coro):
await wait_until(dt)
return await coro
class Partition:
def __init__(self, name:str, notes:list[Note]) -> None:
self.__name = name
self.__notes = notes
async def play(self, output_lambda:Callable[[object], None]):
now = datetime.datetime.now()
tasks_to_wait = []
for note in self.__notes:
tasks_to_wait.append(
asyncio.create_task(
run_at(
now + datetime.timedelta(milliseconds= note.get_start_time()),
output_lambda(note.get_data())
)
)
)
await asyncio.wait(tasks_to_wait)

View File

5
poc_hardware/launch.sh Executable file
View File

@@ -0,0 +1,5 @@
#!/usr/bin/zsh
sudo python3 main.py $1 &!
sleep 3.5
./tester.py $1 $2

56
poc_hardware/leds.py Executable file
View File

@@ -0,0 +1,56 @@
#!/usr/bin/python3
import board
import neopixel
import time
import sys
import asyncio
colorToFill = (0, 0, 0)
pixels = neopixel.NeoPIxel(board.D18, 20, brightness=0.01)
notePixels = { 'si': [0, 1],
'la#': [2, 3],
'la': [4, 5],
'sol#':[6],
'sol':[7, 8, 9],
'fa#':[10],
'fa':[11, 12, 13],
'mi':[14, 15, 16],
're#':[17],
're':[18, 19],
'do#':[],
'do':[]}
def playNote(color, secondsToStay, pixelsToFill):
for pixelIndex in pixelsToFill:
pixels[pixelIndex] = color
time.sleep(secondsToStay)
def launchMusic(noteList):
pixels.fill(0,0,0)
pixels.write()
for notes, tempo in noteList:
for note in notes:
playNote((255, 0, 0), tempo, notePixels[note.lower()])
pixels.fill(colorToFill)
pixels.write()
music = [
(['sol'], 1),
(['sol'], 1),
(['sol'], 1),
(['re#'], 1),
(['la#'], 0.5),
(['sol'], 0.5),
(['re#'], 1),
(['la#'], 0.5),
]
launchMusic(music)

166
poc_hardware/main.py Normal file
View File

@@ -0,0 +1,166 @@
from xmlrpc.client import TRANSPORT_ERROR
from chroma_case.Partition import Partition
from chroma_case.Note import Note
import asyncio
import sys
from mido import MidiFile
import board, neopixel
# on octave is 12
RATIO = float(sys.argv[2] if len(sys.argv) > 2 else 1)
OCTAVE = 5
OCTAVE_AMOUNT_KEYS = 12
TRANSPOSE_AMOUNT = OCTAVE_AMOUNT_KEYS * OCTAVE
pixels = neopixel.NeoPixel(board.D18, 20, brightness=0.01)
notePixels = { 'si': [19],
'la#': [18],
'la': [17],
'sol#':[15],
'sol':[13],
'fa#':[10],
'fa':[9],
'mi':[6],
're#':[5],
're':[3],
'do#':[1],
'do':[0]}
def hue_to_rgb(t1, t2, hue):
if hue < 0: hue += 6
if hue >= 6: hue -= 6
if hue < 1: return (t2 - t1) * hue + t1
if hue < 3: return t2
if hue < 4: return (t2 - t1) * (4 - hue) + t1
return t1
def hsl_to_rgb(hue, sat, light):
hue /= 60
if light <= 0.5:
t2 = light * (sat + 1)
else:
t2 = light + sat - (light * sat)
t1 = light * 2 - t2
r = hue_to_rgb(t1, t2, hue + 2) * 255
g = hue_to_rgb(t1, t2, hue) * 255
b = hue_to_rgb(t1, t2, hue - 2) * 255
return [round(r), round(g), round(b)]
async def to_chroma_case(data):
global pixels
hsl_starting_color = [100, 100, 50]
colored_pixels = notePixels[data["key"].lower()]
#if "announce" in data:
c = data["color"]
"""for i in range(5):
for pixelId in colored_pixels:
pixels[pixelId] = (c[0], int(c[1] * tmp), c[2])
tmp -= 0.2
await asyncio.sleep(data["duration"] / (5 * 1000))"""
"""for i in range(11):
for pixelId in colored_pixels:
pixels[pixelId] = hsl_to_rgb(hsl_starting_color[0], hsl_starting_color[1], hsl_starting_color[2])
hsl_starting_color[2] += 0.01
await asyncio.sleep(0.01)"""
for pixelId in colored_pixels:
pixels[pixelId] = data["color"]
await asyncio.sleep(data['duration'] / 1000)
for pixelId in colored_pixels:
pixels[pixelId] = 0
async def printing(data):
print(f"key: {data['key']}, c:{data['color']} for {data['duration'] / 1000}s, time: {data['time']}")
await asyncio.sleep(data['duration'] / 1000)
print(f"end of {data['key']}")
def midi_key_my_key(midi_key):
keys = list(notePixels.keys())
keys.reverse()
key_index = midi_key - TRANSPOSE_AMOUNT
if key_index >= len(keys):
print("key out of leb barre", key_index)
return "no_key"
return keys[key_index]
async def main():
default_duration = 900
default_color = (255, 0, 0)
notes = []
# notes will start to play at 3500 ms (colors at the start takes this amount of time)
s = 3500
notes_on = {}
prev_note_on = {}
note_color = {}
for msg in MidiFile(sys.argv[1]):
d = msg.dict()
print(msg, s)
s += d['time'] * 1000 * RATIO
if d["type"] == "note_on":
prev_note_on[d["note"]] = 0
if d["note"] in notes_on:
prev_note_on[d["note"]] = notes_on[d["note"]] # 500
notes_on[d["note"]] = s # 0
if d["note"] not in note_color.keys():
note_color[d["note"]] = 1
note_color[d["note"]] = not note_color[d["note"]]
if d["type"] == "note_off":
#duration = s - notes_on[d["note"]]
duration = s - notes_on[d["note"]]
"""notes.append(Note(
s - min(s - prev_note_on[d["note"]], 500),
{
"duration": min(s - prev_note_on[d["note"]], 1000) / 2,
"color": (255, 255, 0),
"key": midi_key_my_key(d["note"]),
"announce": True
}
))"""
note_start = notes_on[d["note"]]
# time value is only used during debug
notes.append(Note(note_start, {"time": note_start, "duration": duration - 10, "color": default_color if note_color[d["note"]] else (255, 100, 0), "key": midi_key_my_key(d["note"])}))
notes_on[d["note"]] = s # 500
starting = []
for i in notePixels.keys():
starting += [
Note(000, {"duration": default_duration, "color": (255, 0, 0), "key": i, "time": 0}),
Note(1000, {"duration": default_duration, "color": (255, 255, 0), "key": i, "time": 0}),
Note(2000, {"duration": default_duration, "color": (0, 255, 0), "key": i, "time": 0}),
]
p = Partition("my_partition",
starting + notes
)
await p.play(to_chroma_case)
return 0
if __name__ == "__main__":
sys.exit(asyncio.run(main()))

4
poc_hardware/midi.py Normal file
View File

@@ -0,0 +1,4 @@
from mido import MidiFile
for msg in MidiFile('new_song_1.mid'):
print(msg)

7
poc_hardware/myscr Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/bash
while true
do
sudo python main.py new_song_2.mid
done

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,2 @@
mido
pygame

15
poc_hardware/test.py Normal file
View File

@@ -0,0 +1,15 @@
import asyncio
async def nested():
return 42
async def main():
# Schedule nested() to run soon concurrently
# with "main()".
task = asyncio.create_task(nested())
# "task" can now be used to cancel "nested()", or
# can simply be awaited to wait until it is complete:
await task
asyncio.run(main())

146
poc_hardware/tester.py Executable file
View File

@@ -0,0 +1,146 @@
#!/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))