Jaman commited on
Commit
e44f204
·
verified ·
1 Parent(s): b80a6e6

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +164 -0
app.py ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from flask import Flask, request, jsonify
2
+ import os
3
+ from spleeter.separator import Separator
4
+ import autochord
5
+ import pretty_midi
6
+ import librosa
7
+ import matchering as mg
8
+ from pedalboard import Pedalboard, HighpassFilter, Compressor, Limiter, Reverb, Gain
9
+ from pedalboard.io import AudioFile
10
+ import numpy as np
11
+ from scipy.signal import butter, lfilter
12
+
13
+ app = Flask(__name__)
14
+
15
+ # Function to perform audio separation
16
+ def separate_audio(input_path, output_path):
17
+ separator = Separator('spleeter:5stems')
18
+ os.makedirs(output_path, exist_ok=True)
19
+ separator.separate_to_file(input_path, output_path)
20
+ return {
21
+ "vocals": os.path.join(output_path, 'vocals.wav'),
22
+ "accompaniment": os.path.join(output_path, 'other.wav'),
23
+ "bass": os.path.join(output_path, 'bass.wav'),
24
+ "drums": os.path.join(output_path, 'drums.wav'),
25
+ "piano": os.path.join(output_path, 'piano.wav')
26
+ }
27
+
28
+ # Class to recognize chords and generate MIDI
29
+ class MusicToChordsConverter:
30
+ def __init__(self, audio_file):
31
+ self.audio_file = audio_file
32
+ self.chords = None
33
+ self.midi_chords = pretty_midi.PrettyMIDI()
34
+ self.instrument_chords = pretty_midi.Instrument(program=0) # Acoustic Grand Piano
35
+
36
+ def recognize_chords(self):
37
+ self.chords = autochord.recognize(self.audio_file, lab_fn='chords.lab')
38
+
39
+ def chord_to_midi_notes(self, chord_name):
40
+ note_mapping = {
41
+ 'C:maj': ['C4', 'E4', 'G4'],
42
+ 'C:min': ['C4', 'E-4', 'G4'],
43
+ 'D:maj': ['D4', 'F#4', 'A4'],
44
+ 'D:min': ['D4', 'F4', 'A4'],
45
+ 'E:maj': ['E4', 'G#4', 'B4'],
46
+ 'E:min': ['E4', 'G4', 'B4'],
47
+ 'F:maj': ['F4', 'A4', 'C5'],
48
+ 'F:min': ['F4', 'A-4', 'C5'],
49
+ 'G:maj': ['G4', 'B4', 'D5'],
50
+ 'G:min': ['G4', 'B-4', 'D5'],
51
+ 'A:maj': ['A4', 'C#5', 'E5'],
52
+ 'A:min': ['A4', 'C5', 'E5'],
53
+ 'B:maj': ['B4', 'D#5', 'F#5'],
54
+ 'B:min': ['B4', 'D5', 'F#5']
55
+ }
56
+ return note_mapping.get(chord_name, [])
57
+
58
+ def generate_midi(self):
59
+ for chord in self.chords:
60
+ start_time = chord[0]
61
+ end_time = chord[1]
62
+ chord_name = chord[2]
63
+ if chord_name != 'N':
64
+ chord_notes = self.chord_to_midi_notes(chord_name)
65
+ for note_name in chord_notes:
66
+ midi_note = pretty_midi.Note(
67
+ velocity=100,
68
+ pitch=librosa.note_to_midi(note_name),
69
+ start=start_time,
70
+ end=end_time
71
+ )
72
+ self.instrument_chords.notes.append(midi_note)
73
+ self.midi_chords.instruments.append(self.instrument_chords)
74
+
75
+ def save_midi(self, output_file):
76
+ self.midi_chords.write(output_file)
77
+ return output_file
78
+
79
+ # Function to master the audio
80
+ def master_audio(input_path, reference_path, output_path):
81
+ mg.log(warning_handler=print)
82
+ mg.process(
83
+ target=input_path,
84
+ reference=reference_path,
85
+ results=[mg.pcm16(output_path)],
86
+ preview_target=mg.pcm16("preview_target.flac"),
87
+ preview_result=mg.pcm16("preview_result.flac"),
88
+ )
89
+
90
+ # Function to process audio with pedalboard effects
91
+ def process_audio(input_path, output_path):
92
+ with AudioFile(input_path) as f:
93
+ audio = f.read(f.frames)
94
+ sample_rate = f.samplerate
95
+
96
+ def stereo_widen(audio, width=1.2):
97
+ left_channel = audio[0::2] * width
98
+ right_channel = audio[1::2] * width
99
+ widened_audio = np.empty_like(audio)
100
+ widened_audio[0::2] = left_channel
101
+ widened_audio[1::2] = right_channel
102
+ return widened_audio
103
+
104
+ def reduce_piano_volume(audio, sample_rate, freq_low=200, freq_high=2000, reduction_db=-18):
105
+ nyquist = 0.5 * sample_rate
106
+ low = freq_low / nyquist
107
+ high = freq_high / nyquist
108
+ b, a = butter(1, [low, high], btype='band')
109
+ filtered_audio = lfilter(b, a, audio)
110
+ gain_reduction = 10 ** (reduction_db / 20)
111
+ reduced_audio = audio - (filtered_audio * gain_reduction)
112
+ return reduced_audio
113
+
114
+ board = Pedalboard([
115
+ HighpassFilter(cutoff_frequency_hz=100),
116
+ Compressor(threshold_db=-20, ratio=4),
117
+ Limiter(threshold_db=-0.1),
118
+ Reverb(room_size=0.3, wet_level=0.2),
119
+ Gain(gain_db=3),
120
+ ])
121
+ processed_audio = board(audio, sample_rate)
122
+ processed_audio = stereo_widen(processed_audio)
123
+ processed_audio = reduce_piano_volume(processed_audio, sample_rate)
124
+ with AudioFile(output_path, 'w', sample_rate, processed_audio.shape[0]) as f:
125
+ f.write(processed_audio)
126
+
127
+ @app.route('/process_audio', methods=['POST'])
128
+ def process_audio_api():
129
+ file = request.files['audio']
130
+ input_path = os.path.join('uploads', file.filename)
131
+ os.makedirs('uploads', exist_ok=True)
132
+ file.save(input_path)
133
+
134
+ output_base_path = 'output'
135
+ base_name = os.path.splitext(os.path.basename(input_path))[0]
136
+ output_path = os.path.join(output_base_path, base_name)
137
+ os.makedirs(output_path, exist_ok=True)
138
+
139
+ # Step 1: Separate audio
140
+ separated_files = separate_audio(input_path, output_path)
141
+
142
+ # Step 2: Recognize chords
143
+ converter = MusicToChordsConverter(separated_files['piano'])
144
+ converter.recognize_chords()
145
+ midi_output_file = os.path.join(output_path, f'{base_name}_chords.mid')
146
+ converter.generate_midi()
147
+ converter.save_midi(midi_output_file)
148
+
149
+ # Step 3: Master audio
150
+ master_audio_path = os.path.join(output_path, f'{base_name}_master.wav')
151
+ master_audio(separated_files['piano'], input_path, master_audio_path)
152
+
153
+ # Step 4: Apply pedalboard effects
154
+ final_output_path = os.path.join(output_path, f'{base_name}_final.wav')
155
+ process_audio(master_audio_path, final_output_path)
156
+
157
+ return jsonify({
158
+ 'separated_files': separated_files,
159
+ 'midi_output_file': midi_output_file,
160
+ 'final_output_path': final_output_path
161
+ })
162
+
163
+ if __name__ == "__main__":
164
+ app.run(debug=True)