Spaces:
Sleeping
Sleeping
ziqiangao
commited on
Commit
·
13586a0
1
Parent(s):
26c1442
surround presets
Browse files
app.py
CHANGED
@@ -22,15 +22,15 @@ def convert_to_wav_float(input_file):
|
|
22 |
return temp_wav.name
|
23 |
|
24 |
|
25 |
-
def apply_reverb_wet_only(audio, samplerate):
|
26 |
"""
|
27 |
-
Apply wet-only reverb using SoX to a single channel.
|
28 |
"""
|
29 |
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tin, \
|
30 |
tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tout:
|
31 |
sf.write(tin.name, audio, samplerate, subtype='FLOAT')
|
32 |
subprocess.run(
|
33 |
-
["sox", tin.name, tout.name, "reverb",
|
34 |
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True
|
35 |
)
|
36 |
wet, _ = sf.read(tout.name, dtype='float32')
|
@@ -39,6 +39,7 @@ def apply_reverb_wet_only(audio, samplerate):
|
|
39 |
return wet
|
40 |
|
41 |
|
|
|
42 |
def sox_filter(audio, samplerate, filter_type, cutoff):
|
43 |
"""
|
44 |
Apply highpass or lowpass filter via SoX.
|
@@ -82,38 +83,55 @@ def extract_phantom_center(input_file, rdf=0.99999):
|
|
82 |
return fs, FL[:len(L)], FR[:len(R)], FC[:len(M)]
|
83 |
|
84 |
|
85 |
-
def create_5_1_surround(input_file):
|
86 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
87 |
fs, FL, FR, FC = extract_phantom_center(input_file)
|
88 |
|
89 |
-
# 2.
|
90 |
wav = convert_to_wav_float(input_file)
|
91 |
stereo, _ = sf.read(wav, dtype='float32')
|
92 |
os.unlink(wav)
|
93 |
-
L_orig, R_orig = stereo[:,0], stereo[:,1]
|
94 |
|
95 |
-
# 3. Wet-only reverb
|
96 |
-
SL = apply_reverb_wet_only(L_orig, fs)
|
97 |
-
SR = apply_reverb_wet_only(R_orig, fs)
|
98 |
|
99 |
-
# 4.
|
100 |
-
FL_hp = sox_filter(FL, fs, 'highpass',
|
101 |
-
FR_hp = sox_filter(FR, fs, 'highpass',
|
102 |
-
FC_hp = sox_filter(FC, fs, 'highpass',
|
103 |
-
SL_hp = sox_filter(SL, fs, 'highpass',
|
104 |
-
SR_hp = sox_filter(SR, fs, 'highpass',
|
105 |
|
106 |
-
# 5.
|
107 |
bass_sum = 0.5 * (FL + FR)
|
108 |
-
LFE = sox_filter(bass_sum, fs, 'lowpass',
|
109 |
|
110 |
-
# 6.
|
111 |
channels = [FL_hp, FR_hp, FC_hp, LFE, SL_hp, SR_hp]
|
112 |
length = max(len(ch) for ch in channels)
|
113 |
def pad(x): return np.pad(x, (0, length - len(x)))
|
114 |
multich = np.column_stack([pad(ch) for ch in channels])
|
115 |
|
116 |
-
# 7. Write and encode
|
117 |
out_wav = tempfile.NamedTemporaryFile(suffix='.wav', delete=False)
|
118 |
sf.write(out_wav.name, multich, fs, subtype='FLOAT')
|
119 |
out_wav.close()
|
@@ -126,14 +144,22 @@ def create_5_1_surround(input_file):
|
|
126 |
os.unlink(out_wav.name)
|
127 |
return out_ogg.name
|
128 |
|
|
|
129 |
# ========== Gradio UI ==========
|
130 |
with gr.Blocks(title="Stereo to 5.1 Surround") as demo:
|
131 |
gr.Markdown("# 🎧 Stereo to 5.1 OGG Converter")
|
132 |
-
gr.Markdown("
|
|
|
133 |
inp = gr.Audio(label="Upload stereo audio", type="filepath")
|
|
|
|
|
|
|
|
|
|
|
134 |
btn = gr.Button("Convert to 5.1 OGG")
|
135 |
out = gr.File(label="Download 5.1 OGG")
|
136 |
-
|
|
|
137 |
|
138 |
if __name__ == "__main__":
|
139 |
-
demo.launch()
|
|
|
22 |
return temp_wav.name
|
23 |
|
24 |
|
25 |
+
def apply_reverb_wet_only(audio, samplerate, reverb_args):
|
26 |
"""
|
27 |
+
Apply wet-only reverb using SoX to a single channel with custom reverb args.
|
28 |
"""
|
29 |
with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tin, \
|
30 |
tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as tout:
|
31 |
sf.write(tin.name, audio, samplerate, subtype='FLOAT')
|
32 |
subprocess.run(
|
33 |
+
["sox", tin.name, tout.name, "reverb", "-w"] + reverb_args,
|
34 |
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True
|
35 |
)
|
36 |
wet, _ = sf.read(tout.name, dtype='float32')
|
|
|
39 |
return wet
|
40 |
|
41 |
|
42 |
+
|
43 |
def sox_filter(audio, samplerate, filter_type, cutoff):
|
44 |
"""
|
45 |
Apply highpass or lowpass filter via SoX.
|
|
|
83 |
return fs, FL[:len(L)], FR[:len(R)], FC[:len(M)]
|
84 |
|
85 |
|
86 |
+
def create_5_1_surround(input_file, preset="music"):
|
87 |
+
# Preset-based parameters
|
88 |
+
# Reverberance (50%) HF-damping (50%) room-scale (100%) stereo-depth (100%) pre-delay (0ms) wet-gain (0dB)
|
89 |
+
if preset == "music":
|
90 |
+
hp_cutoff = 120
|
91 |
+
lfe_cutoff = 120
|
92 |
+
reverb_args = ['85', '70', '100', '95', '10', '-2']
|
93 |
+
elif preset == "speech":
|
94 |
+
hp_cutoff = 120
|
95 |
+
lfe_cutoff = 120
|
96 |
+
reverb_args = ['50', '99', '50', '70', '0', '0']
|
97 |
+
elif preset == "open":
|
98 |
+
hp_cutoff = 120
|
99 |
+
lfe_cutoff = 120
|
100 |
+
reverb_args = ['20', '50', '100', '100', '100', '0']
|
101 |
+
else:
|
102 |
+
raise ValueError(f"Unknown preset: {preset}")
|
103 |
+
|
104 |
+
# 1. Extract FL/FR/phantom centre
|
105 |
fs, FL, FR, FC = extract_phantom_center(input_file)
|
106 |
|
107 |
+
# 2. Get stereo original for reverb
|
108 |
wav = convert_to_wav_float(input_file)
|
109 |
stereo, _ = sf.read(wav, dtype='float32')
|
110 |
os.unlink(wav)
|
111 |
+
L_orig, R_orig = stereo[:, 0], stereo[:, 1]
|
112 |
|
113 |
+
# 3. Wet-only reverb with chosen settings
|
114 |
+
SL = apply_reverb_wet_only(L_orig, fs, reverb_args)
|
115 |
+
SR = apply_reverb_wet_only(R_orig, fs, reverb_args)
|
116 |
|
117 |
+
# 4. Highpass filter everything except LFE
|
118 |
+
FL_hp = sox_filter(FL, fs, 'highpass', hp_cutoff)
|
119 |
+
FR_hp = sox_filter(FR, fs, 'highpass', hp_cutoff)
|
120 |
+
FC_hp = sox_filter(FC, fs, 'highpass', hp_cutoff)
|
121 |
+
SL_hp = sox_filter(SL, fs, 'highpass', hp_cutoff)
|
122 |
+
SR_hp = sox_filter(SR, fs, 'highpass', hp_cutoff)
|
123 |
|
124 |
+
# 5. Lowpass for LFE
|
125 |
bass_sum = 0.5 * (FL + FR)
|
126 |
+
LFE = sox_filter(bass_sum, fs, 'lowpass', lfe_cutoff)
|
127 |
|
128 |
+
# 6. Stack and pad
|
129 |
channels = [FL_hp, FR_hp, FC_hp, LFE, SL_hp, SR_hp]
|
130 |
length = max(len(ch) for ch in channels)
|
131 |
def pad(x): return np.pad(x, (0, length - len(x)))
|
132 |
multich = np.column_stack([pad(ch) for ch in channels])
|
133 |
|
134 |
+
# 7. Write WAV and encode to OGG
|
135 |
out_wav = tempfile.NamedTemporaryFile(suffix='.wav', delete=False)
|
136 |
sf.write(out_wav.name, multich, fs, subtype='FLOAT')
|
137 |
out_wav.close()
|
|
|
144 |
os.unlink(out_wav.name)
|
145 |
return out_ogg.name
|
146 |
|
147 |
+
|
148 |
# ========== Gradio UI ==========
|
149 |
with gr.Blocks(title="Stereo to 5.1 Surround") as demo:
|
150 |
gr.Markdown("# 🎧 Stereo to 5.1 OGG Converter")
|
151 |
+
gr.Markdown("Choose music or speech preset for surround processing")
|
152 |
+
|
153 |
inp = gr.Audio(label="Upload stereo audio", type="filepath")
|
154 |
+
preset = gr.Dropdown(
|
155 |
+
label="Select Preset",
|
156 |
+
choices=["music", "speech", "open"],
|
157 |
+
value="music" # or whichever you want as the default
|
158 |
+
)
|
159 |
btn = gr.Button("Convert to 5.1 OGG")
|
160 |
out = gr.File(label="Download 5.1 OGG")
|
161 |
+
|
162 |
+
btn.click(fn=create_5_1_surround, inputs=[inp, preset], outputs=[out])
|
163 |
|
164 |
if __name__ == "__main__":
|
165 |
+
demo.launch()
|