namelessai commited on
Commit
536aac5
·
verified ·
1 Parent(s): ce6ed11

add stereo imaging and neural pan + fix error

Browse files
Files changed (1) hide show
  1. app.py +55 -29
app.py CHANGED
@@ -7,10 +7,10 @@ import gradio as gr
7
 
8
  # Default presets for common genres
9
  PRESETS = {
10
- "rock": {"gain": 6, "compress_threshold": -20, "compress_ratio": 4, "low_pass": 12000, "high_pass": 80},
11
- "pop": {"gain": 4, "compress_threshold": -18, "compress_ratio": 3, "low_pass": 15000, "high_pass": 100},
12
- "jazz": {"gain": 3, "compress_threshold": -22, "compress_ratio": 2, "low_pass": 14000, "high_pass": 60},
13
- "electronic": {"gain": 8, "compress_threshold": -16, "compress_ratio": 5, "low_pass": 20000, "high_pass": 120},
14
  }
15
 
16
  # JSON export extension
@@ -27,33 +27,59 @@ def apply_mastering(audio: AudioSegment, params: dict) -> AudioSegment:
27
  # Gain
28
  processed = audio + params["gain"]
29
  # Compression
30
- processed = compress_dynamic_range(processed,
 
31
  threshold=params["compress_threshold"],
32
- ratio=params["compress_ratio"])
 
33
  # EQ filters
34
  processed = high_pass_filter(processed, params["high_pass"])
35
  processed = low_pass_filter(processed, params["low_pass"])
 
 
 
 
36
  # Normalization
37
  processed = normalize(processed)
38
  return processed
39
 
40
 
41
- def process_and_export(audio_file, preset_name, gain, threshold, ratio, lp, hp):
42
- # Load
43
- audio = AudioSegment.from_file(audio_file.name)
44
- # Determine params
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
45
  if preset_name != "custom":
46
  params = PRESETS[preset_name]
47
  else:
48
  params = {"gain": gain, "compress_threshold": threshold, "compress_ratio": ratio,
49
- "low_pass": lp, "high_pass": hp}
50
- # Process
51
  out = apply_mastering(audio, params)
52
- # Export audio
53
  buf = io.BytesIO()
54
  out.export(buf, format="wav")
55
  buf.seek(0)
56
- # Create preset JSON
57
  preset = {"preset": preset_name, "params": params}
58
  preset_buf = io.BytesIO(json.dumps(preset, indent=2).encode())
59
  preset_buf.name = "preset" + EXPORT_EXT
@@ -65,40 +91,40 @@ def load_preset_json(file_obj):
65
  name = data.get("preset", "custom")
66
  params = data.get("params", {})
67
  return name, params.get("gain", 0), params.get("compress_threshold", 0), \
68
- params.get("compress_ratio", 1), params.get("low_pass", 20000), params.get("high_pass", 20)
 
69
 
70
  with gr.Blocks(css=CUSTOM_CSS) as demo:
71
  gr.Markdown("<div id='title'>Advanced Browser-based Audio Mastering Suite</div>")
72
  with gr.Row():
73
  with gr.Column():
74
- audio_input = gr.Audio(label="Upload Audio", type="file")
 
75
  preset_dropdown = gr.Dropdown(choices=list(PRESETS.keys()) + ["custom"], value="rock", label="Preset")
76
  gain = gr.Slider(-10, 20, value=PRESETS["rock"]["gain"], label="Gain (dB)")
77
  threshold = gr.Slider(-60, 0, value=PRESETS["rock"]["compress_threshold"], label="Compress Threshold (dB)")
78
  ratio = gr.Slider(1, 10, value=PRESETS["rock"]["compress_ratio"], label="Compress Ratio")
79
  lp = gr.Slider(1000, 20000, value=PRESETS["rock"]["low_pass"], label="Low-pass Frequency (Hz)")
80
  hp = gr.Slider(20, 500, value=PRESETS["rock"]["high_pass"], label="High-pass Frequency (Hz)")
 
 
81
  load_preset = gr.File(label="Load .master Preset", file_types=[".master"])
82
  export_button = gr.Button("Process & Export")
83
  with gr.Column():
84
- output_audio = gr.Audio(label="Processed Audio")
 
85
  export_preset_file = gr.File(label="Download Preset (.master)")
86
-
87
- # Interactivity
88
  def sync_sliders(preset):
89
  if preset != "custom":
90
  p = PRESETS[preset]
91
- return gr.update(value=p["gain"]), gr.update(value=p["compress_threshold"]), \
92
- gr.update(value=p["compress_ratio"]), gr.update(value=p["low_pass"]), \
93
- gr.update(value=p["high_pass"])
 
94
  return None
95
-
96
- preset_dropdown.change(sync_sliders, inputs=[preset_dropdown], outputs=[gain, threshold, ratio, lp, hp])
97
- load_preset.upload(load_preset_json, inputs=[load_preset],
98
- outputs=[preset_dropdown, gain, threshold, ratio, lp, hp])
99
- export_button.click(process_and_export,
100
- inputs=[audio_input, preset_dropdown, gain, threshold, ratio, lp, hp],
101
- outputs=[output_audio, export_preset_file])
102
 
103
  if __name__ == "__main__":
104
  demo.launch()
 
7
 
8
  # Default presets for common genres
9
  PRESETS = {
10
+ "rock": {"gain": 6, "compress_threshold": -20, "compress_ratio": 4, "low_pass": 12000, "high_pass": 80, "stereo_width": 100, "pan": 0},
11
+ "pop": {"gain": 4, "compress_threshold": -18, "compress_ratio": 3, "low_pass": 15000, "high_pass": 100, "stereo_width": 100, "pan": 0},
12
+ "jazz": {"gain": 3, "compress_threshold": -22, "compress_ratio": 2, "low_pass": 14000, "high_pass": 60, "stereo_width": 90, "pan": 0},
13
+ "electronic": {"gain": 8, "compress_threshold": -16, "compress_ratio": 5, "low_pass": 20000, "high_pass": 120, "stereo_width": 120, "pan": 0},
14
  }
15
 
16
  # JSON export extension
 
27
  # Gain
28
  processed = audio + params["gain"]
29
  # Compression
30
+ processed = compress_dynamic_range(
31
+ processed,
32
  threshold=params["compress_threshold"],
33
+ ratio=params["compress_ratio"]
34
+ )
35
  # EQ filters
36
  processed = high_pass_filter(processed, params["high_pass"])
37
  processed = low_pass_filter(processed, params["low_pass"])
38
+ # Stereo Imaging (Width)
39
+ processed = apply_stereo_width(processed, params.get("stereo_width", 100))
40
+ # Neural Pan
41
+ processed = apply_pan(processed, params.get("pan", 0))
42
  # Normalization
43
  processed = normalize(processed)
44
  return processed
45
 
46
 
47
+ def apply_stereo_width(audio: AudioSegment, width: float) -> AudioSegment:
48
+ # Width in percent: 0 = mono, 100 = original, >100 = enhanced
49
+ left, right = audio.split_to_mono()
50
+ mid = (left + right) / 2
51
+ side = (left - right) / 2
52
+ # scale side for width control
53
+ side = side * (width / 100)
54
+ new_left = mid + side
55
+ new_right = mid - side
56
+ return AudioSegment.from_mono_audiosegments(new_left, new_right)
57
+
58
+
59
+ def apply_pan(audio: AudioSegment, pan: float) -> AudioSegment:
60
+ # Pan from -100 (left) to 100 (right)
61
+ left, right = audio.split_to_mono()
62
+ pan = max(-100, min(100, pan)) / 100
63
+ if pan > 0:
64
+ new_left = left * (1 - pan)
65
+ new_right = right
66
+ else:
67
+ new_left = left
68
+ new_right = right * (1 + pan)
69
+ return AudioSegment.from_mono_audiosegments(new_left, new_right)
70
+
71
+
72
+ def process_and_export(audio_file, preset_name, gain, threshold, ratio, lp, hp, width, pan):
73
+ audio = AudioSegment.from_file(audio_file)
74
  if preset_name != "custom":
75
  params = PRESETS[preset_name]
76
  else:
77
  params = {"gain": gain, "compress_threshold": threshold, "compress_ratio": ratio,
78
+ "low_pass": lp, "high_pass": hp, "stereo_width": width, "pan": pan}
 
79
  out = apply_mastering(audio, params)
 
80
  buf = io.BytesIO()
81
  out.export(buf, format="wav")
82
  buf.seek(0)
 
83
  preset = {"preset": preset_name, "params": params}
84
  preset_buf = io.BytesIO(json.dumps(preset, indent=2).encode())
85
  preset_buf.name = "preset" + EXPORT_EXT
 
91
  name = data.get("preset", "custom")
92
  params = data.get("params", {})
93
  return name, params.get("gain", 0), params.get("compress_threshold", 0), \
94
+ params.get("compress_ratio", 1), params.get("low_pass", 20000), \
95
+ params.get("high_pass", 20), params.get("stereo_width", 100), params.get("pan", 0)
96
 
97
  with gr.Blocks(css=CUSTOM_CSS) as demo:
98
  gr.Markdown("<div id='title'>Advanced Browser-based Audio Mastering Suite</div>")
99
  with gr.Row():
100
  with gr.Column():
101
+ # Use 'filepath' to get the file path for AudioSegment
102
+ audio_input = gr.Audio(label="Upload Audio", type="filepath")
103
  preset_dropdown = gr.Dropdown(choices=list(PRESETS.keys()) + ["custom"], value="rock", label="Preset")
104
  gain = gr.Slider(-10, 20, value=PRESETS["rock"]["gain"], label="Gain (dB)")
105
  threshold = gr.Slider(-60, 0, value=PRESETS["rock"]["compress_threshold"], label="Compress Threshold (dB)")
106
  ratio = gr.Slider(1, 10, value=PRESETS["rock"]["compress_ratio"], label="Compress Ratio")
107
  lp = gr.Slider(1000, 20000, value=PRESETS["rock"]["low_pass"], label="Low-pass Frequency (Hz)")
108
  hp = gr.Slider(20, 500, value=PRESETS["rock"]["high_pass"], label="High-pass Frequency (Hz)")
109
+ width = gr.Slider(0, 200, value=PRESETS["rock"]["stereo_width"], label="Stereo Width (%)")
110
+ pan = gr.Slider(-100, 100, value=PRESETS["rock"]["pan"], label="Pan (-100 Left to 100 Right)")
111
  load_preset = gr.File(label="Load .master Preset", file_types=[".master"])
112
  export_button = gr.Button("Process & Export")
113
  with gr.Column():
114
+ # Set output to filepath to serve downloadable file
115
+ output_audio = gr.Audio(label="Processed Audio", type="filepath")
116
  export_preset_file = gr.File(label="Download Preset (.master)")
 
 
117
  def sync_sliders(preset):
118
  if preset != "custom":
119
  p = PRESETS[preset]
120
+ return (gr.update(value=p["gain"]), gr.update(value=p["compress_threshold"]), \
121
+ gr.update(value=p["compress_ratio"]), gr.update(value=p["low_pass"]), \
122
+ gr.update(value=p["high_pass"]), gr.update(value=p["stereo_width"]), \
123
+ gr.update(value=p["pan"]))
124
  return None
125
+ preset_dropdown.change(sync_sliders, inputs=[preset_dropdown], outputs=[gain, threshold, ratio, lp, hp, width, pan])
126
+ load_preset.upload(load_preset_json, inputs=[load_preset], outputs=[preset_dropdown, gain, threshold, ratio, lp, hp, width, pan])
127
+ export_button.click(process_and_export, inputs=[audio_input, preset_dropdown, gain, threshold, ratio, lp, hp, width, pan], outputs=[output_audio, export_preset_file])
 
 
 
 
128
 
129
  if __name__ == "__main__":
130
  demo.launch()