FILMITO commited on
Commit
74e496a
Β·
verified Β·
1 Parent(s): 2d81959

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +147 -252
app.py CHANGED
@@ -2,316 +2,211 @@ import gradio as gr
2
  import pretty_midi
3
  import numpy as np
4
  import tempfile
5
- import os
6
  import librosa
7
  import soundfile as sf
8
- from pathlib import Path
9
 
10
- class MP3ToHumanizedMP3:
11
  def __init__(self):
12
- self.instrument_sets = {
13
- "pop": [
14
- {"program": 0, "name": "Drums", "is_drum": True, "volume": 0.8},
15
- {"program": 33, "name": "Bass Guitar", "is_drum": False, "volume": 0.7},
16
- {"program": 25, "name": "Acoustic Guitar", "is_drum": False, "volume": 0.6},
17
- {"program": 1, "name": "Piano", "is_drum": False, "volume": 0.5},
18
- ],
19
- "electronic": [
20
- {"program": 0, "name": "Drums", "is_drum": True, "volume": 0.9},
21
- {"program": 39, "name": "Synth Bass", "is_drum": False, "volume": 0.8},
22
- {"program": 81, "name": "Lead Synth", "is_drum": False, "volume": 0.7},
23
- {"program": 89, "name": "Pad", "is_drum": False, "volume": 0.4},
24
- ],
25
- "rock": [
26
- {"program": 0, "name": "Drums", "is_drum": True, "volume": 0.9},
27
- {"program": 33, "name": "Bass", "is_drum": False, "volume": 0.7},
28
- {"program": 30, "name": "Distortion Guitar", "is_drum": False, "volume": 0.8},
29
- {"program": 27, "name": "Clean Guitar", "is_drum": False, "volume": 0.6},
30
- ],
31
- "cinematic": [
32
- {"program": 0, "name": "Drums", "is_drum": True, "volume": 0.6},
33
- {"program": 48, "name": "String Ensemble", "is_drum": False, "volume": 0.8},
34
- {"program": 61, "name": "French Horn", "is_drum": False, "volume": 0.7},
35
- {"program": 5, "name": "Electric Piano", "is_drum": False, "volume": 0.5},
36
- ]
37
  }
38
 
39
- def create_full_song(self, audio_path, style="pop", intensity=0.7):
40
- """Convert MP3 to a complete humanized song - SIMPLIFIED VERSION"""
41
  try:
42
- # Load original audio
43
- y, sr = librosa.load(audio_path, sr=22050, mono=True)
 
44
 
45
- # Create MIDI structure
46
  midi = pretty_midi.PrettyMIDI()
47
 
48
- # Add instruments based on style
49
- for inst_info in self.instrument_sets[style]:
50
- instrument = pretty_midi.Instrument(
51
- program=inst_info["program"],
52
- is_drum=inst_info["is_drum"],
53
- name=inst_info["name"]
54
- )
55
  midi.instruments.append(instrument)
56
 
57
- # SIMPLIFIED: Extract basic rhythm and melody
58
- self.simple_music_extraction(midi, y, sr, style, intensity)
59
 
60
- # Humanize the performance
61
- self.humanize_performance(midi, intensity)
62
 
63
- # Synthesize to audio
64
  audio_data = midi.synthesize()
65
 
66
  return audio_data, sr
67
 
68
  except Exception as e:
69
- raise Exception(f"Song creation failed: {str(e)}")
70
 
71
- def simple_music_extraction(self, midi, y, sr, style, intensity):
72
- """Simplified music extraction without complex dependencies"""
73
- try:
74
- # Get basic tempo and beats
75
- tempo, beat_frames = librosa.beat.beat_track(y=y, sr=sr, units='time')
76
- if len(beat_frames) > 0:
77
- beat_times = beat_frames
78
- else:
79
- # Fallback: create artificial beats
80
- duration = len(y) / sr
81
- beat_times = np.linspace(0, duration, 16)
82
-
83
- instruments = midi.instruments
84
-
85
- # Add simple drum pattern
86
- drum_instrument = next((inst for inst in instruments if inst.is_drum), None)
87
- if drum_instrument:
88
- self.add_simple_drums(drum_instrument, beat_times, style)
89
-
90
- # Add simple bass line
91
- bass_instrument = next((inst for inst in instruments if not inst.is_drum and 32 <= inst.program <= 39), None)
92
- if bass_instrument:
93
- self.add_simple_bass(bass_instrument, beat_times)
94
-
95
- # Add simple melody
96
- lead_instrument = next((inst for inst in instruments if not inst.is_drum and inst != bass_instrument), None)
97
- if lead_instrument:
98
- self.add_simple_melody(lead_instrument, y, sr, intensity)
99
-
100
- except Exception as e:
101
- # If extraction fails, add a basic pattern
102
- self.add_fallback_pattern(midi, y, sr)
103
-
104
- def add_simple_drums(self, drums, beat_times, style):
105
- """Add basic drum pattern"""
106
- for i, beat_time in enumerate(beat_times[:16]): # First 16 beats
107
- # Kick on beat 1
108
- if i % 4 == 0:
109
- note = pretty_midi.Note(
110
- velocity=90, pitch=36, start=beat_time, end=beat_time + 0.3
111
- )
112
- drums.notes.append(note)
113
-
114
- # Snare on beat 3
115
- if i % 4 == 2:
116
- note = pretty_midi.Note(
117
- velocity=80, pitch=38, start=beat_time, end=beat_time + 0.2
118
- )
119
- drums.notes.append(note)
120
-
121
- # Hi-hat on all beats for electronic/pop
122
- if style in ["electronic", "pop"] and i % 2 == 0:
123
- note = pretty_midi.Note(
124
- velocity=60, pitch=42, start=beat_time, end=beat_time + 0.1
125
- )
126
- drums.notes.append(note)
127
-
128
- def add_simple_bass(self, bass, beat_times):
129
- """Add simple bass line"""
130
- bass_notes = [36, 38, 41, 43] # C, D, F, G
131
 
132
- for i, beat_time in enumerate(beat_times[:8]):
133
- if i % 2 == 0: # Every other beat
134
- note_pitch = bass_notes[i % len(bass_notes)]
135
- note = pretty_midi.Note(
136
- velocity=80,
137
- pitch=note_pitch,
138
- start=beat_time,
139
- end=beat_time + 0.8
140
- )
141
- bass.notes.append(note)
142
-
143
- def add_simple_melody(self, lead, y, sr, intensity):
144
- """Add simple melody using basic pitch detection"""
145
- try:
146
- # Use simple onset detection
147
- onset_frames = librosa.onset.onset_detect(y=y, sr=sr, hop_length=512)
148
- onset_times = librosa.frames_to_time(onset_frames, sr=sr, hop_length=512)
149
-
150
- melody_notes = [60, 62, 64, 65, 67, 69, 71, 72] # C Major scale
151
-
152
- for i, onset_time in enumerate(onset_times[:12]): # First 12 onsets
153
- note_pitch = melody_notes[i % len(melody_notes)]
154
- note = pretty_midi.Note(
155
- velocity=np.random.randint(70, 90),
156
- pitch=note_pitch,
157
- start=onset_time,
158
- end=onset_time + 0.5
159
- )
160
- lead.notes.append(note)
161
-
162
- except:
163
- # Fallback: add a simple arpeggio
164
- for i in range(8):
165
- note = pretty_midi.Note(
166
- velocity=80,
167
- pitch=60 + i,
168
- start=i * 0.5,
169
- end=i * 0.5 + 0.4
170
- )
171
- lead.notes.append(note)
172
-
173
- def add_fallback_pattern(self, midi, y, sr):
174
- """Add a basic musical pattern if extraction fails"""
175
- duration = len(y) / sr
176
- instruments = [inst for inst in midi.instruments if not inst.is_drum]
177
 
178
- if instruments:
179
- lead = instruments[0]
180
- # Add a simple scale
181
- for i in range(8):
182
- note = pretty_midi.Note(
183
- velocity=80,
184
- pitch=60 + i,
185
- start=i * 0.5,
186
- end=i * 0.5 + 0.4
187
- )
188
- lead.notes.append(note)
189
 
190
- def humanize_performance(self, midi, intensity):
191
- """Add human feel to all instruments"""
 
 
 
 
 
 
 
 
 
192
  for instrument in midi.instruments:
193
  for note in instrument.notes:
194
- # Humanize timing
195
- timing_shift = np.random.normal(0, 0.02 * intensity)
196
- note.start = max(0, note.start + timing_shift)
197
 
198
- # Humanize velocity
199
- vel_shift = np.random.randint(-15, 15)
200
- note.velocity = max(40, min(127, note.velocity + int(vel_shift * intensity)))
201
 
202
- # Humanize duration (except drums)
203
  if not instrument.is_drum:
204
- duration_shift = np.random.normal(0, 0.1 * intensity)
205
- note.end = max(note.start + 0.1, note.end + duration_shift)
206
 
207
- def process_to_mp3(files, style, intensity):
208
- """Process audio files and return MP3 results"""
209
- if not files:
210
- return None, "Please upload audio files"
211
-
212
- converter = MP3ToHumanizedMP3()
213
- output_files = []
214
 
215
- for file in files:
216
- try:
217
- print(f"Processing: {file.name}")
218
-
219
- # Create full humanized song
220
- audio_data, sr = converter.create_full_song(file.name, style, intensity)
221
-
222
- # Save as MP3
223
- mp3_path = tempfile.mktemp(suffix='_humanized.mp3')
224
- sf.write(mp3_path, audio_data, sr)
225
-
226
- output_files.append(mp3_path)
227
- print(f"Successfully created: {mp3_path}")
228
-
229
- except Exception as e:
230
- error_msg = f"Error processing {file.name}: {str(e)}"
231
- print(error_msg)
232
- return None, error_msg
233
 
234
- if output_files:
235
- return output_files, f"βœ… Success! Created {len(output_files)} humanized MP3 files"
236
- else:
237
- return None, "❌ Processing failed - no files were created"
 
 
 
 
 
 
 
 
238
 
239
- # Simple and robust interface
240
  with gr.Blocks(theme=gr.themes.Soft(), title="MP3 Humanizer") as demo:
241
  gr.Markdown("""
242
  # 🎡 MP3 Humanizer
243
- **Convert AI Music to Human-Sounding MP3 - No Errors!**
244
-
245
- Upload your AI-generated music and get back humanized MP3 files with full instrumentation.
246
  """)
247
 
248
  with gr.Row():
249
- with gr.Column():
250
- gr.Markdown("### πŸ“ Upload Your Music")
251
- file_input = gr.File(
252
- file_count="multiple",
253
- file_types=[".mp3", ".wav", ".m4a", ".ogg"],
254
- label="Drag and drop your audio files here"
255
  )
256
 
 
257
  style = gr.Radio(
258
  ["pop", "electronic", "rock", "cinematic"],
259
  value="pop",
260
- label="🎡 Music Style"
261
  )
262
 
263
  intensity = gr.Slider(
264
  0.1, 1.0, value=0.7,
265
- label="πŸŽ›οΈ Human Feel Intensity"
266
  )
267
 
268
- process_btn = gr.Button("πŸš€ Create Humanized MP3", variant="primary", size="lg")
 
 
 
 
269
 
270
- with gr.Column():
271
- gr.Markdown("### πŸ“₯ Your Humanized Songs")
272
- file_output = gr.File(
273
- file_count="multiple",
274
- label="Download your humanized MP3 files"
 
275
  )
276
 
277
- audio_output = gr.Audio(
278
- label="🎧 Preview (First File)",
279
- type="filepath"
 
280
  )
281
 
282
  status = gr.Textbox(
283
  label="Status",
284
- interactive=False,
285
- max_lines=3
286
  )
287
 
288
- with gr.Accordion("πŸ’‘ How It Works", open=True):
289
- gr.Markdown("""
290
- **This version is guaranteed to work:**
291
- 1. **Upload** any audio file (MP3, WAV, M4A, OGG)
292
- 2. **Choose** your preferred music style
293
- 3. **Click Process** - No more scipy errors!
294
- 4. **Download** your humanized MP3 file
295
-
296
- **Each MP3 contains:**
297
- - πŸ₯ Drum patterns
298
- - 🎸 Bass lines
299
- - 🎹 Melodies and chords
300
- - πŸŽ›οΈ Natural human timing
301
-
302
- **No technical knowledge needed - just upload and download!**
303
- """)
304
-
305
- # Connect the processing
306
- process_btn.click(
307
- fn=process_to_mp3,
308
- inputs=[file_input, style, intensity],
309
- outputs=[file_output, status]
310
- ).then(
311
- lambda files: files[0] if files and len(files) > 0 else None,
312
- inputs=[file_output],
313
- outputs=[audio_output]
314
  )
315
 
316
  if __name__ == "__main__":
317
- demo.launch(debug=True)
 
2
  import pretty_midi
3
  import numpy as np
4
  import tempfile
 
5
  import librosa
6
  import soundfile as sf
 
7
 
8
+ class SimpleMP3Humanizer:
9
  def __init__(self):
10
+ self.style_presets = {
11
+ "pop": [0, 33, 25, 1], # Drums, Bass, Guitar, Piano
12
+ "electronic": [0, 39, 81, 89], # Drums, Synth Bass, Lead, Pad
13
+ "rock": [0, 33, 30, 27], # Drums, Bass, Distortion Guitar, Clean Guitar
14
+ "cinematic": [0, 48, 61, 5] # Drums, Strings, Horn, Piano
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
  }
16
 
17
+ def mp3_to_humanized_mp3(self, mp3_path, style="pop", intensity=0.7):
18
+ """Convert MP3 to humanized MP3 in one step"""
19
  try:
20
+ # Load the MP3
21
+ y, sr = librosa.load(mp3_path, sr=22050, mono=True)
22
+ duration = len(y) / sr
23
 
24
+ # Create MIDI
25
  midi = pretty_midi.PrettyMIDI()
26
 
27
+ # Add instruments
28
+ for program in self.style_presets[style]:
29
+ is_drum = (program == 0)
30
+ instrument = pretty_midi.Instrument(program=program, is_drum=is_drum)
 
 
 
31
  midi.instruments.append(instrument)
32
 
33
+ # Create simple music based on audio
34
+ self.create_simple_music(midi, y, sr, duration, style, intensity)
35
 
36
+ # Add human feel
37
+ self.add_human_touch(midi, intensity)
38
 
39
+ # Convert to audio
40
  audio_data = midi.synthesize()
41
 
42
  return audio_data, sr
43
 
44
  except Exception as e:
45
+ raise Exception(f"Conversion failed: {str(e)}")
46
 
47
+ def create_simple_music(self, midi, y, sr, duration, style, intensity):
48
+ """Create basic musical structure"""
49
+ # Create beats
50
+ num_beats = int(duration * 2) # 2 beats per second
51
+ if num_beats < 8:
52
+ num_beats = 8
53
+ if num_beats > 32:
54
+ num_beats = 32
55
+
56
+ beat_times = np.linspace(0, duration, num_beats)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
 
58
+ instruments = midi.instruments
59
+
60
+ # Add drums
61
+ drums = next((inst for inst in instruments if inst.is_drum), None)
62
+ if drums:
63
+ for i, time in enumerate(beat_times):
64
+ # Kick on strong beats
65
+ if i % 4 == 0:
66
+ drums.notes.append(self.create_note(36, 90, time, 0.3))
67
+ # Snare on off-beats
68
+ if i % 4 == 2:
69
+ drums.notes.append(self.create_note(38, 80, time, 0.2))
70
+ # Hi-hats for electronic/pop
71
+ if style in ["electronic", "pop"]:
72
+ drums.notes.append(self.create_note(42, 70, time, 0.1))
73
+
74
+ # Add bass
75
+ bass = next((inst for inst in instruments if not inst.is_drum and 32 <= inst.program <= 39), None)
76
+ if bass:
77
+ bass_notes = [36, 38, 41, 43, 45, 48] # Simple bass line
78
+ for i, time in enumerate(beat_times[::2]): # Every other beat
79
+ if i < len(bass_notes):
80
+ bass.notes.append(self.create_note(bass_notes[i], 85, time, 0.8))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
 
82
+ # Add melody
83
+ melody_instruments = [inst for inst in instruments if not inst.is_drum and inst.program not in range(32, 40)]
84
+ if melody_instruments:
85
+ melody = melody_instruments[0]
86
+ melody_notes = [60, 62, 64, 65, 67, 69, 71, 72] # C major scale
87
+ for i, time in enumerate(beat_times[::4]): # Every 4 beats
88
+ if i < len(melody_notes):
89
+ melody.notes.append(self.create_note(melody_notes[i], 80, time, 1.0))
 
 
 
90
 
91
+ def create_note(self, pitch, velocity, start, duration):
92
+ """Helper to create a note"""
93
+ return pretty_midi.Note(
94
+ velocity=velocity,
95
+ pitch=pitch,
96
+ start=start,
97
+ end=start + duration
98
+ )
99
+
100
+ def add_human_touch(self, midi, intensity):
101
+ """Add humanization to the music"""
102
  for instrument in midi.instruments:
103
  for note in instrument.notes:
104
+ # Random timing
105
+ note.start += np.random.normal(0, 0.02 * intensity)
106
+ note.start = max(0, note.start)
107
 
108
+ # Random velocity
109
+ note.velocity += int(np.random.normal(0, 10 * intensity))
110
+ note.velocity = max(40, min(127, note.velocity))
111
 
112
+ # Random duration for non-drums
113
  if not instrument.is_drum:
114
+ note.end += np.random.normal(0, 0.05 * intensity)
115
+ note.end = max(note.start + 0.1, note.end)
116
 
117
+ def convert_mp3(input_mp3, style, intensity):
118
+ """Main conversion function"""
119
+ if input_mp3 is None:
120
+ return None, "Please upload an MP3 file"
 
 
 
121
 
122
+ converter = SimpleMP3Humanizer()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
123
 
124
+ try:
125
+ # Convert to humanized MP3
126
+ audio_data, sr = converter.mp3_to_humanized_mp3(input_mp3, style, intensity)
127
+
128
+ # Save as temporary MP3 file
129
+ output_path = tempfile.mktemp(suffix='_humanized.mp3')
130
+ sf.write(output_path, audio_data, sr)
131
+
132
+ return output_path, "βœ… Conversion successful! Download your humanized song below."
133
+
134
+ except Exception as e:
135
+ return None, f"❌ Error: {str(e)}"
136
 
137
+ # Clean and simple interface
138
  with gr.Blocks(theme=gr.themes.Soft(), title="MP3 Humanizer") as demo:
139
  gr.Markdown("""
140
  # 🎡 MP3 Humanizer
141
+ **Upload AI Music β†’ Get Human Version β†’ Download MP3**
 
 
142
  """)
143
 
144
  with gr.Row():
145
+ with gr.Column(scale=1):
146
+ gr.Markdown("### 1. Upload Your AI Song")
147
+ input_audio = gr.Audio(
148
+ sources=["upload"],
149
+ type="filepath",
150
+ label="Upload MP3 File"
151
  )
152
 
153
+ gr.Markdown("### 2. Choose Settings")
154
  style = gr.Radio(
155
  ["pop", "electronic", "rock", "cinematic"],
156
  value="pop",
157
+ label="Music Style"
158
  )
159
 
160
  intensity = gr.Slider(
161
  0.1, 1.0, value=0.7,
162
+ label="Human Feel"
163
  )
164
 
165
+ convert_btn = gr.Button(
166
+ "✨ Convert to Human Version",
167
+ variant="primary",
168
+ size="lg"
169
+ )
170
 
171
+ with gr.Column(scale=1):
172
+ gr.Markdown("### 3. Download Result")
173
+ output_audio = gr.Audio(
174
+ label="Your Humanized Song",
175
+ type="filepath",
176
+ interactive=False
177
  )
178
 
179
+ download_btn = gr.DownloadButton(
180
+ "πŸ“₯ Download MP3",
181
+ visible=False,
182
+ size="lg"
183
  )
184
 
185
  status = gr.Textbox(
186
  label="Status",
187
+ interactive=False
 
188
  )
189
 
190
+ # Simple conversion process
191
+ def process_conversion(input_mp3, style, intensity):
192
+ output_path, message = convert_mp3(input_mp3, style, intensity)
193
+ if output_path:
194
+ return output_path, output_path, gr.DownloadButton(visible=True), message
195
+ else:
196
+ return None, None, gr.DownloadButton(visible=False), message
197
+
198
+ convert_btn.click(
199
+ fn=process_conversion,
200
+ inputs=[input_audio, style, intensity],
201
+ outputs=[output_audio, download_btn, download_btn, status]
202
+ )
203
+
204
+ # Update download button when audio is ready
205
+ output_audio.change(
206
+ lambda x: gr.DownloadButton(visible=(x is not None)),
207
+ inputs=[output_audio],
208
+ outputs=[download_btn]
 
 
 
 
 
 
 
209
  )
210
 
211
  if __name__ == "__main__":
212
+ demo.launch()