balthou commited on
Commit
360737a
·
1 Parent(s): 43107b2

xylo player

Browse files
Files changed (3) hide show
  1. app.py +91 -0
  2. requirements.txt +2 -0
  3. synthetizer.py +80 -0
app.py ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from interactive_pipe import interactive_pipeline, interactive, Control, Image
2
+ from synthetizer import NOTE_FREQUENCIES, get_note
3
+ from interactive_pipe.data_objects.audio import Audio
4
+ from pathlib import Path
5
+ from time import sleep
6
+ import argparse
7
+ import cv2
8
+ import numpy as np
9
+
10
+
11
+ def select_note(note="C4", context=None):
12
+ context["note"] = note
13
+
14
+
15
+ def create_note(context={}):
16
+ note = context.get("note", "C4")
17
+ audio_signal = get_note(note)
18
+ return audio_signal
19
+
20
+
21
+ def play_note(audio_signal: np.ndarray, context={}):
22
+ note = context.get("note", "C4")
23
+ file_name = Path(f"__{note}.wav")
24
+ if not file_name.exists():
25
+ Audio.save_audio(audio_signal, str(file_name), 44100)
26
+ while not file_name.exists():
27
+ sleep(0.01)
28
+ print("waiting for file")
29
+ assert file_name.exists()
30
+ context["__set_audio"](file_name)
31
+ context["__play"]()
32
+
33
+
34
+ def display_color(context={}):
35
+ note = context.get("note", "C4")
36
+ return get_color(note, size=(256, 256))
37
+
38
+
39
+ def get_color(note, size=(256, 256)):
40
+ colors = {
41
+ "red": (1.0, 0.0, 0.0),
42
+ "orange": (1.0, 0.65, 0.0),
43
+ "yellow": (1.0, 1.0, 0.0),
44
+ "green": (0.0, 0.5, 0.0),
45
+ "blue": (0.0, 0.0, 1.0),
46
+ "dark blue": (0.0, 0.0, 0.55),
47
+ "purple": (0.5, 0.0, 0.5),
48
+ "pink": (1.0, 0.75, 0.8)
49
+ }
50
+ notes_translation = ["DO", "RE", "MI", "FA", "SOL", "LA", "SI", "DO"]
51
+ index = list(NOTE_FREQUENCIES.keys()).index(note)
52
+ color = colors.get(list(colors.keys())[index], [0., 0., 0.])
53
+ img = np.ones((size[1], size[0], 3)) * np.array(color)[None, None, :]
54
+ text = notes_translation[index]
55
+ font_scale = 4
56
+ thickness = 2
57
+ text_size = cv2.getTextSize(
58
+ text, cv2.FONT_HERSHEY_SIMPLEX, font_scale, thickness)[0]
59
+ text_x = (size[0] - text_size[0]) // 2
60
+ text_y = (size[1] + text_size[1]) // 2
61
+ cv2.putText(
62
+ img,
63
+ text,
64
+ (text_x, text_y),
65
+ cv2.FONT_HERSHEY_SIMPLEX, font_scale, (0, 0, 0), thickness
66
+ )
67
+ return img
68
+
69
+
70
+ def xylo_player():
71
+ select_note()
72
+ audio = create_note()
73
+ play_note(audio)
74
+ out_image = display_color()
75
+ return out_image
76
+
77
+
78
+ if __name__ == '__main__':
79
+ parser = argparse.ArgumentParser(description='Xylophone synthesizer')
80
+ parser.add_argument('-b', '--backend', type=str,
81
+ default='gradio', choices=['gradio', 'qt'])
82
+ args = parser.parse_args()
83
+ all_notes = list(NOTE_FREQUENCIES.keys())
84
+ icon_list = [Path(f"__{note}.jpg") for note in all_notes]
85
+ for note, icon in zip(all_notes, icon_list):
86
+ img = get_color(note, size=(512, 512))
87
+ Image.save_image(img, icon)
88
+ interactive(note=Control("C4", all_notes, icons=icon_list))(select_note)
89
+
90
+ interactive_pipeline(gui=args.backend, cache=False,
91
+ audio=True)(xylo_player)()
requirements.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ interactive-pipe
2
+ wavio
synthetizer.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ NOTE_FREQUENCIES = {
3
+ 'C4': 261.63, # Middle C
4
+ 'D4': 293.66,
5
+ 'E4': 329.63,
6
+ 'F4': 349.23,
7
+ 'G4': 392.00,
8
+ 'A4': 440.00,
9
+ 'B4': 493.88,
10
+ 'C5': 523.25
11
+ }
12
+
13
+ # from pydub import AudioSegment
14
+ # import simpleaudio as sa
15
+
16
+
17
+ def generate_sine_wave(frequency, duration, sample_rate=44100, amplitude=0.5):
18
+ """
19
+ Generate a sine wave for a given frequency, duration, and sample rate.
20
+ """
21
+ t = np.linspace(0, duration, int(sample_rate * duration), endpoint=False)
22
+ wave = amplitude * np.sin(2 * np.pi * frequency * t)
23
+ return wave
24
+
25
+
26
+ def apply_decay(wave, decay_factor=0.0001):
27
+ """
28
+ Apply an exponential decay to the waveform to simulate the damping effect.
29
+ """
30
+ decay = np.exp(-decay_factor * np.arange(len(wave)))
31
+ return wave * decay
32
+
33
+
34
+ def create_xylophone_note(frequency, duration=0.5, sample_rate=44100):
35
+ """
36
+ Create a synthesized xylophone note using sine waves and damping.
37
+ """
38
+ # Generate the fundamental frequency
39
+ wave = generate_sine_wave(frequency, duration, sample_rate)
40
+
41
+ # Add overtones (harmonics) to mimic the xylophone's metallic timbre
42
+ overtone1 = generate_sine_wave(
43
+ frequency * 2.5, duration, sample_rate, amplitude=0.3)
44
+ overtone2 = generate_sine_wave(
45
+ frequency * 4.0, duration, sample_rate, amplitude=0.2)
46
+
47
+ # Combine the fundamental frequency and overtones
48
+ combined_wave = wave + overtone1 + overtone2
49
+
50
+ # Apply decay to simulate the note fading out
51
+ combined_wave = apply_decay(combined_wave)
52
+
53
+ # Normalize to 16-bit range and convert to audio segment
54
+ # audio_data = (combined_wave * 32767).astype(np.int16)
55
+ audio_data = combined_wave
56
+ return audio_data
57
+ # audio_segment = AudioSegment(audio_data.tobytes(
58
+ # ), frame_rate=sample_rate, sample_width=2, channels=1)
59
+
60
+ # return audio_segment
61
+
62
+
63
+ # def play_note(note):
64
+ # """
65
+ # Play a given AudioSegment note using simpleaudio.
66
+ # """
67
+ # play_obj = sa.play_buffer(
68
+ # note.raw_data, num_channels=1, bytes_per_sample=2, sample_rate=note.frame_rate)
69
+ # play_obj.wait_done()
70
+
71
+
72
+ def get_note(note: str) -> np.ndarray:
73
+ # Example usage to create and play a sequence of xylophone notes
74
+
75
+ return create_xylophone_note(NOTE_FREQUENCIES.get(note, 'C4'))
76
+ # Generate and play each note
77
+ # for note_name, frequency in note_frequencies.items():
78
+ # print(f"Playing {note_name}")
79
+ # note = create_xylophone_note(frequency)
80
+ # play_note(note)