Spaces:
Runtime error
Runtime error
from interactive_pipe import interactive_pipeline, interactive, Control, Image | |
from synthetizer import NOTE_FREQUENCIES, get_note | |
from interactive_pipe.data_objects.audio import Audio | |
from pathlib import Path | |
from typing import Tuple | |
from time import sleep | |
import argparse | |
import cv2 | |
import numpy as np | |
import logging | |
SONG_1 = "Au clair de la lune" | |
SONG_2 = "Ainsi font, font, font" | |
SONG_3 = "Dodo l'enfant do" | |
SONG_4 = "A la claire fontaine" | |
SONG = { | |
SONG_1: "fa fa fa sol la - sol - fa la sol sol fa - fa fa fa sol la - sol - fa la sol sol fa - - sol sol sol sol re - re - sol fa mi re do - fa fa fa sol la - sol - fa la sol sol fa", | |
SONG_2: "mi mi do - mi - sol - sol - la sol fa mi re mi do mi mi do - mi - sol - sol - la sol fa mi re do", | |
SONG_3: "mi - do - mi mi do - re mi fa mi re sol mi do - mi - do - mi mi do - re mi fa mi re sol do", | |
SONG_4: "fa - fa la la sol la sol - fa - fa la la sol la - la - la sol fa la do la do - do la fa la sol - fa - fa la la sol fa la fa la - la sol fa la sol fa" | |
} | |
def select_song(song: str = SONG_1, context={}): | |
previous_song = context.get("song", None) | |
if previous_song != song: | |
# reset time index | |
context["time_index"] = 0 | |
context["song"] = song | |
def select_note(note="C4", context={}): | |
context["note"] = note | |
def create_note(context={}): | |
note = context.get("note", "C4") | |
audio_signal = get_note(note) | |
return audio_signal | |
def play_note(audio_signal: np.ndarray, context={}): | |
note = context.get("note", "C4") | |
file_name = Path(f"__{note}.wav") | |
if not file_name.exists(): | |
Audio.save_audio(audio_signal, str(file_name), 44100) | |
while not file_name.exists(): | |
sleep(0.01) | |
print("waiting for file") | |
assert file_name.exists() | |
try: | |
if context["time_index"] == 0: | |
context["__stop"]() | |
else: | |
context["__set_audio"](file_name) | |
context["__play"]() | |
except Exception as e: | |
logging.warning( | |
f"Error playing note {note}: {e}, not expected to work with MPL backend for instance") | |
def display_current_color(context={}): | |
if context["time_index"] == 0: | |
return np.zeros((256, 256, 3)) | |
note = context.get("note", "C4") | |
return get_color(note, size=(256, 256)) | |
def display_next_color(context={}): | |
target_note = context.get("target_note", None) | |
if target_note is None: | |
return np.zeros((256, 256, 3)) | |
return get_color(target_note, size=(256, 256)) | |
NOTES_TRANSLATION = ["do", "re", "mi", "fa", "sol", "la", "si", "do2"] | |
NOTES_CORRESPONDANCE = { | |
NOTES_TRANSLATION[i]: note for i, note in enumerate(list(NOTE_FREQUENCIES.keys()))} | |
def get_color(note, size=(256, 256)): | |
colors = { | |
"red": (1.0, 0.0, 0.0), | |
"orange": (1.0, 0.65, 0.0), | |
"yellow": (0.9, 0.9, 0.0), | |
"green": (0.0, 0.5, 0.0), | |
"cyan": (0.0, 0.7, 1.0), | |
"dark blue": (0.0, 0.0, 0.7), | |
"purple": (0.5, 0.0, 0.5), | |
"pink": (1.0, 0.75, 0.8), | |
} | |
index = list(NOTE_FREQUENCIES.keys()).index(note) | |
color = colors.get(list(colors.keys())[index], [0., 0., 0.]) | |
img = np.ones((size[1], size[0], 3)) * np.array(color)[None, None, :] | |
text = NOTES_TRANSLATION[index].upper() | |
font_scale = size[0] // 64 | |
thickness = 2 | |
text_size = cv2.getTextSize( | |
text, cv2.FONT_HERSHEY_SIMPLEX, font_scale, thickness)[0] | |
text_x = (size[0] - text_size[0]) // 2 | |
text_y = (size[1] + text_size[1]) // 2 | |
cv2.putText( | |
img, | |
text, | |
(text_x, text_y), | |
cv2.FONT_HERSHEY_SIMPLEX, font_scale, (255, 255, 255), thickness | |
) | |
return img | |
def add_border(img, border_size=10, color=(0.2, 0.2, 0.2)): | |
border_size = 4 | |
img[:border_size, :] = color | |
img[-border_size:, :] = color | |
img[:, :border_size] = color | |
img[:, -border_size:] = color | |
def increment_time(context: dict = {}) -> None: | |
time_index = context.get("time_index", None) | |
if time_index is not None: | |
context["time_index"] += 1 | |
else: | |
context["time_index"] = 0 | |
def xylo_player(): | |
select_song() | |
select_note() | |
full_song = song_player() | |
audio = create_note() | |
play_note(audio) | |
# current_note = display_current_color() | |
target_note = display_next_color() | |
increment_time() | |
return [full_song, target_note] | |
def song_player(context={}): | |
song = context["song"] | |
song_str = SONG.get(song, "") | |
image_song, target_note = generate_song( | |
song_str, current_time=context.get("time_index", 0)) | |
context["target_note"] = target_note | |
return image_song | |
def generate_song(song_str, current_time=None) -> Tuple[np.ndarray, str]: | |
notes = song_str.split(" ") | |
all_notes = [] | |
size = (64, 128) | |
index_no_silence = -1 | |
target_note = None | |
for idx, note in enumerate(notes): | |
if note in ["-", "."]: | |
img_note = np.zeros((size[1], size[0], 3)) | |
color = (0.2, 0.2, 0.2) | |
else: | |
note_classic = NOTES_CORRESPONDANCE.get(note, None) | |
index_no_silence += 1 | |
if note_classic is None: | |
print(f"Note {note} not found") | |
continue | |
img_note = get_color(note_classic, size=size) | |
if current_time == index_no_silence: | |
target_note = note_classic | |
color = (0.8, 0., 0.) | |
else: | |
color = (0.2, 0.2, 0.2) | |
add_border(img_note, color=color) | |
all_notes.append(img_note) | |
max_notes_per_line = 12 | |
remainder = max_notes_per_line - len(all_notes) % max_notes_per_line | |
for _ in range(remainder): | |
all_notes.append(np.zeros_like(all_notes[0])) | |
note_lines = [all_notes[i:i + max_notes_per_line] | |
for i in range(0, len(all_notes), max_notes_per_line)] | |
out_image = np.vstack([np.hstack(line) for line in note_lines]) | |
return out_image, target_note | |
if __name__ == '__main__': | |
parser = argparse.ArgumentParser(description='Xylophone synthesizer') | |
parser.add_argument('-b', '--backend', type=str, | |
default='gradio', choices=['gradio', 'qt', 'mpl']) | |
args = parser.parse_args() | |
all_notes = list(NOTE_FREQUENCIES.keys()) | |
icon_list = [Path(f"__{note}.jpg") for note in all_notes] | |
for note, icon in zip(all_notes, icon_list): | |
img = get_color(note, size=(512, 512)) | |
Image.save_image(img, icon) | |
interactive(note=Control("C4", all_notes, icons=icon_list))(select_note) | |
interactive(song=(SONG_1, list(SONG.keys())))(select_song) | |
interactive_pipeline( | |
gui=args.backend, | |
cache=False, | |
audio=True | |
)(xylo_player)() | |