avans06 commited on
Commit
7a65465
·
1 Parent(s): 0676790

refine(synth): Make Bass Boost frequency-dependent to prevent muddiness

Browse files

Upgrades 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.

Files changed (1) hide show
  1. app.py +33 -12
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 (Sub-Octave Oscillator) ---
1050
  if params.s8bit_bass_boost_level > 0:
1051
- bass_freq = freq / 2.0
1052
- # Only add bass if the frequency is reasonably audible
1053
- if bass_freq > 20:
1054
- # Bass uses a simple square wave, no vibrato, for stability
1055
- bass_phase_inc = 2 * np.pi * bass_freq / fs
1056
- bass_phase = np.cumsum(np.full(num_samples, bass_phase_inc))
1057
- bass_sub_waveform = signal.square(bass_phase, duty=0.5)
1058
- # Mix the main and bass waveforms.
1059
- # As bass level increases, slightly decrease main waveform volume to prevent clipping.
1060
- main_level = 1.0 - (0.5 * params.s8bit_bass_boost_level)
1061
- note_waveform = (note_waveform * main_level) + (bass_sub_waveform * params.s8bit_bass_boost_level)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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",