pachet commited on
Commit
52d7d5e
·
1 Parent(s): a3cb78c

Update .gitignore with comprehensive patterns for generated and system files

Browse files
Files changed (8) hide show
  1. .gitignore +27 -0
  2. app.py +236 -62
  3. app_in_progress.py +253 -0
  4. essai.py +37 -0
  5. format_conversions.py +14 -1
  6. old_app.py +11 -10
  7. requirements.txt +4 -1
  8. working_midi_to_svg_app.py +56 -0
.gitignore ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ *.pyd
6
+ .Python
7
+
8
+ # IDE
9
+ .idea/
10
+ *.iml
11
+ *.iws
12
+
13
+ # macOS
14
+ .DS_Store
15
+
16
+ # Generated files
17
+ *.png
18
+ *.mid
19
+ *.mp3
20
+ *.wav
21
+ *.ly
22
+ output.*
23
+ temp.*
24
+
25
+ # Project specific
26
+ soundfont.sf2
27
+ .gradio/
app.py CHANGED
@@ -1,9 +1,18 @@
1
- import os
 
2
  import gradio as gr
 
 
 
 
 
 
 
 
 
3
  import verovio
4
- import music21
5
- from music21 import converter, musicxml
6
 
 
7
 
8
  def get_verovio_resource_path():
9
  for path in verovio.__path__:
@@ -28,77 +37,242 @@ tk.setOptions({
28
  "landscape": False,
29
  })
30
 
31
- # Initialize Verovio toolkit
32
- tk = verovio.toolkit()
33
- tk.setOptions({
34
- "scale": 40,
35
- "adjustPageHeight": True
36
- })
37
 
38
- def midi_to_musicxml_string(midi_path):
39
- # Parse MIDI file into a music21 stream
40
- score = converter.parse(midi_path)
41
- # Export to MusicXML string
42
- exporter = musicxml.m21ToXml.GeneralObjectExporter(score)
43
- musicxml_str = exporter.parse().decode('utf-8') # parse() returns bytes
44
- return musicxml_str
45
 
46
- def xml_to_mei_string(xmlstring):
47
- tk.loadData(xmlstring)
48
- return tk.getMEI()
 
 
 
 
 
 
49
 
50
- def midi_to_svg(midi_path):
51
- try:
52
- # Convert MIDI to MEI using music21
53
- musicxml = midi_to_musicxml_string(midi_path)
54
- mei_str = xml_to_mei_string(musicxml)
55
- # Load MEI and render SVG
56
- tk.loadData(mei_str)
57
- svg = tk.renderToSVG(1)
58
- return svg
59
- except Exception as e:
60
- return f"<p style='color:red'>Error: {e}</p>"
61
 
62
- # Gradio UI
63
- with gr.Blocks() as demo:
64
- gr.Markdown("## 🎼 MIDI to Score Viewer (via MEI)")
 
 
 
 
 
 
 
 
 
 
65
 
66
- midi_input = gr.File(label="Upload a MIDI file", file_types=[".mid", ".midi"])
67
- render_btn = gr.Button("Convert & Render")
68
- score_display = gr.HTML(label="Score Output")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
- render_btn.click(fn=midi_to_svg, inputs=midi_input, outputs=score_display)
71
 
72
- import subprocess
73
- import tempfile
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
 
75
- def midi_to_mei_string(midi_path):
76
- try:
77
- with tempfile.NamedTemporaryFile(suffix=".mei", delete=False) as tmp:
78
- mei_path = tmp.name
79
 
80
- # Call Verovio CLI
81
- subprocess.run(
82
- ["verovio", "-f", "midi", midi_path, "-o", mei_path],
83
- check=True
84
- )
85
 
86
- # Read the resulting MEI file
87
- with open(mei_path, "r", encoding="utf-8") as f:
88
- mei_str = f.read()
89
 
90
- return mei_str
 
 
 
 
 
 
 
 
 
 
 
 
91
 
92
- except subprocess.CalledProcessError as e:
93
- return f"<p style='color:red'>Verovio CLI failed: {e}</p>"
94
- except Exception as e:
95
- return f"<p style='color:red'>Error: {e}</p>"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
 
98
- # Smart launch settings
99
- if os.getenv("SPACE_ID"):
100
- demo.launch()
101
- else:
102
- # demo.launch(share=False, inbrowser=True)
103
- demo.launch(server_name="0.0.0.0", server_port=7860)
104
 
 
1
+ import ast
2
+ import shutil
3
  import gradio as gr
4
+ import numpy as np
5
+ from matplotlib import pyplot as plt
6
+ from mido import Message, MidiFile, MidiTrack
7
+ import os
8
+ import subprocess
9
+ from music21 import converter, note
10
+ from pydub import AudioSegment
11
+ import hexachords
12
+ from format_conversions import Format_Converter
13
  import verovio
 
 
14
 
15
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
16
 
17
  def get_verovio_resource_path():
18
  for path in verovio.__path__:
 
37
  "landscape": False,
38
  })
39
 
40
+ class HexachordApp:
 
 
 
 
 
41
 
42
+ def __init__(self):
43
+ self._hexachord = hexachords.Hexachord()
44
+ self._hexachord_base_sequence = None
45
+ self.ui = None
46
+ self.on_huggingface = "HUGGINGFACE_SPACE" in os.environ
 
 
47
 
48
+ def is_fsynth_installed(self):
49
+ """ Check to make sure fluidsynth exists in the PATH """
50
+ for path in os.environ['PATH'].split(os.pathsep):
51
+ f = os.path.join(path, 'fluidsynth')
52
+ if os.path.exists(f) and os.access(f, os.X_OK):
53
+ print('fluidsynth is installed')
54
+ return True
55
+ print('fluidsynth is NOT installed')
56
+ return False
57
 
58
+ def generate_chords(self, note_names, itvl):
59
+ # Placeholder for your actual chord generation function
60
+ # Assuming hexachord is a list of MIDI note numbers
61
+ interval_21 = 'P5'
62
+ if itvl == 'fourth':
63
+ interval_21 = 'P4'
64
+ self._hexachord_base_sequence = self._hexachord.generate_chord_sequence(note_names, intrvl=interval_21)
65
+ return self._hexachord_base_sequence
 
 
 
66
 
67
+ def generate_realizations(self):
68
+ # returns 3 triples of midipath, piano roll, audio player
69
+ reals = self._hexachord.generate_3_chords_realizations(self._hexachord_base_sequence)
70
+ midi_path1 = self.create_midi(reals[0], "real1.mid")
71
+ piano_roll_path1 = self.generate_piano_roll(reals[0], "real1.png")
72
+ audio_path1 = self.convert_midi_to_audio(midi_path1, "real1")
73
+ midi_path2 = self.create_midi(reals[1], "real2.mid")
74
+ piano_roll_path2 = self.generate_piano_roll(reals[1], "real2.png")
75
+ audio_path2 = self.convert_midi_to_audio(midi_path2, "real2")
76
+ midi_path3 = self.create_midi(reals[2], "real3.mid")
77
+ piano_roll_path3 = self.generate_piano_roll(reals[2], "real3.png")
78
+ audio_path3 = self.convert_midi_to_audio(midi_path3, "real3")
79
+ return midi_path1, piano_roll_path1, audio_path1, midi_path2, piano_roll_path2, audio_path2, midi_path3, piano_roll_path3, audio_path3
80
 
81
+ def create_midi(self, chords, file_name):
82
+ mid = MidiFile()
83
+ track = MidiTrack()
84
+ mid.tracks.append(track)
85
+ delta_time = 480 * 4
86
+ for i_chord, chord in enumerate(chords):
87
+ for i, note in enumerate(chord):
88
+ if i == 0 and i_chord != 0:
89
+ track.append(Message('note_on', note=note.pitch.midi, velocity=64, time=1))
90
+ else:
91
+ track.append(Message('note_on', note=note.pitch.midi, velocity=64, time=0))
92
+ for i, note in enumerate(chord):
93
+ if i==0:
94
+ track.append(Message('note_off', note=note.pitch.midi, velocity=0, time=delta_time - 1))
95
+ else:
96
+ track.append(Message('note_off', note=note.pitch.midi, velocity=0, time=0))
97
+ midi_path = os.path.join(BASE_DIR, file_name)
98
+ mid.save(midi_path)
99
+ return midi_path
100
 
 
101
 
102
+ def convert_midi_to_audio(self, midi_path, file_name):
103
+ if not shutil.which("fluidsynth"):
104
+ try:
105
+ subprocess.run(["apt-get", "update"], check=True)
106
+ subprocess.run(["apt-get", "install", "-y", "fluidsynth"], check=True)
107
+ except Exception as e:
108
+ return f"Error installing Fluidsynth: {str(e)}"
109
+ wav_path = os.path.join(BASE_DIR, file_name + ".wav")
110
+ mp3_path = os.path.join(BASE_DIR, file_name + ".mp3")
111
+ soundfont_path = os.path.join(BASE_DIR, "soundfont.sf2")
112
+ if not os.path.exists(soundfont_path):
113
+ return "Error: SoundFont file not found. Please provide a valid .sf2 file."
114
+ try:
115
+ subprocess.run(["fluidsynth", "-ni", soundfont_path, midi_path, "-F", wav_path, "-r", "44100"], check=True)
116
+ AudioSegment.converter = "ffmpeg"
117
+ audio = AudioSegment.from_wav(wav_path)
118
+ audio.export(mp3_path, format="mp3")
119
+ return mp3_path
120
+ except Exception as e:
121
+ return f"Error converting MIDI to audio: {str(e)}"
122
 
123
+ def generate_svg(self, midi_file, output_file_name):
124
+ return Format_Converter().midi_to_svg_file(tk, midi_file, output_file_name)
 
 
125
 
126
+ def generate_piano_roll(self, chords, label):
127
+ fig, ax = plt.subplots(figsize=(8, 4))
 
 
 
128
 
129
+ for i, chord in enumerate(chords):
130
+ for note in chord:
131
+ ax.broken_barh([(i * 1, 0.8)], (note.pitch.midi - 0.4, 0.8), facecolors='blue')
132
 
133
+ ax.set_xlabel("Chord Progression")
134
+ ax.set_ylabel("MIDI Note Number")
135
+ min_min_chords = 128
136
+ max_max_chords = 0
137
+ for ch in chords:
138
+ for note in ch:
139
+ if note.pitch.midi < min_min_chords:
140
+ min_min_chords = note.pitch.midi
141
+ if note.pitch.midi > max_max_chords:
142
+ max_max_chords = note.pitch.midi
143
+ ax.set_yticks(range(min_min_chords, max_max_chords + 1, 2))
144
+ ax.set_xticks(range(len(chords)))
145
+ ax.set_xticklabels([f"Chord {i + 1}" for i in range(len(chords))])
146
 
147
+ plt.grid(True, linestyle='--', alpha=0.5)
148
+ plt.savefig(label)
149
+ return label
150
+
151
+ def launch_score_editor(self, midi_path):
152
+ try:
153
+ score = converter.parse(midi_path)
154
+ score.show('musicxml')
155
+ return "Opened MIDI file in the default score editor!"
156
+ except Exception as e:
157
+ return f"Error opening score editor: {str(e)}"
158
+
159
+ def process_hexachord(self, hexachord_str, itvl):
160
+ try:
161
+ notes = [note.Note(n) for n in hexachord_str.split()]
162
+ if len(notes) != 6 or len(set(notes)) != 6:
163
+ return "Please enter exactly 6 unique MIDI note numbers."
164
+ except ValueError:
165
+ return "Invalid input. Enter 6 MIDI note numbers separated by spaces."
166
+ chords = self.generate_chords(notes, itvl)
167
+ midi_path = self.create_midi(chords, "base_chords.mid")
168
+ # piano_roll_path = self.generate_piano_roll(chords, "base_chords.png")
169
+ score_path = self.generate_svg (midi_path, "score_base_chords.svg")
170
+ audio_path = self.convert_midi_to_audio(midi_path, "base_chords")
171
+
172
+ # return midi_path, piano_roll_path, audio_path
173
+ return midi_path, score_path, audio_path
174
+
175
+ def render(self):
176
+ with gr.Blocks() as ui:
177
+ gr.Markdown("# Hexachord-based Chord Generator")
178
+
179
+ with gr.Tabs():
180
+ with gr.TabItem("Hexachord Generator"):
181
+ with gr.Row():
182
+ hexachord_selector = gr.Dropdown(label="Select Known Hexachord",
183
+ choices=self.get_known_hexachords_choice(), value=None, interactive=True)
184
+ hexachord_input = gr.Textbox(
185
+ label="Enter 6 notes (pitchclass plus octave, separated by spaces)",
186
+ value="C3 D3 E3 G3 A3 B3",
187
+ interactive = True
188
+ )
189
+ interval_switch = gr.Radio(
190
+ choices=["fourth", "fifth"],
191
+ label="Select Interval",
192
+ value="fifth"
193
+ )
194
+ generate_button = gr.Button("Generate Chords")
195
+ midi_output = gr.File(label="Download MIDI File")
196
+ # piano_roll_output = gr.Image(label="Piano Roll Visualization")
197
+ score_output = gr.Image(label="Score Visualization")
198
+ audio_output = gr.Audio(label="Play Generated Chords", value=None, interactive=False)
199
+
200
+ hexachord_selector.change(
201
+ fn=self.get_selected_hexachord,
202
+ inputs=[hexachord_selector],
203
+ outputs=[hexachord_input]
204
+ )
205
+ # generate_button.click(
206
+ # fn=self.process_hexachord,
207
+ # inputs=[hexachord_selector, interval_switch],
208
+ # outputs=[midi_output, score_output, audio_output]
209
+ # )
210
+
211
+ generate_button.click(
212
+ fn=self.process_hexachord,
213
+ inputs=[hexachord_input, interval_switch],
214
+ # outputs=[midi_output, piano_roll_output, audio_output]
215
+ outputs = [midi_output, score_output, audio_output]
216
+ )
217
+ # Pressing Enter in the textbox also triggers processing
218
+ hexachord_input.submit(
219
+ fn=self.process_hexachord,
220
+ inputs=[hexachord_input, interval_switch],
221
+ outputs=[midi_output, score_output, audio_output]
222
+ )
223
+
224
+ with gr.TabItem("Alternative Chord Realizations"):
225
+ gr.Markdown("Alternative Chord Realizations")
226
+
227
+ realization_button = gr.Button("Generate Alternative Realizations")
228
+ realization_outputs = []
229
+
230
+ for i in range(3): # Three alternative realizations
231
+ with gr.Group():
232
+ gr.Markdown(f"#### Realization {i + 1}")
233
+ midi_output = gr.File(label="Download MIDI File")
234
+ piano_roll = gr.Image(label=f"Piano Roll {i + 1}")
235
+ audio_player = gr.Audio(label=f"Play Chords {i + 1}", interactive=False)
236
+ realization_outputs.append((midi_output, piano_roll, audio_player))
237
+
238
+ # Clicking the button triggers realization generation
239
+ realization_button.click(
240
+ fn=self.generate_realizations,
241
+ inputs=[],
242
+ outputs=[output for trip in realization_outputs for output in trip]
243
+ )
244
+
245
+ with gr.TabItem("Settings"):
246
+ gr.Markdown("### Configuration Options")
247
+ setting_1 = gr.Checkbox(label="Enable Advanced Mode")
248
+ setting_2 = gr.Slider(0, 100, label="Complexity Level")
249
+ self.ui = ui
250
+
251
+ def get_known_hexachords_choice(self):
252
+ return self._hexachord.known_hexachords
253
+
254
+ def get_selected_hexachord(self, x):
255
+ # lambda x: {"Hexachord 1": "C3 D3 E3 G3 A3 B3", "Hexachord 2": "D3 E3 F3 A3 B3 C4",
256
+ # "Hexachord 3": "E3 G3 A3 C4 D4 F4"}.get(x, "")
257
+ item = x[x.index('['):x.index(']')+1]
258
+ int_array = np.array(ast.literal_eval(item))
259
+ hexa_string = ''
260
+ start_note = note.Note('C3')
261
+ for i in int_array:
262
+ add_note = start_note.transpose(int(i))
263
+ hexa_string = hexa_string + ' ' + add_note.nameWithOctave
264
+ return hexa_string
265
+
266
+
267
+ def launch_app():
268
+ hex = HexachordApp()
269
+ hex.is_fsynth_installed()
270
+ hex.render()
271
+ if hex.on_huggingface:
272
+ hex.ui.launch(server_name="0.0.0.0", server_port=7860)
273
+ else:
274
+ hex.ui.launch()
275
 
276
 
277
+ launch_app()
 
 
 
 
 
278
 
app_in_progress.py ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ast
2
+ import shutil
3
+ import gradio as gr
4
+ import numpy as np
5
+ from matplotlib import pyplot as plt
6
+ from mido import Message, MidiFile, MidiTrack
7
+ import os
8
+ import subprocess
9
+ from music21 import converter, note, interval
10
+ from pydub import AudioSegment
11
+ import hexachords
12
+
13
+ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
14
+
15
+ class HexachordApp:
16
+
17
+ def __init__(self):
18
+ self._hexachord = hexachords.Hexachord()
19
+ self._hexachord_base_sequence = None
20
+ self.ui = None
21
+ self.on_huggingface = "HUGGINGFACE_SPACE" in os.environ
22
+
23
+ def is_fsynth_installed(self):
24
+ """ Check to make sure fluidsynth exists in the PATH """
25
+ for path in os.environ['PATH'].split(os.pathsep):
26
+ f = os.path.join(path, 'fluidsynth')
27
+ if os.path.exists(f) and os.access(f, os.X_OK):
28
+ print('fluidsynth is installed')
29
+ return True
30
+ print('fluidsynth is NOT installed')
31
+ return False
32
+
33
+ def generate_chords(self, note_names, itvl):
34
+ # Placeholder for your actual chord generation function
35
+ # Assuming hexachord is a list of MIDI note numbers
36
+ interval_21 = 'P5'
37
+ if itvl == 'fourth':
38
+ interval_21 = 'P4'
39
+ self._hexachord_base_sequence = self._hexachord.generate_chord_sequence(note_names, intrvl=interval_21)
40
+ return self._hexachord_base_sequence
41
+
42
+ def generate_realizations(self):
43
+ # returns 3 triples of midipath, piano roll, audio player
44
+ reals = self._hexachord.generate_3_chords_realizations(self._hexachord_base_sequence)
45
+ midi_path1 = self.create_midi(reals[0], "real1.mid")
46
+ piano_roll_path1 = self.generate_piano_roll(reals[0], "real1.png")
47
+ audio_path1 = self.convert_midi_to_audio(midi_path1, "real1")
48
+ midi_path2 = self.create_midi(reals[1], "real2.mid")
49
+ piano_roll_path2 = self.generate_piano_roll(reals[1], "real2.png")
50
+ audio_path2 = self.convert_midi_to_audio(midi_path2, "real2")
51
+ midi_path3 = self.create_midi(reals[2], "real3.mid")
52
+ piano_roll_path3 = self.generate_piano_roll(reals[2], "real3.png")
53
+ audio_path3 = self.convert_midi_to_audio(midi_path3, "real3")
54
+ return midi_path1, piano_roll_path1, audio_path1, midi_path2, piano_roll_path2, audio_path2, midi_path3, piano_roll_path3, audio_path3
55
+
56
+ def create_midi(self, chords, file_name):
57
+ mid = MidiFile()
58
+ track = MidiTrack()
59
+ mid.tracks.append(track)
60
+ delta_time = 480 * 4
61
+ for i_chord, chord in enumerate(chords):
62
+ for i, note in enumerate(chord):
63
+ if i == 0 and i_chord != 0:
64
+ track.append(Message('note_on', note=note.pitch.midi, velocity=64, time=1))
65
+ else:
66
+ track.append(Message('note_on', note=note.pitch.midi, velocity=64, time=0))
67
+ for i, note in enumerate(chord):
68
+ if i==0:
69
+ track.append(Message('note_off', note=note.pitch.midi, velocity=0, time=delta_time - 1))
70
+ else:
71
+ track.append(Message('note_off', note=note.pitch.midi, velocity=0, time=0))
72
+ midi_path = os.path.join(BASE_DIR, file_name)
73
+ mid.save(midi_path)
74
+ return midi_path
75
+
76
+
77
+ def convert_midi_to_audio(self, midi_path, file_name):
78
+ if not shutil.which("fluidsynth"):
79
+ try:
80
+ subprocess.run(["apt-get", "update"], check=True)
81
+ subprocess.run(["apt-get", "install", "-y", "fluidsynth"], check=True)
82
+ except Exception as e:
83
+ return f"Error installing Fluidsynth: {str(e)}"
84
+ wav_path = os.path.join(BASE_DIR, file_name + ".wav")
85
+ mp3_path = os.path.join(BASE_DIR, file_name + ".mp3")
86
+ soundfont_path = os.path.join(BASE_DIR, "soundfont.sf2")
87
+ if not os.path.exists(soundfont_path):
88
+ return "Error: SoundFont file not found. Please provide a valid .sf2 file."
89
+ try:
90
+ subprocess.run(["fluidsynth", "-ni", soundfont_path, midi_path, "-F", wav_path, "-r", "44100"], check=True)
91
+ AudioSegment.converter = "ffmpeg"
92
+ audio = AudioSegment.from_wav(wav_path)
93
+ audio.export(mp3_path, format="mp3")
94
+ return mp3_path
95
+ except Exception as e:
96
+ return f"Error converting MIDI to audio: {str(e)}"
97
+
98
+ def generate_png(self, midi_file, output_file_name):
99
+ return self._hexachord.midi_to_svg_file(midi_file, output_file_name)
100
+
101
+ def generate_piano_roll(self, chords, label):
102
+ fig, ax = plt.subplots(figsize=(8, 4))
103
+
104
+ for i, chord in enumerate(chords):
105
+ for note in chord:
106
+ ax.broken_barh([(i * 1, 0.8)], (note.pitch.midi - 0.4, 0.8), facecolors='blue')
107
+
108
+ ax.set_xlabel("Chord Progression")
109
+ ax.set_ylabel("MIDI Note Number")
110
+ min_min_chords = 128
111
+ max_max_chords = 0
112
+ for ch in chords:
113
+ for note in ch:
114
+ if note.pitch.midi < min_min_chords:
115
+ min_min_chords = note.pitch.midi
116
+ if note.pitch.midi > max_max_chords:
117
+ max_max_chords = note.pitch.midi
118
+ ax.set_yticks(range(min_min_chords, max_max_chords + 1, 2))
119
+ ax.set_xticks(range(len(chords)))
120
+ ax.set_xticklabels([f"Chord {i + 1}" for i in range(len(chords))])
121
+
122
+ plt.grid(True, linestyle='--', alpha=0.5)
123
+ plt.savefig(label)
124
+ return label
125
+
126
+ def launch_score_editor(self, midi_path):
127
+ try:
128
+ score = converter.parse(midi_path)
129
+ score.show('musicxml')
130
+ return "Opened MIDI file in the default score editor!"
131
+ except Exception as e:
132
+ return f"Error opening score editor: {str(e)}"
133
+
134
+ def process_hexachord(self, hexachord_str, itvl):
135
+ try:
136
+ notes = [note.Note(n) for n in hexachord_str.split()]
137
+ if len(notes) != 6 or len(set(notes)) != 6:
138
+ return "Please enter exactly 6 unique MIDI note numbers."
139
+ except ValueError:
140
+ return "Invalid input. Enter 6 MIDI note numbers separated by spaces."
141
+ chords = self.generate_chords(notes, itvl)
142
+ midi_path = self.create_midi(chords, "base_chords.mid")
143
+ #piano_roll_path = self.generate_piano_roll(chords, "base_chords.png")
144
+ score_path = self.generate_png (midi_path, "score_base_chords.png")
145
+ audio_path = self.convert_midi_to_audio(midi_path, "base_chords")
146
+
147
+ # return midi_path, piano_roll_path, audio_path
148
+ return midi_path, score_path, audio_path
149
+
150
+ def render(self):
151
+ with gr.Blocks() as ui:
152
+ gr.Markdown("# Hexachord-based Chord Generator")
153
+
154
+ with gr.Tabs():
155
+ with gr.TabItem("Hexachord Generator"):
156
+ with gr.Row():
157
+ hexachord_selector = gr.Dropdown(label="Select Known Hexachord",
158
+ choices=self.get_known_hexachords_choice(), value=None, interactive=True)
159
+ hexachord_input = gr.Textbox(
160
+ label="Enter 6 notes (pitchclass plus octave, separated by spaces)",
161
+ value="C3 D3 E3 G3 A3 B3",
162
+ interactive = True
163
+ )
164
+ interval_switch = gr.Radio(
165
+ choices=["fourth", "fifth"],
166
+ label="Select Interval",
167
+ value="fifth"
168
+ )
169
+ generate_button = gr.Button("Generate Chords")
170
+ midi_output = gr.File(label="Download MIDI File")
171
+ # piano_roll_output = gr.Image(label="Piano Roll Visualization")
172
+ score_output = gr.Image(label="Score Visualization")
173
+ audio_output = gr.Audio(label="Play Generated Chords", value=None, interactive=False)
174
+
175
+ hexachord_selector.change(
176
+ fn=self.get_selected_hexachord,
177
+ inputs=[hexachord_selector],
178
+ outputs=[hexachord_input]
179
+ )
180
+ # generate_button.click(
181
+ # fn=self.process_hexachord,
182
+ # inputs=[hexachord_selector, interval_switch],
183
+ # outputs=[midi_output, score_output, audio_output]
184
+ # )
185
+
186
+ generate_button.click(
187
+ fn=self.process_hexachord,
188
+ inputs=[hexachord_input, interval_switch],
189
+ # outputs=[midi_output, piano_roll_output, audio_output]
190
+ outputs = [midi_output, score_output, audio_output]
191
+ )
192
+ # Pressing Enter in the textbox also triggers processing
193
+ hexachord_input.submit(
194
+ fn=self.process_hexachord,
195
+ inputs=[hexachord_input, interval_switch],
196
+ outputs=[midi_output, score_output, audio_output]
197
+ )
198
+
199
+ with gr.TabItem("Alternative Chord Realizations"):
200
+ gr.Markdown("Alternative Chord Realizations")
201
+
202
+ realization_button = gr.Button("Generate Alternative Realizations")
203
+ realization_outputs = []
204
+
205
+ for i in range(3): # Three alternative realizations
206
+ with gr.Group():
207
+ gr.Markdown(f"#### Realization {i + 1}")
208
+ midi_output = gr.File(label="Download MIDI File")
209
+ piano_roll = gr.Image(label=f"Piano Roll {i + 1}")
210
+ audio_player = gr.Audio(label=f"Play Chords {i + 1}", interactive=False)
211
+ realization_outputs.append((midi_output, piano_roll, audio_player))
212
+
213
+ # Clicking the button triggers realization generation
214
+ realization_button.click(
215
+ fn=self.generate_realizations,
216
+ inputs=[],
217
+ outputs=[output for trip in realization_outputs for output in trip]
218
+ )
219
+
220
+ with gr.TabItem("Settings"):
221
+ gr.Markdown("### Configuration Options")
222
+ setting_1 = gr.Checkbox(label="Enable Advanced Mode")
223
+ setting_2 = gr.Slider(0, 100, label="Complexity Level")
224
+ self.ui = ui
225
+
226
+ def get_known_hexachords_choice(self):
227
+ return self._hexachord.known_hexachords
228
+
229
+ def get_selected_hexachord(self, x):
230
+ # lambda x: {"Hexachord 1": "C3 D3 E3 G3 A3 B3", "Hexachord 2": "D3 E3 F3 A3 B3 C4",
231
+ # "Hexachord 3": "E3 G3 A3 C4 D4 F4"}.get(x, "")
232
+ item = x[x.index('['):x.index(']')+1]
233
+ int_array = np.array(ast.literal_eval(item))
234
+ hexa_string = ''
235
+ start_note = note.Note('C3')
236
+ for i in int_array:
237
+ add_note = start_note.transpose(int(i))
238
+ hexa_string = hexa_string + ' ' + add_note.nameWithOctave
239
+ return hexa_string
240
+
241
+
242
+ def launch_app():
243
+ hex = HexachordApp()
244
+ hex.is_fsynth_installed()
245
+ hex.render()
246
+ if hex.on_huggingface:
247
+ hex.ui.launch(server_name="0.0.0.0", server_port=7860)
248
+ else:
249
+ hex.ui.launch()
250
+
251
+
252
+ launch_app()
253
+
essai.py CHANGED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import verovio
2
+ import os
3
+
4
+ def get_verovio_resource_path():
5
+ for path in verovio.__path__:
6
+ candidate = os.path.join(path, "data")
7
+ if os.path.exists(candidate):
8
+ return candidate
9
+ return None
10
+
11
+ def render_mei(mei_path):
12
+ tk = verovio.toolkit()
13
+ # Set resource path explicitly
14
+ resource_path = get_verovio_resource_path()
15
+ print("[Debug] Using resource path:", resource_path)
16
+ if resource_path:
17
+ try:
18
+ tk.setResourcePath(resource_path)
19
+ except Exception as e:
20
+ print("[Error] Failed to set resourcePath:", e)
21
+ # tk.setOptions({
22
+ # "font": "/app/data/Bravura", # Path to the font folder
23
+ # })
24
+ with open(mei_path, "r", encoding="utf-8") as f:
25
+ mei_data = f.read()
26
+ # tk.loadFile("path-to-mei-file")
27
+ # return tk.renderToSVG(1)
28
+
29
+ try:
30
+ tk.loadData(mei_data)
31
+ return tk.renderToSVG(1)
32
+ except Exception as e:
33
+ return f"<pre>Rendering failed:\n{e}</pre>"
34
+
35
+
36
+ string = render_mei("output.mei")
37
+ print(string)
format_conversions.py CHANGED
@@ -22,7 +22,7 @@ class Format_Converter:
22
  try:
23
  # Convert MIDI to MEI using music21
24
  musicxml = cls.midi_to_musicxml_string(midi_path)
25
- mei_str = cls.xml_to_mei_string(musicxml)
26
  # Load MEI and render SVG
27
  tk.loadData(mei_str)
28
  svg = tk.renderToSVG(1)
@@ -30,5 +30,18 @@ class Format_Converter:
30
  except Exception as e:
31
  return f"<p style='color:red'>Error: {e}</p>"
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  if __name__ == '__main__':
34
  svg_string = Format_Converter().midi_to_svg(verovio.toolkit(), 'output.mid')
 
22
  try:
23
  # Convert MIDI to MEI using music21
24
  musicxml = cls.midi_to_musicxml_string(midi_path)
25
+ mei_str = cls.xml_to_mei_string(tk, musicxml)
26
  # Load MEI and render SVG
27
  tk.loadData(mei_str)
28
  svg = tk.renderToSVG(1)
 
30
  except Exception as e:
31
  return f"<p style='color:red'>Error: {e}</p>"
32
 
33
+ @classmethod
34
+ def midi_to_svg_file(cls, tk, midi_path, output_file):
35
+ try:
36
+ # Convert MIDI to MEI using music21
37
+ musicxml = cls.midi_to_musicxml_string(midi_path)
38
+ mei_str = cls.xml_to_mei_string(tk, musicxml)
39
+ # Load MEI and render SVG
40
+ tk.loadData(mei_str)
41
+ svg = tk.renderToSVGFile(output_file)
42
+ return output_file
43
+ except Exception as e:
44
+ return f"<p style='color:red'>Error: {e}</p>"
45
+
46
  if __name__ == '__main__':
47
  svg_string = Format_Converter().midi_to_svg(verovio.toolkit(), 'output.mid')
old_app.py CHANGED
@@ -1,4 +1,5 @@
1
  import verovio
 
2
  import os
3
 
4
  def get_verovio_resource_path():
@@ -12,26 +13,26 @@ def render_mei(mei_path):
12
  tk = verovio.toolkit()
13
  # Set resource path explicitly
14
  resource_path = get_verovio_resource_path()
 
15
  print("[Debug] Using resource path:", resource_path)
16
  if resource_path:
17
  try:
18
  tk.setResourcePath(resource_path)
19
  except Exception as e:
20
  print("[Error] Failed to set resourcePath:", e)
21
- # tk.setOptions({
22
- # "font": "/app/data/Bravura", # Path to the font folder
23
- # })
 
24
  with open(mei_path, "r", encoding="utf-8") as f:
25
  mei_data = f.read()
26
- # tk.loadFile("path-to-mei-file")
27
- # return tk.renderToSVG(1)
28
-
29
  try:
30
  tk.loadData(mei_data)
31
- return tk.renderToSVG(1)
 
32
  except Exception as e:
33
  return f"<pre>Rendering failed:\n{e}</pre>"
34
 
35
-
36
- string = render_mei("output.mei")
37
- print(string)
 
1
  import verovio
2
+ import gradio as gr
3
  import os
4
 
5
  def get_verovio_resource_path():
 
13
  tk = verovio.toolkit()
14
  # Set resource path explicitly
15
  resource_path = get_verovio_resource_path()
16
+ print(f"resource path: {os.listdir(resource_path)}")
17
  print("[Debug] Using resource path:", resource_path)
18
  if resource_path:
19
  try:
20
  tk.setResourcePath(resource_path)
21
  except Exception as e:
22
  print("[Error] Failed to set resourcePath:", e)
23
+ # print(tk.getOptions())
24
+ options = {"pageHeight": 2000, "pageWidth": 3000, "scale": 60, "header": "none"}
25
+ tk.setOptions(options)
26
+ tk.redoLayout()
27
  with open(mei_path, "r", encoding="utf-8") as f:
28
  mei_data = f.read()
 
 
 
29
  try:
30
  tk.loadData(mei_data)
31
+ res = tk.renderToSVG(1)
32
+ return res
33
  except Exception as e:
34
  return f"<pre>Rendering failed:\n{e}</pre>"
35
 
36
+ gr.Interface(fn=render_mei, inputs=gr.File(type="filepath"), outputs=gr.HTML()).launch(
37
+ server_name="0.0.0.0", server_port=7860
38
+ )
requirements.txt CHANGED
@@ -1,2 +1,5 @@
1
  verovio
2
- music21
 
 
 
 
1
  verovio
2
+ music21
3
+ gradio
4
+ mido
5
+ ortools
working_midi_to_svg_app.py ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import gradio as gr
3
+ import verovio
4
+
5
+ from format_conversions import Format_Converter
6
+
7
+ def get_verovio_resource_path():
8
+ for path in verovio.__path__:
9
+ candidate = os.path.join(path, "data")
10
+ if os.path.exists(candidate):
11
+ return candidate
12
+ return None
13
+
14
+ # Initialize the Verovio toolkit with rendering options
15
+ tk = verovio.toolkit()
16
+ resource_path = get_verovio_resource_path()
17
+ print(f"resource path: {os.listdir(resource_path)}")
18
+ print("[Debug] Using resource path:", resource_path)
19
+ if resource_path:
20
+ try:
21
+ tk.setResourcePath(resource_path)
22
+ except Exception as e:
23
+ print("[Error] Failed to set resourcePath:", e)
24
+ tk.setOptions({
25
+ "scale": 40,
26
+ "adjustPageHeight": True,
27
+ "landscape": False,
28
+ })
29
+
30
+ # Initialize Verovio toolkit
31
+ tk = verovio.toolkit()
32
+ tk.setOptions({
33
+ "scale": 40,
34
+ "adjustPageHeight": True
35
+ })
36
+
37
+ def midi_to_svg(midi_path):
38
+ return Format_Converter().midi_to_svg(tk, midi_path)
39
+
40
+ # Gradio UI
41
+ with gr.Blocks() as demo:
42
+ gr.Markdown("## 🎼 MIDI to Score Viewer (via MEI)")
43
+
44
+ midi_input = gr.File(label="Upload a MIDI file", file_types=[".mid", ".midi"])
45
+ render_btn = gr.Button("Convert & Render")
46
+ score_display = gr.HTML(label="Score Output")
47
+
48
+ render_btn.click(fn=midi_to_svg, inputs=midi_input, outputs=score_display)
49
+
50
+ # Smart launch settings
51
+ if os.getenv("SPACE_ID"):
52
+ demo.launch()
53
+ else:
54
+ # demo.launch(share=False, inbrowser=True)
55
+ demo.launch(server_name="0.0.0.0", server_port=7860)
56
+