refine(synth): Make Bass Boost frequency-dependent to prevent muddiness
Browse filesUpgrades the 8-bit synthesizer's Bass Boost feature to be "intelligent."
The effect is now dynamically scaled based on the note's pitch, so it is strongest on mid/high-frequency notes and automatically fades out on low-frequency notes. This prevents the muddiness and clipping that previously occurred when boosting an already deep bassline.
A new UI slider allows users to control the cutoff frequency for this behavior.
app.py
CHANGED
@@ -177,6 +177,7 @@ class AppParameters:
|
|
177 |
s8bit_noise_lowpass_hz: float = 9000.0 # Lowpass filter frequency for noise
|
178 |
s8bit_harmonic_lowpass_factor: float = 12.0 # Multiplier for frequency-dependent lowpass filter
|
179 |
s8bit_final_gain: float = 0.8 # Final gain/limiter level to prevent clipping
|
|
|
180 |
|
181 |
# --- MIDI Pre-processing to Reduce Harshness ---
|
182 |
s8bit_enable_midi_preprocessing: bool = True # Master switch for this feature
|
@@ -1046,19 +1047,33 @@ def synthesize_8bit_style(*, midi_data: pretty_midi.PrettyMIDI, fs: int, params
|
|
1046 |
edge_smooth_ms = getattr(params, 's8bit_edge_smoothing_ms', 0.5)
|
1047 |
note_waveform = smooth_square_or_saw(note_waveform, fs, smooth_ms=edge_smooth_ms)
|
1048 |
|
1049 |
-
# --- Bass Boost (
|
1050 |
if params.s8bit_bass_boost_level > 0:
|
1051 |
-
|
1052 |
-
|
1053 |
-
|
1054 |
-
|
1055 |
-
|
1056 |
-
|
1057 |
-
|
1058 |
-
|
1059 |
-
|
1060 |
-
|
1061 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1062 |
|
1063 |
# --- Noise & Distortion (Reordered and Improved) ---
|
1064 |
if params.s8bit_noise_level > 0:
|
@@ -3725,6 +3740,12 @@ if __name__ == "__main__":
|
|
3725 |
label="Bass Boost Level",
|
3726 |
info="Mixes in a sub-octave (a square wave one octave lower). Low values have no effect; high values add significant weight and power."
|
3727 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
3728 |
s8bit_smooth_notes_level = gr.Slider(
|
3729 |
0.0, 1.0, value=0.0, step=0.05,
|
3730 |
label="Smooth Notes Level",
|
|
|
177 |
s8bit_noise_lowpass_hz: float = 9000.0 # Lowpass filter frequency for noise
|
178 |
s8bit_harmonic_lowpass_factor: float = 12.0 # Multiplier for frequency-dependent lowpass filter
|
179 |
s8bit_final_gain: float = 0.8 # Final gain/limiter level to prevent clipping
|
180 |
+
s8bit_bass_boost_cutoff_hz: float = 200.0 # Parameter for Intelligent Bass Boost
|
181 |
|
182 |
# --- MIDI Pre-processing to Reduce Harshness ---
|
183 |
s8bit_enable_midi_preprocessing: bool = True # Master switch for this feature
|
|
|
1047 |
edge_smooth_ms = getattr(params, 's8bit_edge_smoothing_ms', 0.5)
|
1048 |
note_waveform = smooth_square_or_saw(note_waveform, fs, smooth_ms=edge_smooth_ms)
|
1049 |
|
1050 |
+
# --- Intelligent Bass Boost (Frequency-Dependent) ---
|
1051 |
if params.s8bit_bass_boost_level > 0:
|
1052 |
+
# --- Step 1: Calculate the dynamic boost level for this specific note ---
|
1053 |
+
cutoff_hz = getattr(params, 's8bit_bass_boost_cutoff_hz', 200.0)
|
1054 |
+
|
1055 |
+
# Create a smooth fade-out curve for the boost effect.
|
1056 |
+
# The effect is at 100% strength at the cutoff frequency,
|
1057 |
+
# and fades to 0% at half the cutoff frequency.
|
1058 |
+
# `clip` ensures the value is between 0 and 1.
|
1059 |
+
dynamic_boost_scale = np.clip((freq - (cutoff_hz / 2)) / (cutoff_hz / 2), 0, 1)
|
1060 |
+
|
1061 |
+
# The final boost level is the user's setting multiplied by our dynamic scale.
|
1062 |
+
final_boost_level = params.s8bit_bass_boost_level * dynamic_boost_scale
|
1063 |
+
|
1064 |
+
# --- Step 2: Apply the boost only if it's still audible ---
|
1065 |
+
if final_boost_level > 0.01: # A small threshold to avoid unnecessary computation
|
1066 |
+
bass_freq = freq / 2.0
|
1067 |
+
# Only add bass if the frequency is reasonably audible
|
1068 |
+
if bass_freq > 20:
|
1069 |
+
# Bass uses a simple square wave, no vibrato, for stability
|
1070 |
+
bass_phase_inc = 2 * np.pi * bass_freq / fs
|
1071 |
+
bass_phase = np.cumsum(np.full(num_samples, bass_phase_inc))
|
1072 |
+
bass_sub_waveform = signal.square(bass_phase, duty=0.5)
|
1073 |
+
|
1074 |
+
# The ducking amount is now also dynamic.
|
1075 |
+
main_level = 1.0 - (0.5 * final_boost_level)
|
1076 |
+
note_waveform = (note_waveform * main_level) + (bass_sub_waveform * final_boost_level)
|
1077 |
|
1078 |
# --- Noise & Distortion (Reordered and Improved) ---
|
1079 |
if params.s8bit_noise_level > 0:
|
|
|
3740 |
label="Bass Boost Level",
|
3741 |
info="Mixes in a sub-octave (a square wave one octave lower). Low values have no effect; high values add significant weight and power."
|
3742 |
)
|
3743 |
+
# UI control for Intelligent Bass Boost
|
3744 |
+
s8bit_bass_boost_cutoff_hz = gr.Slider(
|
3745 |
+
50.0, 500.0, value=200.0, step=10.0,
|
3746 |
+
label="Bass Boost Cutoff (Hz)",
|
3747 |
+
info="Intelligent Bass Boost: The boost effect will gradually fade out for notes BELOW this frequency, preventing muddiness in the low-end."
|
3748 |
+
)
|
3749 |
s8bit_smooth_notes_level = gr.Slider(
|
3750 |
0.0, 1.0, value=0.0, step=0.05,
|
3751 |
label="Smooth Notes Level",
|