pachet commited on
Commit
d877082
·
1 Parent(s): 57b79b0

Updated app.py and format_conversions.py

Browse files
Files changed (2) hide show
  1. app.py +2 -0
  2. format_conversions.py +100 -2
app.py CHANGED
@@ -89,11 +89,13 @@ class HexachordApp:
89
  for i, note in enumerate(chord):
90
  if i == 0 and i_chord != 0:
91
  track.append(Message('note_on', note=note.pitch.midi, velocity=64, time=1))
 
92
  else:
93
  track.append(Message('note_on', note=note.pitch.midi, velocity=64, time=0))
94
  for i, note in enumerate(chord):
95
  if i==0:
96
  track.append(Message('note_off', note=note.pitch.midi, velocity=0, time=delta_time - 1))
 
97
  else:
98
  track.append(Message('note_off', note=note.pitch.midi, velocity=0, time=0))
99
  midi_path = os.path.join(BASE_DIR, file_name)
 
89
  for i, note in enumerate(chord):
90
  if i == 0 and i_chord != 0:
91
  track.append(Message('note_on', note=note.pitch.midi, velocity=64, time=1))
92
+ # track.append(Message('note_on', note=note.pitch.midi, velocity=64, time=0))
93
  else:
94
  track.append(Message('note_on', note=note.pitch.midi, velocity=64, time=0))
95
  for i, note in enumerate(chord):
96
  if i==0:
97
  track.append(Message('note_off', note=note.pitch.midi, velocity=0, time=delta_time - 1))
98
+ # track.append(Message('note_off', note=note.pitch.midi, velocity=0, time=delta_time))
99
  else:
100
  track.append(Message('note_off', note=note.pitch.midi, velocity=0, time=0))
101
  midi_path = os.path.join(BASE_DIR, file_name)
format_conversions.py CHANGED
@@ -1,8 +1,105 @@
1
- from music21 import converter, musicxml
 
2
  import verovio
3
 
 
4
  class Format_Converter:
5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
  @classmethod
7
  def midi_to_musicxml_string(cls, midi_path):
8
  # Parse MIDI file into a music21 stream
@@ -30,11 +127,12 @@ class Format_Converter:
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)
 
1
+ import copy
2
+ from music21 import converter, musicxml, stream, note, chord, clef
3
  import verovio
4
 
5
+
6
  class Format_Converter:
7
 
8
+ def midi_to_musicxml_string_two_staves(midi_path, split_pitch=60):
9
+ score = converter.parse(midi_path)
10
+ merged = score.flatten()
11
+ treble = stream.Part()
12
+ bass = stream.Part()
13
+ treble.insert(0, clef.TrebleClef())
14
+ bass.insert(0, clef.BassClef())
15
+
16
+ for el in merged.recurse().notesAndRests:
17
+ true_offset = el.getOffsetInHierarchy(score)
18
+
19
+ if isinstance(el, note.Rest):
20
+ treble.insert(true_offset, copy.deepcopy(el))
21
+ bass.insert(true_offset, copy.deepcopy(el))
22
+
23
+ elif isinstance(el, note.Note):
24
+ target = treble if el.pitch.midi >= split_pitch else bass
25
+ target.insert(true_offset, copy.deepcopy(el))
26
+
27
+ elif isinstance(el, chord.Chord):
28
+ treble_pitches = [p for p in el.pitches if p.midi >= split_pitch]
29
+ bass_pitches = [p for p in el.pitches if p.midi < split_pitch]
30
+
31
+ if treble_pitches:
32
+ treble_chord = chord.Chord(treble_pitches)
33
+ treble_chord.quarterLength = el.quarterLength
34
+ treble_chord.volume = el.volume
35
+ treble.insert(true_offset, treble_chord)
36
+
37
+ if bass_pitches:
38
+ bass_chord = chord.Chord(bass_pitches)
39
+ bass_chord.quarterLength = el.quarterLength
40
+ bass_chord.volume = el.volume
41
+ bass.insert(true_offset, bass_chord)
42
+
43
+ final_score = stream.Score()
44
+ final_score.insert(0, treble) # order: top staff first
45
+ final_score.insert(0, bass)
46
+
47
+ exporter = musicxml.m21ToXml.GeneralObjectExporter(final_score)
48
+ musicxml_str = exporter.parse().decode('utf-8')
49
+ return musicxml_str
50
+
51
+ @classmethod
52
+ def midi_to_musicxml_string_two_staves_old(cls, midi_path):
53
+ # Parse MIDI file into a music21 stream
54
+ score = converter.parse(midi_path)
55
+ score = score.flattenParts()
56
+ # Create a new Score with two staves: Treble and Bass
57
+ treble = stream.Part()
58
+ bass = stream.Part()
59
+ treble.id = 'Treble'
60
+ bass.id = 'Bass'
61
+ # Assign clefs (optional but recommended)
62
+ from music21 import clef
63
+ treble.insert(0, clef.TrebleClef())
64
+ bass.insert(0, clef.BassClef())
65
+ split_pitch = 60
66
+ # Split notes by pitch (simple heuristic)
67
+ treble_voice = stream.Voice()
68
+ bass_voice = stream.Voice()
69
+ for el in score.recurse().notesAndRests:
70
+ true_offset = el.getOffsetInHierarchy(score)
71
+ if isinstance(el, note.Rest):
72
+ # Send same rest to both staves (or adjust as needed)
73
+ treble.insert(true_offset, el)
74
+ bass.insert(true_offset, el)
75
+
76
+ elif isinstance(el, note.Note):
77
+ target = treble if el.pitch.midi >= split_pitch else bass
78
+ target.insert(true_offset, el)
79
+
80
+ elif isinstance(el, chord.Chord):
81
+ # Split the chord into individual notes
82
+ for p in el.pitches:
83
+ n = note.Note(p)
84
+ n.quarterLength = el.quarterLength
85
+ n.volume = el.volume
86
+ if p.midi >= split_pitch:
87
+ treble_voice.insert(true_offset, n)
88
+ else:
89
+ bass_voice.insert(true_offset, n)
90
+ # Combine the two parts into a score
91
+ treble.append(treble_voice)
92
+ bass.append(bass_voice)
93
+ new_score = stream.Score()
94
+ new_score.insert(0, treble)
95
+ new_score.insert(0, bass)
96
+ # staff_group = StaffGroup([bass, treble], symbol='brace', barTogether=True)
97
+ # new_score.insert(0, staff_group)
98
+ # Export to MusicXML string
99
+ exporter = musicxml.m21ToXml.GeneralObjectExporter(new_score)
100
+ musicxml_str = exporter.parse().decode('utf-8') # parse() returns bytes
101
+ return musicxml_str
102
+
103
  @classmethod
104
  def midi_to_musicxml_string(cls, midi_path):
105
  # Parse MIDI file into a music21 stream
 
127
  except Exception as e:
128
  return f"<p style='color:red'>Error: {e}</p>"
129
 
130
+
131
  @classmethod
132
  def midi_to_svg_file(cls, tk, midi_path, output_file):
133
  try:
134
  # Convert MIDI to MEI using music21
135
+ musicxml = cls.midi_to_musicxml_string_two_staves(midi_path)
136
  mei_str = cls.xml_to_mei_string(tk, musicxml)
137
  # Load MEI and render SVG
138
  tk.loadData(mei_str)