FILMITO commited on
Commit
feb9588
Ā·
verified Ā·
1 Parent(s): 6b3afc0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +322 -242
app.py CHANGED
@@ -6,223 +6,302 @@ import librosa
6
  import soundfile as sf
7
  import os
8
 
9
- class AdvancedMP3Humanizer:
10
  def __init__(self):
11
- # Enhanced instrument sets with better sound combinations
12
- self.style_presets = {
13
- "pop": [
14
- {"program": 0, "name": "Drums", "is_drum": True, "role": "rhythm"},
15
- {"program": 33, "name": "Bass Guitar", "is_drum": False, "role": "bass"},
16
- {"program": 25, "name": "Acoustic Guitar", "is_drum": False, "role": "chords"},
17
- {"program": 1, "name": "Piano", "is_drum": False, "role": "melody"},
18
- {"program": 54, "name": "Synth Voice", "is_drum": False, "role": "pad"}
19
- ],
20
- "electronic": [
21
- {"program": 0, "name": "Electronic Drums", "is_drum": True, "role": "rhythm"},
22
- {"program": 39, "name": "Synth Bass", "is_drum": False, "role": "bass"},
23
- {"program": 81, "name": "Lead Synth", "is_drum": False, "role": "melody"},
24
- {"program": 89, "name": "Warm Pad", "is_drum": False, "role": "pad"},
25
- {"program": 55, "name": "Orchestral Hit", "is_drum": False, "role": "accent"}
26
- ],
27
- "rock": [
28
- {"program": 0, "name": "Rock Drums", "is_drum": True, "role": "rhythm"},
29
- {"program": 33, "name": "Electric Bass", "is_drum": False, "role": "bass"},
30
- {"program": 30, "name": "Distortion Guitar", "is_drum": False, "role": "rhythm"},
31
- {"program": 27, "name": "Clean Guitar", "is_drum": False, "role": "melody"},
32
- {"program": 49, "name": "String Ensemble", "is_drum": False, "role": "pad"}
33
- ],
34
- "cinematic": [
35
- {"program": 0, "name": "Orchestral Drums", "is_drum": True, "role": "rhythm"},
36
- {"program": 48, "name": "String Ensemble", "is_drum": False, "role": "pad"},
37
- {"program": 61, "name": "French Horn", "is_drum": False, "role": "melody"},
38
- {"program": 5, "name": "Electric Piano", "is_drum": False, "role": "chords"},
39
- {"program": 91, "name": "Atmosphere Pad", "is_drum": False, "role": "background"}
40
- ]
 
 
 
 
 
 
 
 
 
 
 
 
41
  }
42
 
43
- def mp3_to_humanized_mp3(self, mp3_path, style="pop", intensity=0.7):
44
- """Convert MP3 to humanized MP3 with better sounds"""
45
  try:
46
- # Load the MP3 with better analysis
47
  y, sr = librosa.load(mp3_path, sr=22050, mono=True)
48
  duration = len(y) / sr
49
 
50
- # Create MIDI with better timing
51
  midi = pretty_midi.PrettyMIDI()
52
 
53
- # Add enhanced instruments
54
- for inst_info in self.style_presets[style]:
55
- instrument = pretty_midi.Instrument(
56
- program=inst_info["program"],
57
- is_drum=inst_info["is_drum"],
58
- name=inst_info["name"]
59
- )
60
- midi.instruments.append(instrument)
61
 
62
- # Extract musical elements from the audio
63
- tempo, beats, melody_info = self.analyze_audio(y, sr)
64
 
65
- # Create enhanced musical arrangement
66
- self.create_enhanced_music(midi, y, sr, duration, style, intensity, tempo, beats, melody_info)
67
 
68
- # Add advanced humanization
69
- self.advanced_humanization(midi, intensity)
70
 
71
- # Synthesize with better sound quality
72
  audio_data = midi.synthesize()
73
 
74
  return audio_data, sr
75
 
76
  except Exception as e:
77
- raise Exception(f"Conversion failed: {str(e)}")
78
 
79
- def analyze_audio(self, y, sr):
80
- """Enhanced audio analysis"""
81
- try:
82
- # Get tempo and beats
83
- tempo, beat_frames = librosa.beat.beat_track(y=y, sr=sr, units='time')
84
-
85
- # Detect melody using multiple methods
86
- onset_frames = librosa.onset.onset_detect(y=y, sr=sr, hop_length=512, delta=0.1)
87
- onset_times = librosa.frames_to_time(onset_frames, sr=sr, hop_length=512)
88
-
89
- # Simple pitch detection for melody
90
- melody_contour = []
91
- for onset_time in onset_times[:20]: # Analyze first 20 onsets
92
- start_idx = int(onset_time * sr)
93
- end_idx = min(start_idx + int(0.3 * sr), len(y))
94
- if end_idx > start_idx:
95
- segment = y[start_idx:end_idx]
96
- freq = self.detect_strongest_frequency(segment, sr)
97
- if 100 < freq < 800:
98
- melody_contour.append(freq)
99
-
100
- return tempo, beat_frames, melody_contour
101
-
102
- except:
103
- # Fallback values
104
- return 120, np.linspace(0, 10, 20), [440, 523, 659, 784] # A, C, E, G frequencies
105
 
106
- def detect_strongest_frequency(self, segment, sr):
107
- """Detect the strongest frequency in a segment"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
108
  try:
109
- # Apply windowing
110
- window = np.hanning(len(segment))
111
- segment_windowed = segment * window
112
-
113
- # FFT analysis
114
- fft = np.fft.rfft(segment_windowed)
115
- freqs = np.fft.rfftfreq(len(segment_windowed), 1/sr)
116
  mags = np.abs(fft)
117
 
118
- # Find strongest frequency in vocal range
119
- mask = (freqs > 80) & (freqs < 1000)
120
  if np.any(mask):
121
  peak_idx = np.argmax(mags[mask])
122
  return freqs[mask][peak_idx]
123
  except:
124
  pass
125
- return 440 # Default to A4
126
 
127
- def create_enhanced_music(self, midi, y, sr, duration, style, intensity, tempo, beats, melody_contour):
128
- """Create realistic musical arrangement"""
129
- instruments = {inst.name: inst for inst in midi.instruments}
 
130
 
131
- # Create better beat structure
132
- if len(beats) > 0:
133
- beat_times = beats[:min(32, len(beats))]
134
- else:
135
- beat_times = np.linspace(0, min(duration, 16), 16)
 
 
 
 
 
 
 
 
136
 
137
- # Enhanced drum patterns
138
- if "Drums" in instruments:
139
- self.create_advanced_drums(instruments["Drums"], beat_times, style)
 
140
 
141
- # Better bass lines
142
- bass_instruments = [inst for inst in midi.instruments if not inst.is_drum and inst.program in [33, 34, 35, 36, 37, 38, 39]]
143
- if bass_instruments:
144
- self.create_melodic_bass(bass_instruments[0], beat_times, melody_contour)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
- # Realistic melody extraction and creation
147
- melody_instruments = [inst for inst in midi.instruments if not inst.is_drum and inst.program not in range(32, 40)]
148
- if melody_instruments:
149
- self.create_adaptive_melody(melody_instruments[0], y, sr, duration, melody_contour, style)
 
150
 
151
- # Chordal accompaniment
152
- chord_instruments = [inst for inst in midi.instruments if not inst.is_drum and inst not in bass_instruments and inst not in melody_instruments]
153
- for inst in chord_instruments:
154
- self.create_harmonic_background(inst, beat_times, style)
 
 
155
 
156
- def create_advanced_drums(self, drums, beat_times, style):
157
- """Create style-appropriate drum patterns"""
158
- for i, time in enumerate(beat_times):
159
- # Always add kick on beat 1
160
- if i % 4 == 0:
161
- drums.notes.append(self.create_note(36, 95, time, 0.3)) # Kick
162
-
163
- # Style-specific snare patterns
164
- if style == "rock" and i % 4 == 2:
165
- drums.notes.append(self.create_note(38, 90, time, 0.25)) # Snare
166
- elif style == "pop" and i % 4 in [2, 3]:
167
- drums.notes.append(self.create_note(38, 85, time + 0.05, 0.2)) # Snare with offset
168
- elif style == "electronic" and i % 2 == 1:
169
- drums.notes.append(self.create_note(38, 80, time, 0.15)) # Electronic snare
170
-
171
- # Hi-hat patterns
172
- if style in ["pop", "electronic"]:
173
- if i % 2 == 0: # Every other beat
174
- drums.notes.append(self.create_note(42, 70, time, 0.1)) # Closed hi-hat
175
- if style == "electronic" and i % 4 == 0:
176
- drums.notes.append(self.create_note(46, 75, time + 0.2, 0.3)) # Open hi-hat
177
-
178
- # Additional percussion
179
- if style == "cinematic" and i % 8 == 0:
180
- drums.notes.append(self.create_note(49, 80, time, 0.5)) # Crash cymbal
 
 
 
 
 
181
 
182
- def create_melodic_bass(self, bass, beat_times, melody_contour):
183
- """Create melodic bass lines that follow the music"""
184
- if len(melody_contour) > 0:
185
- # Use detected melody to create bass line
186
- base_note = self.freq_to_midi(np.median(melody_contour)) - 12
187
- bass_scale = [base_note, base_note + 2, base_note + 4, base_note + 5, base_note + 7]
188
- else:
189
- # Fallback bass scale
190
- bass_scale = [36, 38, 41, 43, 48]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
191
 
192
- for i, time in enumerate(beat_times[::2]): # Every other beat
193
- if i < len(bass_scale):
194
- note_pitch = bass_scale[i % len(bass_scale)]
195
- # Vary velocity for expression
196
- velocity = 80 + (i % 3) * 10
197
- bass.notes.append(self.create_note(note_pitch, velocity, time, 0.9))
198
 
199
- def create_adaptive_melody(self, melody, y, sr, duration, melody_contour, style):
200
- """Create melody that adapts to the original audio"""
201
- if len(melody_contour) > 0:
202
- # Use detected frequencies to create melody
203
- for i, freq in enumerate(melody_contour[:16]): # Use first 16 detected frequencies
204
- midi_note = self.freq_to_midi(freq)
205
- if 48 <= midi_note <= 84: # Reasonable melody range
206
- time = (i / len(melody_contour)) * min(duration, 8) # Spread over 8 seconds max
207
- duration_val = 0.3 + (i % 3) * 0.2 # Vary note durations
208
- melody.notes.append(self.create_note(midi_note, 75 + (i % 4)*5, time, duration_val))
209
- else:
210
- # Create melodic pattern based on style
211
- if style == "pop":
212
- melody_notes = [60, 64, 67, 72, 67, 64, 60, 65] # C major arpeggio
213
- elif style == "electronic":
214
- melody_notes = [65, 69, 72, 77, 72, 69, 65, 70] # F major with tension
215
- elif style == "rock":
216
- melody_notes = [59, 62, 65, 67, 65, 62, 59, 64] # B minor pentatonic
217
- else: # cinematic
218
- melody_notes = [60, 63, 67, 70, 67, 63, 60, 65] # C minor arpeggio
219
-
220
- for i, note_pitch in enumerate(melody_notes):
221
- time = (i / len(melody_notes)) * min(duration, 8)
222
- melody.notes.append(self.create_note(note_pitch, 80, time, 0.5))
223
 
224
- def create_harmonic_background(self, instrument, beat_times, style):
225
- """Create chordal harmony"""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
226
  if style == "pop":
227
  chords = [[60, 64, 67], [65, 69, 72], [67, 71, 74], [62, 65, 69]] # C, F, G, Am
228
  elif style == "electronic":
@@ -232,10 +311,7 @@ class AdvancedMP3Humanizer:
232
  else: # cinematic
233
  chords = [[60, 63, 67], [65, 68, 72], [67, 70, 74], [62, 65, 69]] # Cm, Fm, Gm, Ab
234
 
235
- for i, time in enumerate(beat_times[::4]): # Every 4 beats
236
- chord = chords[i % len(chords)]
237
- for note_pitch in chord:
238
- instrument.notes.append(self.create_note(note_pitch, 60, time, 2.0))
239
 
240
  def freq_to_midi(self, frequency):
241
  """Convert frequency to MIDI note number"""
@@ -250,58 +326,61 @@ class AdvancedMP3Humanizer:
250
  end=start + duration
251
  )
252
 
253
- def advanced_humanization(self, midi, intensity):
254
- """Advanced humanization with more natural variations"""
255
  for instrument in midi.instruments:
256
  # Different humanization for different instrument types
257
  if instrument.is_drum:
258
- humanization_factor = 0.8 # Less humanization for drums
259
- elif instrument.program in range(32, 40): # Bass instruments
260
- humanization_factor = 0.6
261
- else: # Melody/harmony instruments
262
- humanization_factor = 1.2
 
 
 
263
 
264
  for note in instrument.notes:
265
- # Timing variations (different for different instruments)
266
- timing_variation = np.random.normal(0, 0.03 * intensity * humanization_factor)
267
- note.start = max(0, note.start + timing_variation)
268
 
269
- # Velocity variations (more expressive)
270
- vel_variation = np.random.normal(0, 12 * intensity * humanization_factor)
271
- note.velocity = max(40, min(127, note.velocity + int(vel_variation)))
272
 
273
  # Duration variations (except drums)
274
  if not instrument.is_drum:
275
- dur_variation = np.random.normal(0, 0.08 * intensity * humanization_factor)
276
- note.end = max(note.start + 0.1, note.end + dur_variation)
277
 
278
- def convert_mp3(input_mp3, style, intensity):
279
- """Main conversion function"""
280
  if input_mp3 is None:
281
  return None, "Please upload an MP3 file"
282
 
283
- converter = AdvancedMP3Humanizer()
284
 
285
  try:
286
- # Convert to humanized MP3 with better sounds
287
- audio_data, sr = converter.mp3_to_humanized_mp3(input_mp3, style, intensity)
288
 
289
- # Save as temporary MP3 file
290
- output_path = tempfile.mktemp(suffix='_humanized.mp3')
291
  sf.write(output_path, audio_data, sr)
292
 
293
- return output_path, "āœ… Conversion successful! Your humanized song is ready below."
294
 
295
  except Exception as e:
296
  return None, f"āŒ Error: {str(e)}"
297
 
298
- # Enhanced interface
299
- with gr.Blocks(theme=gr.themes.Soft(), title="Advanced MP3 Humanizer") as demo:
300
  gr.Markdown("""
301
- # šŸŽµ Advanced MP3 Humanizer
302
- **Transform AI Music into Realistic Human Performances**
303
 
304
- *Now with better instrument sounds and realistic musical arrangements!*
305
  """)
306
 
307
  with gr.Row():
@@ -313,67 +392,68 @@ with gr.Blocks(theme=gr.themes.Soft(), title="Advanced MP3 Humanizer") as demo:
313
  label="Upload MP3 File"
314
  )
315
 
316
- gr.Markdown("### 2. Choose Music Style")
317
  style = gr.Radio(
318
  ["pop", "electronic", "rock", "cinematic"],
319
  value="pop",
320
- label="Select Style",
321
- info="Each style uses different instruments and patterns"
322
  )
323
 
324
  intensity = gr.Slider(
325
- 0.1, 1.0, value=0.7,
326
  label="Human Feel Intensity",
327
- info="Higher = more natural human variations"
328
  )
329
 
330
- convert_btn = gr.Button(
331
- "šŸŽ¹ Create Human Version",
332
  variant="primary",
333
  size="lg"
334
  )
335
 
336
  with gr.Column(scale=1):
337
- gr.Markdown("### 3. Your Humanized Song")
338
  output_audio = gr.Audio(
339
- label="Enhanced Human Version",
340
  type="filepath",
341
  interactive=False
342
  )
343
 
344
  status = gr.Textbox(
345
- label="Status",
346
  interactive=False
347
  )
348
 
349
- # Enhanced instructions
350
- with gr.Accordion("šŸŽø What's New in This Version", open=True):
351
  gr.Markdown("""
352
- **Major Improvements:**
 
 
 
 
 
353
 
354
- 🄁 **Better Drum Patterns**: Style-specific rhythms with kicks, snares, hi-hats
355
- šŸŽø **Real Bass Lines**: Melodic bass that follows the music
356
- šŸŽ¹ **Adaptive Melodies**: Extracts and enhances melodies from your audio
357
- šŸŽ» **Rich Harmony**: Chord progressions and background pads
358
- šŸŽ›ļø **Advanced Humanization**: Different variations for each instrument type
359
 
360
- **Each style now includes:**
361
- - **Pop**: Drums, Bass Guitar, Acoustic Guitar, Piano, Synth Voice
362
- - **Electronic**: Electronic Drums, Synth Bass, Lead Synth, Warm Pad, Orchestral Hits
363
- - **Rock**: Rock Drums, Electric Bass, Distortion & Clean Guitars, Strings
364
- - **Cinematic**: Orchestral Drums, String Ensemble, French Horn, Electric Piano, Atmosphere
 
365
 
366
- **How to use:**
367
- 1. Upload your AI-generated MP3
368
- 2. Choose the style that matches your music
369
- 3. Adjust human feel (0.7 is recommended)
370
- 4. Click convert and wait ~10 seconds
371
- 5. Play the preview and download your enhanced MP3!
372
  """)
373
 
374
- # Conversion process
375
- convert_btn.click(
376
- fn=convert_mp3,
377
  inputs=[input_audio, style, intensity],
378
  outputs=[output_audio, status]
379
  )
 
6
  import soundfile as sf
7
  import os
8
 
9
+ class CompleteSongHumanizer:
10
  def __init__(self):
11
+ # Complete band setups for each style
12
+ self.band_setups = {
13
+ "pop": {
14
+ "drums": {"program": 0, "name": "Pop Drums"},
15
+ "bass": {"program": 33, "name": "Bass Guitar"},
16
+ "rhythm": {"program": 25, "name": "Acoustic Guitar"},
17
+ "piano": {"program": 1, "name": "Piano"},
18
+ "strings": {"program": 49, "name": "String Ensemble"},
19
+ "synth": {"program": 81, "name": "Lead Synth"}
20
+ },
21
+ "electronic": {
22
+ "drums": {"program": 0, "name": "EDM Drums"},
23
+ "bass": {"program": 39, "name": "Synth Bass"},
24
+ "lead": {"program": 81, "name": "Lead Synth"},
25
+ "pad": {"program": 89, "name": "Warm Pad"},
26
+ "fx": {"program": 103, "name": "FX"},
27
+ "chords": {"program": 5, "name": "Electric Piano"}
28
+ },
29
+ "rock": {
30
+ "drums": {"program": 0, "name": "Rock Drums"},
31
+ "bass": {"program": 33, "name": "Bass Guitar"},
32
+ "guitar1": {"program": 30, "name": "Distortion Guitar"},
33
+ "guitar2": {"program": 27, "name": "Clean Guitar"},
34
+ "keys": {"program": 5, "name": "Electric Piano"},
35
+ "strings": {"program": 48, "name": "String Ensemble"}
36
+ },
37
+ "cinematic": {
38
+ "drums": {"program": 0, "name": "Orchestral Percussion"},
39
+ "strings1": {"program": 48, "name": "String Ensemble"},
40
+ "strings2": {"program": 49, "name": "Slow Strings"},
41
+ "brass": {"program": 61, "name": "French Horn"},
42
+ "woodwinds": {"program": 68, "name": "Oboe"},
43
+ "harp": {"program": 46, "name": "Harp"}
44
+ }
45
+ }
46
+
47
+ # Song structures
48
+ self.song_structures = {
49
+ "pop": ["intro", "verse", "chorus", "verse", "chorus", "bridge", "chorus", "outro"],
50
+ "electronic": ["intro", "build", "drop", "break", "build", "drop", "outro"],
51
+ "rock": ["intro", "verse", "chorus", "verse", "chorus", "solo", "chorus", "outro"],
52
+ "cinematic": ["intro", "theme", "build", "climax", "resolution", "outro"]
53
  }
54
 
55
+ def create_complete_song(self, mp3_path, style="pop", intensity=0.7):
56
+ """Create a complete humanized song from MP3"""
57
  try:
58
+ # Load and analyze the audio
59
  y, sr = librosa.load(mp3_path, sr=22050, mono=True)
60
  duration = len(y) / sr
61
 
62
+ # Create MIDI object
63
  midi = pretty_midi.PrettyMIDI()
64
 
65
+ # Setup complete band
66
+ band = self.setup_band(midi, style)
 
 
 
 
 
 
67
 
68
+ # Analyze audio to get musical content
69
+ audio_features = self.analyze_audio_features(y, sr, duration)
70
 
71
+ # Create complete song structure
72
+ self.build_complete_song(midi, band, style, audio_features, duration, intensity)
73
 
74
+ # Apply advanced humanization
75
+ self.apply_complete_humanization(midi, intensity)
76
 
77
+ # Synthesize to audio
78
  audio_data = midi.synthesize()
79
 
80
  return audio_data, sr
81
 
82
  except Exception as e:
83
+ raise Exception(f"Song creation failed: {str(e)}")
84
 
85
+ def setup_band(self, midi, style):
86
+ """Setup complete band instruments"""
87
+ band = {}
88
+ for role, inst_info in self.band_setups[style].items():
89
+ instrument = pretty_midi.Instrument(
90
+ program=inst_info["program"],
91
+ is_drum=(role == "drums"),
92
+ name=inst_info["name"]
93
+ )
94
+ midi.instruments.append(instrument)
95
+ band[role] = instrument
96
+ return band
 
 
 
 
 
 
 
 
 
 
 
 
 
 
97
 
98
+ def analyze_audio_features(self, y, sr, duration):
99
+ """Extract musical features from audio"""
100
+ # Get tempo and beats
101
+ tempo, beat_frames = librosa.beat.beat_track(y=y, sr=sr, units='time')
102
+
103
+ # Detect onsets for melody extraction
104
+ onset_frames = librosa.onset.onset_detect(y=y, sr=sr, hop_length=512, delta=0.08)
105
+ onset_times = librosa.frames_to_time(onset_frames, sr=sr, hop_length=512)
106
+
107
+ # Extract melody contour
108
+ melody_contour = []
109
+ for onset_time in onset_times[:50]: # Analyze first 50 onsets
110
+ start_idx = int(onset_time * sr)
111
+ end_idx = min(start_idx + int(0.4 * sr), len(y))
112
+ if end_idx > start_idx:
113
+ segment = y[start_idx:end_idx]
114
+ freq = self.detect_pitch(segment, sr)
115
+ if 100 < freq < 1000:
116
+ melody_contour.append((onset_time, freq))
117
+
118
+ # Detect energy changes for song sections
119
+ energy = librosa.feature.rms(y=y)[0]
120
+ energy_times = librosa.times_like(energy, sr=sr)
121
+
122
+ return {
123
+ 'tempo': tempo if tempo else 120,
124
+ 'beats': beat_frames if len(beat_frames) > 0 else np.linspace(0, duration, 32),
125
+ 'melody_contour': melody_contour,
126
+ 'energy': list(zip(energy_times, energy)),
127
+ 'duration': duration
128
+ }
129
+
130
+ def detect_pitch(self, segment, sr):
131
+ """Detect pitch in audio segment"""
132
  try:
133
+ # Simple FFT-based pitch detection
134
+ fft = np.fft.rfft(segment * np.hanning(len(segment)))
135
+ freqs = np.fft.rfftfreq(len(segment), 1/sr)
 
 
 
 
136
  mags = np.abs(fft)
137
 
138
+ # Find strongest frequency in reasonable range
139
+ mask = (freqs > 80) & (freqs < 1200)
140
  if np.any(mask):
141
  peak_idx = np.argmax(mags[mask])
142
  return freqs[mask][peak_idx]
143
  except:
144
  pass
145
+ return 440
146
 
147
+ def build_complete_song(self, midi, band, style, features, duration, intensity):
148
+ """Build complete song with structure"""
149
+ structure = self.song_structures[style]
150
+ section_duration = duration / len(structure)
151
 
152
+ for section_idx, section_name in enumerate(structure):
153
+ start_time = section_idx * section_duration
154
+ end_time = (section_idx + 1) * section_duration
155
+
156
+ # Create section-specific music
157
+ self.create_section(
158
+ band, style, section_name, section_idx,
159
+ start_time, end_time, features, intensity
160
+ )
161
+
162
+ def create_section(self, band, style, section_name, section_idx, start_time, end_time, features, intensity):
163
+ """Create music for a specific song section"""
164
+ section_duration = end_time - start_time
165
 
166
+ # Get beats for this section
167
+ section_beats = [t for t in features['beats'] if start_time <= t < end_time]
168
+ if not section_beats:
169
+ section_beats = np.linspace(start_time, end_time, 8)
170
 
171
+ # Section-specific arrangements
172
+ if section_name in ["intro", "outro"]:
173
+ self.create_intro_outro(band, style, section_name, start_time, end_time, section_beats)
174
+ elif section_name in ["verse", "theme"]:
175
+ self.create_verse(band, style, start_time, end_time, section_beats, features)
176
+ elif section_name in ["chorus", "drop", "climax"]:
177
+ self.create_chorus(band, style, start_time, end_time, section_beats, features)
178
+ elif section_name in ["bridge", "break", "solo"]:
179
+ self.create_bridge(band, style, start_time, end_time, section_beats, features)
180
+ elif section_name in ["build"]:
181
+ self.create_build(band, style, start_time, end_time, section_beats, features)
182
+
183
+ def create_intro_outro(self, band, style, section_name, start_time, end_time, beats):
184
+ """Create intro/outro sections"""
185
+ # Drums - simple pattern
186
+ if 'drums' in band:
187
+ for i, beat_time in enumerate(beats):
188
+ if i % 4 == 0: # Kick on downbeat
189
+ band['drums'].notes.append(self.create_note(36, 80, beat_time, 0.3))
190
+ if section_name == "intro" and i % 2 == 0: # Hi-hat in intro
191
+ band['drums'].notes.append(self.create_note(42, 60, beat_time, 0.1))
192
 
193
+ # Bass - simple root notes
194
+ if 'bass' in band:
195
+ root_note = 36 if style != "cinematic" else 48
196
+ for i, beat_time in enumerate(beats[::2]):
197
+ band['bass'].notes.append(self.create_note(root_note, 70, beat_time, 0.8))
198
 
199
+ # Pad/strings - atmospheric
200
+ pad_instrument = next((inst for role, inst in band.items() if 'pad' in role or 'string' in role), None)
201
+ if pad_instrument:
202
+ chord_notes = self.get_chord_for_section(style, section_name, 0)
203
+ for note_pitch in chord_notes:
204
+ pad_instrument.notes.append(self.create_note(note_pitch, 50, start_time, end_time - start_time))
205
 
206
+ def create_verse(self, band, style, start_time, end_time, beats, features):
207
+ """Create verse section"""
208
+ # Full drum pattern
209
+ if 'drums' in band:
210
+ for i, beat_time in enumerate(beats):
211
+ # Kick on 1 and 3
212
+ if i % 4 in [0, 2]:
213
+ band['drums'].notes.append(self.create_note(36, 85, beat_time, 0.3))
214
+ # Snare on 2 and 4
215
+ if i % 4 in [1, 3]:
216
+ band['drums'].notes.append(self.create_note(38, 80, beat_time, 0.25))
217
+ # Hi-hats
218
+ if style in ["pop", "electronic"]:
219
+ band['drums'].notes.append(self.create_note(42, 65, beat_time, 0.1))
220
+
221
+ # Bass line
222
+ if 'bass' in band:
223
+ bass_line = self.create_bass_line(beats, style)
224
+ for note in bass_line:
225
+ band['bass'].notes.append(note)
226
+
227
+ # Melody from audio analysis
228
+ if 'lead' in band or 'guitar1' in band:
229
+ lead_instrument = band.get('lead') or band.get('guitar1')
230
+ if lead_instrument:
231
+ melody = self.extract_melody_for_section(features['melody_contour'], start_time, end_time)
232
+ for time, freq in melody:
233
+ midi_note = self.freq_to_midi(freq)
234
+ if 48 <= midi_note <= 84:
235
+ lead_instrument.notes.append(self.create_note(midi_note, 80, time, 0.4))
236
 
237
+ def create_chorus(self, band, style, start_time, end_time, beats, features):
238
+ """Create chorus/drop section - more intense"""
239
+ # Energetic drums
240
+ if 'drums' in band:
241
+ for i, beat_time in enumerate(beats):
242
+ # Stronger kicks
243
+ if i % 4 in [0, 2]:
244
+ band['drums'].notes.append(self.create_note(36, 95, beat_time, 0.4))
245
+ # Louder snares
246
+ if i % 4 in [1, 3]:
247
+ band['drums'].notes.append(self.create_note(38, 90, beat_time, 0.3))
248
+ # More hi-hats
249
+ if style in ["pop", "electronic"]:
250
+ band['drums'].notes.append(self.create_note(42, 75, beat_time, 0.15))
251
+ # Crash cymbal on first beat
252
+ if i == 0:
253
+ band['drums'].notes.append(self.create_note(49, 100, beat_time, 1.0))
254
+
255
+ # More active bass
256
+ if 'bass' in band:
257
+ for i, beat_time in enumerate(beats):
258
+ bass_note = 36 + (i % 4) * 2
259
+ band['bass'].notes.append(self.create_note(bass_note, 85, beat_time, 0.6))
260
 
261
+ # All instruments play
262
+ for role, instrument in band.items():
263
+ if role not in ['drums', 'bass'] and not instrument.is_drum:
264
+ chord_notes = self.get_chord_for_section(style, "chorus", 0)
265
+ for note_pitch in chord_notes:
266
+ instrument.notes.append(self.create_note(note_pitch, 70, start_time, end_time - start_time))
267
 
268
+ def create_bass_line(self, beats, style):
269
+ """Create melodic bass line"""
270
+ bass_notes = []
271
+ if style == "pop":
272
+ pattern = [36, 38, 41, 43, 41, 38, 36, 35]
273
+ elif style == "electronic":
274
+ pattern = [36, 36, 39, 39, 41, 41, 39, 36]
275
+ elif style == "rock":
276
+ pattern = [36, 38, 36, 41, 36, 38, 36, 43]
277
+ else: # cinematic
278
+ pattern = [36, 39, 43, 46, 43, 39, 36, 34]
279
+
280
+ for i, beat_time in enumerate(beats[::2]): # Every other beat
281
+ note_pitch = pattern[i % len(pattern)]
282
+ bass_notes.append(self.create_note(note_pitch, 80, beat_time, 0.9))
283
+
284
+ return bass_notes
 
 
 
 
 
 
 
285
 
286
+ def extract_melody_for_section(self, melody_contour, start_time, end_time):
287
+ """Extract melody notes for a specific section"""
288
+ section_melody = []
289
+ for time, freq in melody_contour:
290
+ if start_time <= time < end_time:
291
+ section_melody.append((time, freq))
292
+
293
+ # If no melody detected, create one
294
+ if not section_melody:
295
+ section_duration = end_time - start_time
296
+ for i in range(8):
297
+ time = start_time + (i / 8) * section_duration
298
+ freq = 440 * (2 ** (i / 12)) # Rising pattern
299
+ section_melody.append((time, freq))
300
+
301
+ return section_melody
302
+
303
+ def get_chord_for_section(self, style, section_name, section_idx):
304
+ """Get appropriate chords for section"""
305
  if style == "pop":
306
  chords = [[60, 64, 67], [65, 69, 72], [67, 71, 74], [62, 65, 69]] # C, F, G, Am
307
  elif style == "electronic":
 
311
  else: # cinematic
312
  chords = [[60, 63, 67], [65, 68, 72], [67, 70, 74], [62, 65, 69]] # Cm, Fm, Gm, Ab
313
 
314
+ return chords[section_idx % len(chords)]
 
 
 
315
 
316
  def freq_to_midi(self, frequency):
317
  """Convert frequency to MIDI note number"""
 
326
  end=start + duration
327
  )
328
 
329
+ def apply_complete_humanization(self, midi, intensity):
330
+ """Apply realistic humanization to entire song"""
331
  for instrument in midi.instruments:
332
  # Different humanization for different instrument types
333
  if instrument.is_drum:
334
+ timing_variance = 0.01 # Drums are tighter
335
+ velocity_variance = 8
336
+ elif any(role in instrument.name.lower() for role in ['bass', 'pad', 'string']):
337
+ timing_variance = 0.02 # Background instruments
338
+ velocity_variance = 10
339
+ else:
340
+ timing_variance = 0.03 # Lead instruments have more feel
341
+ velocity_variance = 15
342
 
343
  for note in instrument.notes:
344
+ # Timing variations
345
+ note.start += np.random.normal(0, timing_variance * intensity)
346
+ note.start = max(0, note.start)
347
 
348
+ # Velocity variations
349
+ vel_change = int(np.random.normal(0, velocity_variance * intensity))
350
+ note.velocity = max(30, min(127, note.velocity + vel_change))
351
 
352
  # Duration variations (except drums)
353
  if not instrument.is_drum:
354
+ dur_change = np.random.normal(0, 0.05 * intensity)
355
+ note.end = max(note.start + 0.1, note.end + dur_change)
356
 
357
+ def create_complete_song(input_mp3, style, intensity):
358
+ """Main function to create complete song"""
359
  if input_mp3 is None:
360
  return None, "Please upload an MP3 file"
361
 
362
+ humanizer = CompleteSongHumanizer()
363
 
364
  try:
365
+ # Create complete humanized song
366
+ audio_data, sr = humanizer.create_complete_song(input_mp3, style, intensity)
367
 
368
+ # Save as MP3
369
+ output_path = tempfile.mktemp(suffix='_complete_song.mp3')
370
  sf.write(output_path, audio_data, sr)
371
 
372
+ return output_path, "šŸŽµ Complete song created! Your humanized masterpiece is ready!"
373
 
374
  except Exception as e:
375
  return None, f"āŒ Error: {str(e)}"
376
 
377
+ # Professional interface
378
+ with gr.Blocks(theme=gr.themes.Soft(), title="Complete Song Humanizer") as demo:
379
  gr.Markdown("""
380
+ # šŸŽµ Complete Song Humanizer
381
+ **Transform AI Music into Full, Professional Human Performances**
382
 
383
+ *Creates complete songs with verses, choruses, and full band arrangements*
384
  """)
385
 
386
  with gr.Row():
 
392
  label="Upload MP3 File"
393
  )
394
 
395
+ gr.Markdown("### 2. Choose Song Style")
396
  style = gr.Radio(
397
  ["pop", "electronic", "rock", "cinematic"],
398
  value="pop",
399
+ label="Music Genre",
400
+ info="Each style creates different band arrangements"
401
  )
402
 
403
  intensity = gr.Slider(
404
+ 0.1, 1.0, value=0.8,
405
  label="Human Feel Intensity",
406
+ info="How natural and human-like the performance sounds"
407
  )
408
 
409
+ create_btn = gr.Button(
410
+ "šŸŽ¹ Create Complete Song",
411
  variant="primary",
412
  size="lg"
413
  )
414
 
415
  with gr.Column(scale=1):
416
+ gr.Markdown("### 3. Your Complete Song")
417
  output_audio = gr.Audio(
418
+ label="Your Humanized Masterpiece",
419
  type="filepath",
420
  interactive=False
421
  )
422
 
423
  status = gr.Textbox(
424
+ label="Creation Status",
425
  interactive=False
426
  )
427
 
428
+ # Song details
429
+ with gr.Accordion("šŸŽ¼ What You're Getting", open=True):
430
  gr.Markdown("""
431
+ **Each song includes:**
432
+
433
+ **šŸŽµ Complete Song Structure:**
434
+ - Intro, Verses, Choruses, Bridge, Outro
435
+ - Professional arrangement with buildup and climax
436
+ - Dynamic changes between sections
437
 
438
+ **šŸŽø Full Band Arrangement:**
439
+ - **Pop**: Drums, Bass, Guitar, Piano, Strings, Synth (6 instruments)
440
+ - **Electronic**: EDM Drums, Synth Bass, Lead, Pad, FX, Electric Piano (6 instruments)
441
+ - **Rock**: Rock Drums, Bass, 2 Guitars, Keys, Strings (6 instruments)
442
+ - **Cinematic**: Orchestral Drums, 2 String sections, Brass, Woodwinds, Harp (6 instruments)
443
 
444
+ **šŸŽ›ļø Professional Production:**
445
+ - Realistic human timing variations
446
+ - Dynamic velocity changes
447
+ - Section-specific arrangements
448
+ - Melodic development
449
+ - Harmonic progression
450
 
451
+ **ā±ļø Processing Time:** ~15-30 seconds for a complete song
 
 
 
 
 
452
  """)
453
 
454
+ # Creation process
455
+ create_btn.click(
456
+ fn=create_complete_song,
457
  inputs=[input_audio, style, intensity],
458
  outputs=[output_audio, status]
459
  )