FILMITO commited on
Commit
07bbd8c
Β·
verified Β·
1 Parent(s): 7fd7e83

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +238 -0
app.py ADDED
@@ -0,0 +1,238 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pretty_midi
3
+ import numpy as np
4
+ import tempfile
5
+ import os
6
+ import scipy
7
+ from scipy import signal
8
+ import librosa
9
+ import io
10
+ import base64
11
+ from pathlib import Path
12
+
13
+ class HumanizeBot:
14
+ def __init__(self):
15
+ self.groove_profiles = {
16
+ "drums": {"timing_var": 0.02, "velocity_var": 15, "swing_factor": 0.1},
17
+ "melody": {"timing_var": 0.01, "velocity_var": 10, "swing_factor": 0.05},
18
+ "bass": {"timing_var": 0.015, "velocity_var": 12, "swing_factor": 0.07},
19
+ "chords": {"timing_var": 0.008, "velocity_var": 8, "swing_factor": 0.03},
20
+ "other": {"timing_var": 0.01, "velocity_var": 10, "swing_factor": 0.05}
21
+ }
22
+
23
+ def classify_instrument(self, instrument):
24
+ """Classify instrument type for appropriate humanization"""
25
+ if instrument.is_drum:
26
+ return "drums"
27
+ elif 32 <= instrument.program <= 39: # Bass
28
+ return "bass"
29
+ elif 0 <= instrument.program <= 7: # Piano
30
+ return "chords"
31
+ elif 40 <= instrument.program <= 55: # Strings, orchestra
32
+ return "chords"
33
+ elif 80 <= instrument.program <= 104: # Synth leads, pads
34
+ return "melody"
35
+ else:
36
+ return "melody"
37
+
38
+ def apply_swing(self, notes, swing_factor, tempo):
39
+ """Apply swing/groove to notes"""
40
+ swung_notes = []
41
+ for note in notes:
42
+ # Simple swing: push even 8th notes slightly later
43
+ beat_position = (note.start * tempo / 60) % 1
44
+ if 0.25 < beat_position < 0.75: # Off-beat positions
45
+ note.start += 0.01 * swing_factor
46
+ note.end += 0.01 * swing_factor
47
+ swung_notes.append(note)
48
+ return swung_notes
49
+
50
+ def humanize_midi(self, midi_file, intensity=0.7, style="organic", add_swing=True):
51
+ """Main humanization function"""
52
+ try:
53
+ # Load MIDI file
54
+ midi_data = pretty_midi.PrettyMIDI(midi_file.name)
55
+ tempo = midi_data.estimate_tempo()
56
+
57
+ # Process each instrument
58
+ for instrument in midi_data.instruments:
59
+ inst_type = self.classify_instrument(instrument)
60
+ profile = self.groove_profiles[inst_type]
61
+
62
+ # Apply swing if requested
63
+ if add_swing and inst_type in ["drums", "bass"]:
64
+ instrument.notes = self.apply_swing(
65
+ instrument.notes,
66
+ profile["swing_factor"] * intensity,
67
+ tempo
68
+ )
69
+
70
+ # Humanize timing and velocity
71
+ for note in instrument.notes:
72
+ # Humanize timing (more variation for drums)
73
+ timing_shift = np.random.normal(0, profile["timing_var"] * intensity)
74
+ note.start = max(0, note.start + timing_shift)
75
+
76
+ # Humanize note duration (except for drums)
77
+ if not instrument.is_drum:
78
+ duration_shift = np.random.normal(0, profile["timing_var"] * 0.5 * intensity)
79
+ note.end = max(note.start + 0.05, note.end + duration_shift)
80
+
81
+ # Humanize velocity
82
+ vel_pattern = self.get_velocity_pattern(note, instrument, style)
83
+ vel_shift = np.random.randint(-profile["velocity_var"], profile["velocity_var"])
84
+ new_velocity = note.velocity + int(vel_shift * intensity * vel_pattern)
85
+ note.velocity = max(20, min(127, new_velocity))
86
+
87
+ # Save humanized MIDI
88
+ output_path = tempfile.mktemp(suffix='_humanized.mid')
89
+ midi_data.write(output_path)
90
+ return output_path, "βœ… Humanization successful! File is ready for download."
91
+
92
+ except Exception as e:
93
+ return None, f"❌ Error processing file: {str(e)}"
94
+
95
+ def get_velocity_pattern(self, note, instrument, style):
96
+ """Get velocity multiplier based on style and musical context"""
97
+ if style == "organic":
98
+ return 1.0
99
+ elif style == "groovy":
100
+ # Accentuate beats more
101
+ beat_position = (note.start * 2) % 1 # Simple beat detection
102
+ if beat_position < 0.1: # On strong beats
103
+ return 1.2
104
+ else:
105
+ return 0.9
106
+ elif style == "gentle":
107
+ return 0.8
108
+ return 1.0
109
+
110
+ def create_audio_preview(midi_path):
111
+ """Create a simple audio preview from MIDI"""
112
+ try:
113
+ midi_data = pretty_midi.PrettyMIDI(midi_path)
114
+ # Generate audio using fluidsynth (simplified)
115
+ audio_data = midi_data.synthesize()
116
+ return 44100, audio_data.astype(np.float32)
117
+ except:
118
+ return None, None
119
+
120
+ def process_files(files, intensity, style, add_swing):
121
+ if not files:
122
+ return None, None, "Please upload MIDI files to begin."
123
+
124
+ bot = HumanizeBot()
125
+ processed_files = []
126
+ audio_previews = []
127
+
128
+ for file in files:
129
+ humanized_path, message = bot.humanize_midi(file, intensity, style, add_swing)
130
+ if humanized_path:
131
+ processed_files.append(humanized_path)
132
+
133
+ # Create audio preview
134
+ sr, audio = create_audio_preview(humanized_path)
135
+ if audio is not None:
136
+ audio_previews.append((sr, audio))
137
+
138
+ if processed_files:
139
+ return processed_files, audio_previews[0] if audio_previews else None, f"βœ… Successfully processed {len(processed_files)} files!"
140
+ else:
141
+ return None, None, "❌ No files were processed successfully."
142
+
143
+ # Create the Gradio interface
144
+ with gr.Blocks(theme=gr.themes.Soft(), title="HumanizeBot") as demo:
145
+ gr.Markdown("""
146
+ # 🎡 HumanizeBot
147
+ **Remove AI traces from your music and make it sound human-made!**
148
+
149
+ Upload MIDI files from AI music generators to apply natural humanization: subtle timing variations, velocity changes, and musical feel.
150
+ """)
151
+
152
+ with gr.Row():
153
+ with gr.Column(scale=1):
154
+ gr.Markdown("### πŸ“ Upload & Settings")
155
+
156
+ file_input = gr.File(
157
+ file_count="multiple",
158
+ file_types=[".mid", ".midi"],
159
+ label="Upload MIDI Files",
160
+ type="filepath"
161
+ )
162
+
163
+ intensity = gr.Slider(
164
+ 0.1, 1.0,
165
+ value=0.7,
166
+ label="🎚️ Humanization Intensity",
167
+ info="Low = subtle, High = very human"
168
+ )
169
+
170
+ style = gr.Radio(
171
+ ["organic", "groovy", "gentle"],
172
+ value="organic",
173
+ label="🎸 Humanization Style",
174
+ info="Organic = natural, Groovy = rhythmic, Gentle = subtle"
175
+ )
176
+
177
+ add_swing = gr.Checkbox(
178
+ value=True,
179
+ label="πŸ”„ Add Swing/Groove",
180
+ info="Add rhythmic push and pull"
181
+ )
182
+
183
+ process_btn = gr.Button(
184
+ "✨ Humanize My Music!",
185
+ variant="primary",
186
+ size="lg"
187
+ )
188
+
189
+ with gr.Column(scale=1):
190
+ gr.Markdown("### πŸ“₯ Download Results")
191
+
192
+ file_output = gr.File(
193
+ file_count="multiple",
194
+ label="Download Humanized MIDI Files"
195
+ )
196
+
197
+ audio_output = gr.Audio(
198
+ label="Audio Preview (First File)",
199
+ interactive=False
200
+ )
201
+
202
+ status = gr.Textbox(
203
+ label="Status",
204
+ interactive=False,
205
+ max_lines=3
206
+ )
207
+
208
+ # Examples section
209
+ with gr.Accordion("🎯 Examples & Tips", open=False):
210
+ gr.Markdown("""
211
+ **Best used with:**
212
+ - AI-generated MIDI from Soundraw, AIVA, MuseNet, etc.
213
+ - Robotic-sounding drum patterns
214
+ - Static piano or synth sequences
215
+
216
+ **How it works:**
217
+ - Adds subtle timing variations (like a human player)
218
+ - Adjusts velocity (note strength) dynamically
219
+ - Can add swing/groove for rhythmic parts
220
+ - Preserves the original musical content
221
+
222
+ **Pro tip:** Start with intensity 0.7 for balanced results!
223
+ """)
224
+
225
+ # Connect the processing function
226
+ process_btn.click(
227
+ fn=process_files,
228
+ inputs=[file_input, intensity, style, add_swing],
229
+ outputs=[file_output, audio_output, status]
230
+ )
231
+
232
+ gr.Markdown("""
233
+ ---
234
+ *Built with ❀️ using Gradio and PrettyMIDI. Works best with MIDI files from AI music generators.*
235
+ """)
236
+
237
+ if __name__ == "__main__":
238
+ demo.launch(debug=True)