Spaces:
Sleeping
Sleeping
Update app and hexachord
Browse files- app.py +26 -15
- hexachords.py +0 -43
app.py
CHANGED
|
@@ -32,10 +32,13 @@ if resource_path:
|
|
| 32 |
except Exception as e:
|
| 33 |
print("[Error] Failed to set resourcePath:", e)
|
| 34 |
tk.setOptions({
|
| 35 |
-
"
|
|
|
|
|
|
|
| 36 |
"adjustPageHeight": True,
|
| 37 |
"landscape": False,
|
| 38 |
})
|
|
|
|
| 39 |
|
| 40 |
class HexachordApp:
|
| 41 |
|
|
@@ -155,9 +158,24 @@ class HexachordApp:
|
|
| 155 |
except Exception as e:
|
| 156 |
return f"Error opening score editor: {str(e)}"
|
| 157 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
def process_hexachord(self, hexachord_str, itvl):
|
| 159 |
try:
|
| 160 |
-
notes = [note.Note(n) for n in hexachord_str.split()]
|
|
|
|
| 161 |
if len(notes) != 6 or len(set(notes)) != 6:
|
| 162 |
return "Please enter exactly 6 unique MIDI note numbers."
|
| 163 |
except ValueError:
|
|
@@ -174,7 +192,6 @@ class HexachordApp:
|
|
| 174 |
def render(self):
|
| 175 |
with gr.Blocks() as ui:
|
| 176 |
gr.Markdown("# Hexachord-based Chord Generator")
|
| 177 |
-
|
| 178 |
with gr.Tabs():
|
| 179 |
with gr.TabItem("Hexachord Generator"):
|
| 180 |
with gr.Row():
|
|
@@ -182,7 +199,7 @@ class HexachordApp:
|
|
| 182 |
choices=self.get_known_hexachords_choice(), value=None, interactive=True)
|
| 183 |
hexachord_input = gr.Textbox(
|
| 184 |
label="Enter 6 notes (pitchclass plus octave, separated by spaces)",
|
| 185 |
-
value="
|
| 186 |
interactive = True
|
| 187 |
)
|
| 188 |
interval_switch = gr.Radio(
|
|
@@ -191,22 +208,16 @@ class HexachordApp:
|
|
| 191 |
value="fifth"
|
| 192 |
)
|
| 193 |
generate_button = gr.Button("Generate Chords")
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
hexachord_selector.change(
|
| 200 |
fn=self.get_selected_hexachord,
|
| 201 |
inputs=[hexachord_selector],
|
| 202 |
outputs=[hexachord_input]
|
| 203 |
)
|
| 204 |
-
# generate_button.click(
|
| 205 |
-
# fn=self.process_hexachord,
|
| 206 |
-
# inputs=[hexachord_selector, interval_switch],
|
| 207 |
-
# outputs=[midi_output, score_output, audio_output]
|
| 208 |
-
# )
|
| 209 |
-
|
| 210 |
generate_button.click(
|
| 211 |
fn=self.process_hexachord,
|
| 212 |
inputs=[hexachord_input, interval_switch],
|
|
|
|
| 32 |
except Exception as e:
|
| 33 |
print("[Error] Failed to set resourcePath:", e)
|
| 34 |
tk.setOptions({
|
| 35 |
+
"adjustPageWidth": True,
|
| 36 |
+
"header": 'none', # This disables the rendering of the title
|
| 37 |
+
"scale": 70,
|
| 38 |
"adjustPageHeight": True,
|
| 39 |
"landscape": False,
|
| 40 |
})
|
| 41 |
+
print(tk.getOptions())
|
| 42 |
|
| 43 |
class HexachordApp:
|
| 44 |
|
|
|
|
| 158 |
except Exception as e:
|
| 159 |
return f"Error opening score editor: {str(e)}"
|
| 160 |
|
| 161 |
+
def build_octave_dependent_notes_from_string(self, note_string):
|
| 162 |
+
start_octave = 3
|
| 163 |
+
notes = []
|
| 164 |
+
previous_note = None
|
| 165 |
+
for nn in note_string.split():
|
| 166 |
+
n = note.Note(nn)
|
| 167 |
+
n.octave = start_octave
|
| 168 |
+
if previous_note is not None and n.pitch.midi < previous_note.pitch.midi:
|
| 169 |
+
n.octave = n.octave + 1
|
| 170 |
+
start_octave += 1
|
| 171 |
+
notes.append(n)
|
| 172 |
+
previous_note = n
|
| 173 |
+
return notes
|
| 174 |
+
|
| 175 |
def process_hexachord(self, hexachord_str, itvl):
|
| 176 |
try:
|
| 177 |
+
# notes = [note.Note(n) for n in hexachord_str.split()]
|
| 178 |
+
notes = self.build_octave_dependent_notes_from_string(hexachord_str)
|
| 179 |
if len(notes) != 6 or len(set(notes)) != 6:
|
| 180 |
return "Please enter exactly 6 unique MIDI note numbers."
|
| 181 |
except ValueError:
|
|
|
|
| 192 |
def render(self):
|
| 193 |
with gr.Blocks() as ui:
|
| 194 |
gr.Markdown("# Hexachord-based Chord Generator")
|
|
|
|
| 195 |
with gr.Tabs():
|
| 196 |
with gr.TabItem("Hexachord Generator"):
|
| 197 |
with gr.Row():
|
|
|
|
| 199 |
choices=self.get_known_hexachords_choice(), value=None, interactive=True)
|
| 200 |
hexachord_input = gr.Textbox(
|
| 201 |
label="Enter 6 notes (pitchclass plus octave, separated by spaces)",
|
| 202 |
+
value="C D E G A B",
|
| 203 |
interactive = True
|
| 204 |
)
|
| 205 |
interval_switch = gr.Radio(
|
|
|
|
| 208 |
value="fifth"
|
| 209 |
)
|
| 210 |
generate_button = gr.Button("Generate Chords")
|
| 211 |
+
with gr.Row():
|
| 212 |
+
midi_output = gr.File(label="Download MIDI File", scale=1)
|
| 213 |
+
# piano_roll_output = gr.Image(label="Piano Roll Visualization")
|
| 214 |
+
score_output = gr.Image(label="Score Visualization", scale=3)
|
| 215 |
+
audio_output = gr.Audio(label="Play Generated Chords", value=None, interactive=False, scale=3)
|
| 216 |
hexachord_selector.change(
|
| 217 |
fn=self.get_selected_hexachord,
|
| 218 |
inputs=[hexachord_selector],
|
| 219 |
outputs=[hexachord_input]
|
| 220 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 221 |
generate_button.click(
|
| 222 |
fn=self.process_hexachord,
|
| 223 |
inputs=[hexachord_input, interval_switch],
|
hexachords.py
CHANGED
|
@@ -1,10 +1,5 @@
|
|
| 1 |
-
import os
|
| 2 |
-
import subprocess
|
| 3 |
from music21 import note, stream, interval, meter, chord, converter, metadata
|
| 4 |
from ortools.sat.python import cp_model
|
| 5 |
-
from verovio import verovio
|
| 6 |
-
|
| 7 |
-
from format_conversions import Format_Converter
|
| 8 |
|
| 9 |
|
| 10 |
class Hexachord:
|
|
@@ -163,44 +158,6 @@ class Hexachord:
|
|
| 163 |
|
| 164 |
return optimized_chords
|
| 165 |
|
| 166 |
-
def midi_to_svg_file(self, midi_file, output_file):
|
| 167 |
-
# score = converter.parse(midi_file)
|
| 168 |
-
# musicxml_data = score.write('musicxml') # Get MusicXML as a string
|
| 169 |
-
# # Step 2: Load MusicXML into Verovio
|
| 170 |
-
# tk = verovio.toolkit()
|
| 171 |
-
# tk.loadData(musicxml_data.encode()) # Convert to bytes and load into Verovio
|
| 172 |
-
# tk.renderToSVGFile(output_file, 1)
|
| 173 |
-
|
| 174 |
-
tk = verovio.toolkit()
|
| 175 |
-
fm = Format_Converter()
|
| 176 |
-
musicxml = fm.midi_to_musicxml_string(midi_file)
|
| 177 |
-
mei_str = fm.xml_to_mei_string(tk, musicxml)
|
| 178 |
-
# Load MEI and render SVG
|
| 179 |
-
tk.loadData(mei_str)
|
| 180 |
-
svg = tk.renderToSVGFile(output_file)
|
| 181 |
-
return output_file
|
| 182 |
-
|
| 183 |
-
def midi_to_svg(self, midi_file, svg_output):
|
| 184 |
-
"""Convert MIDI to SVG using Verovio's command-line tool."""
|
| 185 |
-
score = converter.parse(midi_file)
|
| 186 |
-
score.metadata = metadata.Metadata()
|
| 187 |
-
score.metadata.title = ''
|
| 188 |
-
musicxml_path = "temp.musicxml"
|
| 189 |
-
score.write('musicxml', fp=musicxml_path)
|
| 190 |
-
# Run Verovio via command line (since Python API fails)
|
| 191 |
-
verovio_executable = "verovio/build/verovio" # Ensure correct path
|
| 192 |
-
if not os.path.exists(verovio_executable):
|
| 193 |
-
return "Error: Verovio binary not found!"
|
| 194 |
-
# Run Verovio with the full path
|
| 195 |
-
command = f"{verovio_executable} {musicxml_path} -o {svg_output} --smufl-text-font embedded --scale 50 --page-width 1000 --page-height 500 --footer none"
|
| 196 |
-
result = subprocess.run(command, shell=True, capture_output=True, text=True)
|
| 197 |
-
|
| 198 |
-
if result.returncode != 0:
|
| 199 |
-
print("Verovio Error:", result.stderr)
|
| 200 |
-
return f"Error running Verovio: {result.stderr}"
|
| 201 |
-
return svg_output
|
| 202 |
-
|
| 203 |
-
|
| 204 |
if __name__ == '__main__':
|
| 205 |
hexa = Hexachord()
|
| 206 |
note_names = ["C3", "Eb3", "E3", "F#3", "G3", "Bb3"]
|
|
|
|
|
|
|
|
|
|
| 1 |
from music21 import note, stream, interval, meter, chord, converter, metadata
|
| 2 |
from ortools.sat.python import cp_model
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
|
| 5 |
class Hexachord:
|
|
|
|
| 158 |
|
| 159 |
return optimized_chords
|
| 160 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 161 |
if __name__ == '__main__':
|
| 162 |
hexa = Hexachord()
|
| 163 |
note_names = ["C3", "Eb3", "E3", "F#3", "G3", "Bb3"]
|