awacke1 commited on
Commit
b50d4ec
·
verified ·
1 Parent(s): 784b974

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +61 -83
app.py CHANGED
@@ -11,63 +11,72 @@ from midi_synthesizer import MidiSynthesizer
11
 
12
  MAX_SEED = np.iinfo(np.int32).max
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  class MIDIManager:
15
  def __init__(self):
16
  self.soundfont_path = hf_hub_download(repo_id="skytnt/midi-model", filename="soundfont.sf2")
17
  self.synthesizer = MidiSynthesizer(self.soundfont_path)
18
- self.loaded_midi = {} # Key: midi_id, Value: (file_path, midi_data)
19
  self.modified_files = []
20
  self.is_playing = False
21
- self.midi_in = rtmidi.MidiIn()
22
- self.midi_in.open_port(0) if self.midi_in.get_ports() else None
23
- self.midi_in.set_callback(self.midi_callback)
24
- self.live_notes = []
25
  self.example_files = self.load_example_midis()
26
 
27
  def load_example_midis(self):
28
- # Check for MIDI files in a local 'examples' directory or predefined paths
29
- example_dir = "examples" # Adjust this path as needed
30
  examples = {}
 
31
  if os.path.exists(example_dir):
32
  for file in os.listdir(example_dir):
33
  if file.endswith(".mid") or file.endswith(".midi"):
34
  midi_id = f"example_{len(examples)}"
35
  file_path = os.path.join(example_dir, file)
36
  examples[midi_id] = (file_path, MIDI.load(file_path))
37
- # Add a default example if none found
38
  if not examples:
39
  midi = MIDI.MIDIFile(1)
40
  midi.addTrack()
41
- midi.addNote(0, 0, 60, 0, 100, 100) # C4 note
42
  examples["example_0"] = ("Simple C4.mid", midi)
43
  return examples
44
 
45
- def midi_callback(self, event, data=None):
46
- message, _ = event
47
- if len(message) >= 3 and message[0] & 0xF0 == 0x90: # Note On
48
- note, velocity = message[1], message[2]
49
- if velocity > 0:
50
- self.live_notes.append((note, velocity, 0))
51
-
52
  def load_midi(self, file_path):
53
  midi = MIDI.load(file_path)
54
  midi_id = f"midi_{len(self.loaded_midi) - len(self.example_files)}"
55
  self.loaded_midi[midi_id] = (file_path, midi)
56
  return midi_id
57
 
58
- def extract_notes(self, midi):
59
  notes = []
 
60
  for track in midi.tracks:
61
  for event in track.events:
62
  if event.type == 'note_on' and event.velocity > 0:
63
  notes.append((event.note, event.velocity, event.time))
64
- return notes
 
 
65
 
66
  def generate_variation(self, midi_id, length_factor=2, variation=0.3):
67
  if midi_id not in self.loaded_midi:
68
  return None
69
  _, midi = self.loaded_midi[midi_id]
70
- notes = self.extract_notes(midi)
71
  new_notes = []
72
  for _ in range(int(length_factor)):
73
  for note, vel, time in notes:
@@ -78,10 +87,12 @@ class MIDIManager:
78
  else:
79
  new_notes.append((note, vel, time))
80
 
81
- new_midi = MIDI.MIDIFile(1)
82
- new_midi.addTrack()
83
- for note, vel, time in new_notes:
84
- new_midi.addNote(0, 0, note, time, 100, vel)
 
 
85
 
86
  output = io.BytesIO()
87
  new_midi.writeFile(output)
@@ -113,60 +124,40 @@ class MIDIManager:
113
  self.is_playing = False
114
  return "Stopping..."
115
 
116
- def save_live_midi(self):
117
- if not self.live_notes:
118
- return None
119
- midi = MIDI.MIDIFile(1)
120
- midi.addTrack()
121
- time_cum = 0
122
- for note, vel, _ in self.live_notes:
123
- midi.addNote(0, 0, note, time_cum, 100, vel)
124
- time_cum += 100
125
- output = io.BytesIO()
126
- midi.writeFile(output)
127
- midi_data = base64.b64encode(output.getvalue()).decode('utf-8')
128
- self.modified_files.append(midi_data)
129
- self.live_notes = []
130
- return midi_data
131
-
132
- midi_manager = MIDIManager()
133
 
134
  def create_download_list():
135
  html = "<h3>Downloads</h3><ul>"
136
- for i, data in enumerate(midi_manager.modified_files):
137
  html += f'<li><a href="data:audio/midi;base64,{data}" download="midi_{i}.mid">MIDI {i}</a></li>'
138
  html += "</ul>"
139
  return html
140
 
141
  def get_midi_choices():
142
- return [(os.path.basename(path), midi_id) for midi_id, (path, _) in midi_manager.loaded_midi.items()]
143
 
144
  with gr.Blocks(theme=gr.themes.Soft()) as app:
145
  gr.Markdown("<h1>🎵 MIDI Composer 🎵</h1>")
146
 
147
  with gr.Tabs():
148
- # Tab 1: Load MIDI Files
149
- with gr.Tab("Load MIDI"):
150
  midi_files = gr.File(label="Upload MIDI Files", file_count="multiple")
151
- midi_list = gr.State({})
152
- file_display = gr.HTML(value="No files loaded")
153
  output = gr.Audio(label="Generated Preview", type="bytes", autoplay=True)
154
 
155
  def load_and_generate(files):
156
- midi_list_val = midi_manager.loaded_midi.copy()
157
  html = "<h3>Loaded Files</h3>"
158
  midi_data = None
159
  for file in files or []:
160
- midi_id = midi_manager.load_midi(file.name)
161
- midi_list_val[midi_id] = (file.name, midi_manager.loaded_midi[midi_id][1])
162
- html += f"<div>{file.name}</div>"
163
- midi_data = midi_manager.generate_variation(midi_id)
164
- return (midi_list_val, html,
165
- io.BytesIO(base64.b64decode(midi_data)) if midi_data else None,
166
- get_midi_choices())
167
 
168
  midi_files.change(load_and_generate, inputs=[midi_files],
169
- outputs=[midi_list, file_display, output, gr.State(get_midi_choices())])
170
 
171
  # Tab 2: Generate & Perform
172
  with gr.Tab("Generate & Perform"):
@@ -174,59 +165,46 @@ with gr.Blocks(theme=gr.themes.Soft()) as app:
174
  length_factor = gr.Slider(1, 5, value=2, step=1, label="Length Factor")
175
  variation = gr.Slider(0, 1, value=0.3, label="Variation")
176
  generate_btn = gr.Button("Generate")
177
- effect = gr.Radio(["tempo"], label="Effect", value="tempo")
178
- intensity = gr.Slider(0, 1, value=0.5, label="Intensity")
179
  apply_btn = gr.Button("Apply Effect")
180
  stop_btn = gr.Button("Stop Playback")
181
  output = gr.Audio(label="Preview", type="bytes", autoplay=True)
182
  status = gr.Textbox(label="Status", value="Ready")
 
 
 
 
 
183
 
184
- def update_dropdown(midi_list):
185
- return gr.update(choices=get_midi_choices())
186
-
187
- midi_list.change(update_dropdown, inputs=[midi_list], outputs=[midi_select])
188
 
189
  def generate(midi_id, length, var):
190
  if not midi_id:
191
  return None, "Select a MIDI file"
192
- midi_data = midi_manager.generate_variation(midi_id, length, var)
193
- midi_manager.play_with_loop(midi_data)
194
  return io.BytesIO(base64.b64decode(midi_data)), "Playing"
195
 
196
  def apply_effect(midi_data, fx, inten):
197
  if not midi_data:
198
  return None, "Generate a MIDI first"
199
- new_data = midi_manager.apply_synth_effect(midi_data.decode('utf-8'), fx, inten)
200
- midi_manager.play_with_loop(new_data)
201
  return io.BytesIO(base64.b64decode(new_data)), "Playing"
202
 
203
  generate_btn.click(generate, inputs=[midi_select, length_factor, variation],
204
  outputs=[output, status])
205
  apply_btn.click(apply_effect, inputs=[output, effect, intensity],
206
  outputs=[output, status])
207
- stop_btn.click(midi_manager.stop_playback, inputs=None, outputs=[status])
208
-
209
- # Tab 3: MIDI Input
210
- with gr.Tab("MIDI Input"):
211
- gr.Markdown("Play your MIDI keyboard to record notes")
212
- save_btn = gr.Button("Save Live MIDI")
213
- live_output = gr.Audio(label="Live MIDI", type="bytes", autoplay=True)
214
-
215
- def save_live():
216
- midi_data = midi_manager.save_live_midi()
217
- if midi_data:
218
- midi_manager.play_with_loop(midi_data)
219
- return io.BytesIO(base64.b64decode(midi_data))
220
- return None
221
-
222
- save_btn.click(save_live, inputs=None, outputs=[live_output])
223
 
224
- # Tab 4: Downloads
225
  with gr.Tab("Downloads"):
226
  downloads = gr.HTML(value="No files yet")
227
  def update_downloads(*args):
228
  return create_download_list()
229
- gr.on(triggers=[midi_files.change, generate_btn.click, apply_btn.click, save_btn.click],
230
  fn=update_downloads, inputs=None, outputs=[downloads])
231
 
232
  gr.Markdown("""
 
11
 
12
  MAX_SEED = np.iinfo(np.int32).max
13
 
14
+ # Example song data (simplified from original)
15
+ SONG_DATA = {
16
+ "title": "Do You Believe in Love",
17
+ "progression": ["G", "D", "Em", "C"],
18
+ "lyrics": ["I was walking down a one-way street", "Just a-looking for someone to meet"]
19
+ }
20
+
21
+ class MIDIDeviceManager:
22
+ def __init__(self):
23
+ self.midiout = rtmidi.MidiOut()
24
+ self.midiin = rtmidi.MidiIn()
25
+
26
+ def get_available_devices(self):
27
+ return self.midiout.get_ports() or ["No MIDI devices"]
28
+
29
+ def get_device_info(self):
30
+ devices = self.get_available_devices()
31
+ return "\n".join([f"Port {i}: {name}" for i, name in enumerate(devices)]) if devices else "No MIDI devices detected"
32
+
33
  class MIDIManager:
34
  def __init__(self):
35
  self.soundfont_path = hf_hub_download(repo_id="skytnt/midi-model", filename="soundfont.sf2")
36
  self.synthesizer = MidiSynthesizer(self.soundfont_path)
37
+ self.loaded_midi = {} # midi_id: (file_path, midi_obj)
38
  self.modified_files = []
39
  self.is_playing = False
 
 
 
 
40
  self.example_files = self.load_example_midis()
41
 
42
  def load_example_midis(self):
 
 
43
  examples = {}
44
+ example_dir = "examples"
45
  if os.path.exists(example_dir):
46
  for file in os.listdir(example_dir):
47
  if file.endswith(".mid") or file.endswith(".midi"):
48
  midi_id = f"example_{len(examples)}"
49
  file_path = os.path.join(example_dir, file)
50
  examples[midi_id] = (file_path, MIDI.load(file_path))
 
51
  if not examples:
52
  midi = MIDI.MIDIFile(1)
53
  midi.addTrack()
54
+ midi.addNote(0, 0, 60, 0, 100, 100) # Default C4
55
  examples["example_0"] = ("Simple C4.mid", midi)
56
  return examples
57
 
 
 
 
 
 
 
 
58
  def load_midi(self, file_path):
59
  midi = MIDI.load(file_path)
60
  midi_id = f"midi_{len(self.loaded_midi) - len(self.example_files)}"
61
  self.loaded_midi[midi_id] = (file_path, midi)
62
  return midi_id
63
 
64
+ def extract_notes_and_instruments(self, midi):
65
  notes = []
66
+ instruments = set()
67
  for track in midi.tracks:
68
  for event in track.events:
69
  if event.type == 'note_on' and event.velocity > 0:
70
  notes.append((event.note, event.velocity, event.time))
71
+ if hasattr(event, 'program'):
72
+ instruments.add(event.program)
73
+ return notes, list(instruments)
74
 
75
  def generate_variation(self, midi_id, length_factor=2, variation=0.3):
76
  if midi_id not in self.loaded_midi:
77
  return None
78
  _, midi = self.loaded_midi[midi_id]
79
+ notes, instruments = self.extract_notes_and_instruments(midi)
80
  new_notes = []
81
  for _ in range(int(length_factor)):
82
  for note, vel, time in notes:
 
87
  else:
88
  new_notes.append((note, vel, time))
89
 
90
+ new_midi = MIDI.MIDIFile(len(instruments) or 1)
91
+ for i, inst in enumerate(instruments or [0]):
92
+ new_midi.addTrack()
93
+ new_midi.addProgramChange(i, 0, 0, inst)
94
+ for note, vel, time in new_notes:
95
+ new_midi.addNote(i, 0, note, time, 100, vel)
96
 
97
  output = io.BytesIO()
98
  new_midi.writeFile(output)
 
124
  self.is_playing = False
125
  return "Stopping..."
126
 
127
+ midi_manager = MIDIDeviceManager()
128
+ midi_processor = MIDIManager()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
129
 
130
  def create_download_list():
131
  html = "<h3>Downloads</h3><ul>"
132
+ for i, data in enumerate(midi_processor.modified_files):
133
  html += f'<li><a href="data:audio/midi;base64,{data}" download="midi_{i}.mid">MIDI {i}</a></li>'
134
  html += "</ul>"
135
  return html
136
 
137
  def get_midi_choices():
138
+ return [(os.path.basename(path), midi_id) for midi_id, (path, _) in midi_processor.loaded_midi.items()]
139
 
140
  with gr.Blocks(theme=gr.themes.Soft()) as app:
141
  gr.Markdown("<h1>🎵 MIDI Composer 🎵</h1>")
142
 
143
  with gr.Tabs():
144
+ # Tab 1: Upload MIDI
145
+ with gr.Tab("Upload MIDI"):
146
  midi_files = gr.File(label="Upload MIDI Files", file_count="multiple")
147
+ loaded_display = gr.HTML(value="No files loaded")
 
148
  output = gr.Audio(label="Generated Preview", type="bytes", autoplay=True)
149
 
150
  def load_and_generate(files):
 
151
  html = "<h3>Loaded Files</h3>"
152
  midi_data = None
153
  for file in files or []:
154
+ midi_id = midi_processor.load_midi(file.name)
155
+ html += f"<div>{file.name} <button onclick=\"remove_midi('{midi_id}')\">X</button></div>"
156
+ midi_data = midi_processor.generate_variation(midi_id) # Auto-generate
157
+ return html, (io.BytesIO(base64.b64decode(midi_data)) if midi_data else None), get_midi_choices()
 
 
 
158
 
159
  midi_files.change(load_and_generate, inputs=[midi_files],
160
+ outputs=[loaded_display, output, gr.State(get_midi_choices())])
161
 
162
  # Tab 2: Generate & Perform
163
  with gr.Tab("Generate & Perform"):
 
165
  length_factor = gr.Slider(1, 5, value=2, step=1, label="Length Factor")
166
  variation = gr.Slider(0, 1, value=0.3, label="Variation")
167
  generate_btn = gr.Button("Generate")
168
+ effect = gr.Radio(["tempo"], label="Synth Effect", value="tempo")
169
+ intensity = gr.Slider(0, 1, value=0.5, label="Effect Intensity")
170
  apply_btn = gr.Button("Apply Effect")
171
  stop_btn = gr.Button("Stop Playback")
172
  output = gr.Audio(label="Preview", type="bytes", autoplay=True)
173
  status = gr.Textbox(label="Status", value="Ready")
174
+ midi_device = gr.Dropdown(label="MIDI Output Device", choices=midi_manager.get_available_devices(), type="index")
175
+ tempo = gr.Slider(label="Tempo (BPM)", minimum=40, maximum=200, value=120, step=1)
176
+
177
+ def update_dropdown(choices):
178
+ return gr.update(choices=choices)
179
 
180
+ gr.State(get_midi_choices()).change(update_dropdown, inputs=[gr.State()], outputs=[midi_select])
 
 
 
181
 
182
  def generate(midi_id, length, var):
183
  if not midi_id:
184
  return None, "Select a MIDI file"
185
+ midi_data = midi_processor.generate_variation(midi_id, length, var)
186
+ midi_processor.play_with_loop(midi_data)
187
  return io.BytesIO(base64.b64decode(midi_data)), "Playing"
188
 
189
  def apply_effect(midi_data, fx, inten):
190
  if not midi_data:
191
  return None, "Generate a MIDI first"
192
+ new_data = midi_processor.apply_synth_effect(midi_data.decode('utf-8'), fx, inten)
193
+ midi_processor.play_with_loop(new_data)
194
  return io.BytesIO(base64.b64decode(new_data)), "Playing"
195
 
196
  generate_btn.click(generate, inputs=[midi_select, length_factor, variation],
197
  outputs=[output, status])
198
  apply_btn.click(apply_effect, inputs=[output, effect, intensity],
199
  outputs=[output, status])
200
+ stop_btn.click(midi_processor.stop_playback, inputs=None, outputs=[status])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
 
202
+ # Tab 3: Downloads
203
  with gr.Tab("Downloads"):
204
  downloads = gr.HTML(value="No files yet")
205
  def update_downloads(*args):
206
  return create_download_list()
207
+ gr.on(triggers=[midi_files.change, generate_btn.click, apply_btn.click],
208
  fn=update_downloads, inputs=None, outputs=[downloads])
209
 
210
  gr.Markdown("""