feat(synth): Add advanced frequency management for 8-bit synth MIDI and effects
Browse filesThis commit introduces a suite of new tools specifically for the **8-bit synthesizer**, providing fine-grained control over the frequency spectrum to address issues with both excessive harshness in the high-end and muddiness in the low-end.
These features empower users to create cleaner, more balanced, and professional-sounding mixes directly within the synthesizer.
**1. MIDI Pre-processing (For 8-bit Synth): Low-Pitch Management**
Complements the existing high-pitch taming by adding a new "Low-Pitch Attenuation" rule.
Users can now define a low pitch threshold (e.g., C2) and a velocity scale.
The pre-processor will automatically reduce the velocity of any notes falling below this threshold before they are sent to the **8-bit synthesizer**.
This is a preventative measure to control excessive sub-bass energy, reduce muddiness, and prevent clipping.
**2. Delay/Echo Effect (For 8-bit Synth): Full Frequency Spectrum Control**
The Delay/Echo effect has been upgraded with a comprehensive "Frequency Management" toolkit, allowing users to shape the timbre of the echoes generated by the **8-bit synthesizer**.
**High-Pass Filter:** A configurable high-pass filter can be applied to the echo layer, cleanly removing low frequencies to prevent echoes from adding mud to the mix.
**Low-Pass Filter:** A new, corresponding low-pass filter can be applied to the echo layer, removing high frequencies to make echoes sound darker, warmer, and less harsh.
**Flexible Pitch Shifting (Transposer):** The previous fixed octave checkboxes have been replaced with powerful pitch-shifting sliders for both bass and treble notes, allowing for the creation of complex harmonies in the echo trails.
The audio filtering pipeline in `Render_MIDI` was refactored to support this multi-filter audio processing on the dedicated echo layer.
@@ -182,6 +182,10 @@ class AppParameters:
|
|
182 |
s8bit_enable_midi_preprocessing: bool = True # Master switch for this feature
|
183 |
s8bit_high_pitch_threshold: int = 84 # Pitch (C6) above which velocity is scaled
|
184 |
s8bit_high_pitch_velocity_scale: float = 0.8 # Velocity multiplier for high notes (e.g., 80%)
|
|
|
|
|
|
|
|
|
185 |
s8bit_chord_density_threshold: int = 4 # Min number of notes to be considered a dense chord
|
186 |
s8bit_chord_velocity_threshold: int = 100 # Min average velocity for a chord to be tamed
|
187 |
s8bit_chord_velocity_scale: float = 0.75 # Velocity multiplier for loud, dense chords
|
@@ -202,6 +206,12 @@ class AppParameters:
|
|
202 |
s8bit_delay_division: str = "Dotted 8th Note"
|
203 |
s8bit_delay_feedback: float = 0.5 # Velocity scale for each subsequent echo (50%)
|
204 |
s8bit_delay_repeats: int = 3 # Number of echoes to generate
|
|
|
|
|
|
|
|
|
|
|
|
|
205 |
|
206 |
# =================================================================================================
|
207 |
# === Helper Functions ===
|
@@ -341,32 +351,42 @@ def format_params_for_metadata(params: AppParameters, transcription_log: dict =
|
|
341 |
def preprocess_midi_for_harshness(midi_data: pretty_midi.PrettyMIDI, params: AppParameters):
|
342 |
"""
|
343 |
Analyzes and modifies a PrettyMIDI object in-place to reduce characteristics
|
344 |
-
that can cause harshness in simple synthesizers.
|
|
|
345 |
|
346 |
Args:
|
347 |
midi_data: The PrettyMIDI object to process.
|
348 |
params: The AppParameters object containing the control thresholds.
|
349 |
"""
|
350 |
-
print("Running MIDI pre-processing to reduce harshness...")
|
351 |
-
|
|
|
352 |
chords_tamed = 0
|
353 |
-
|
354 |
-
# Rule 1: High
|
355 |
for instrument in midi_data.instruments:
|
356 |
for note in instrument.notes:
|
|
|
357 |
if note.pitch > params.s8bit_high_pitch_threshold:
|
358 |
-
original_velocity = note.velocity
|
359 |
note.velocity = int(note.velocity * params.s8bit_high_pitch_velocity_scale)
|
360 |
if note.velocity < 1: note.velocity = 1
|
361 |
-
|
362 |
-
|
363 |
-
|
364 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
365 |
|
366 |
-
# Rule
|
367 |
# This is a simplified approach: group notes by near-simultaneous start times
|
368 |
all_notes = sorted([note for instrument in midi_data.instruments for note in instrument.notes], key=lambda x: x.start)
|
369 |
-
|
370 |
time_window = 0.02 # 20ms window to group notes into a chord
|
371 |
i = 0
|
372 |
while i < len(all_notes):
|
@@ -660,12 +680,21 @@ def create_delay_effect(midi_data: pretty_midi.PrettyMIDI, params: AppParameters
|
|
660 |
print(" - No notes found to apply delay to. Skipping.")
|
661 |
return processed_midi
|
662 |
|
663 |
-
# --- Step 3: Generate echo notes using the calculated delay time ---
|
664 |
echo_notes = []
|
|
|
|
|
|
|
665 |
for i in range(1, params.s8bit_delay_repeats + 1):
|
666 |
for original_note in notes_to_echo:
|
667 |
# Create a copy of the note for the echo
|
668 |
echo_note = copy.copy(original_note)
|
|
|
|
|
|
|
|
|
|
|
|
|
669 |
|
670 |
# Use the tempo-synced time and velocity
|
671 |
time_offset = i * delay_time_s
|
@@ -696,6 +725,42 @@ def create_delay_effect(midi_data: pretty_midi.PrettyMIDI, params: AppParameters
|
|
696 |
return processed_midi
|
697 |
|
698 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
699 |
def one_pole_lowpass(x, cutoff_hz, fs):
|
700 |
"""Simple one-pole lowpass filter (causal), stable and cheap."""
|
701 |
if cutoff_hz <= 0 or cutoff_hz >= fs/2:
|
@@ -1832,18 +1897,83 @@ def Render_MIDI(*, input_midi_path: str, params: AppParameters, progress: gr.Pro
|
|
1832 |
arpeggiated_midi = arpeggiate_midi(base_midi, params)
|
1833 |
|
1834 |
# --- Step 2: Render the main (original) layer ---
|
1835 |
-
print(" - Rendering main synthesis layer...")
|
1836 |
# Synthesize the waveform, passing new FX parameters to the synthesis function
|
1837 |
-
|
1838 |
midi_data=base_midi,
|
1839 |
fs=srate,
|
1840 |
params=params,
|
1841 |
progress=progress
|
1842 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1843 |
|
1844 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1845 |
|
1846 |
-
# --- Step
|
1847 |
if arpeggiated_midi and arpeggiated_midi.instruments:
|
1848 |
print(" - Rendering and mixing arpeggiator layer...")
|
1849 |
# Temporarily override panning for the arpeggiator synth call
|
@@ -3764,6 +3894,28 @@ if __name__ == "__main__":
|
|
3764 |
label="Number of Repeats",
|
3765 |
info="The total number of echoes to generate for each note."
|
3766 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3767 |
|
3768 |
# --- Section 2: MIDI Pre-processing (Corrective Tool) ---
|
3769 |
with gr.Accordion("MIDI Pre-processing (Corrective Tool)", open=False):
|
@@ -3783,6 +3935,17 @@ if __name__ == "__main__":
|
|
3783 |
label="High Pitch Velocity Scale",
|
3784 |
info="Multiplier for high notes' velocity (e.g., 0.8 = 80% of original velocity)."
|
3785 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3786 |
s8bit_chord_density_threshold = gr.Slider(
|
3787 |
2, 10, value=4, step=1,
|
3788 |
label="Chord Density Threshold",
|
|
|
182 |
s8bit_enable_midi_preprocessing: bool = True # Master switch for this feature
|
183 |
s8bit_high_pitch_threshold: int = 84 # Pitch (C6) above which velocity is scaled
|
184 |
s8bit_high_pitch_velocity_scale: float = 0.8 # Velocity multiplier for high notes (e.g., 80%)
|
185 |
+
# --- Low-pitch management parameters ---
|
186 |
+
s8bit_low_pitch_threshold: int = 36 # Low pitch threshold (C2)
|
187 |
+
s8bit_low_pitch_velocity_scale: float = 0.9 # Low pitch velocity scale
|
188 |
+
|
189 |
s8bit_chord_density_threshold: int = 4 # Min number of notes to be considered a dense chord
|
190 |
s8bit_chord_velocity_threshold: int = 100 # Min average velocity for a chord to be tamed
|
191 |
s8bit_chord_velocity_scale: float = 0.75 # Velocity multiplier for loud, dense chords
|
|
|
206 |
s8bit_delay_division: str = "Dotted 8th Note"
|
207 |
s8bit_delay_feedback: float = 0.5 # Velocity scale for each subsequent echo (50%)
|
208 |
s8bit_delay_repeats: int = 3 # Number of echoes to generate
|
209 |
+
# --- NEW: Low-End Management for Delay ---
|
210 |
+
s8bit_delay_highpass_cutoff_hz: int = 100 # High-pass filter frequency for delay echoes (removes low-end rumble from echoes)
|
211 |
+
s8bit_delay_bass_pitch_shift: int = 0 # Pitch shift (in semitones) applied to low notes in delay echoes
|
212 |
+
# --- High-End Management for Delay ---
|
213 |
+
s8bit_delay_lowpass_cutoff_hz: int = 5000 # Lowpass filter frequency for delay echoes (removes harsh high frequencies from echoes)
|
214 |
+
s8bit_delay_treble_pitch_shift: int = 0 # Pitch shift (in semitones) applied to high notes in delay echoes
|
215 |
|
216 |
# =================================================================================================
|
217 |
# === Helper Functions ===
|
|
|
351 |
def preprocess_midi_for_harshness(midi_data: pretty_midi.PrettyMIDI, params: AppParameters):
|
352 |
"""
|
353 |
Analyzes and modifies a PrettyMIDI object in-place to reduce characteristics
|
354 |
+
that can cause harshness or muddiness in simple synthesizers.
|
355 |
+
Now includes both high and low pitch attenuation.
|
356 |
|
357 |
Args:
|
358 |
midi_data: The PrettyMIDI object to process.
|
359 |
params: The AppParameters object containing the control thresholds.
|
360 |
"""
|
361 |
+
print("Running MIDI pre-processing to reduce harshness and muddiness...")
|
362 |
+
high_notes_tamed = 0
|
363 |
+
low_notes_tamed = 0
|
364 |
chords_tamed = 0
|
365 |
+
|
366 |
+
# Rule 1 & 2: High and Low Pitch Attenuation
|
367 |
for instrument in midi_data.instruments:
|
368 |
for note in instrument.notes:
|
369 |
+
# Tame very high notes to reduce harshness/aliasing
|
370 |
if note.pitch > params.s8bit_high_pitch_threshold:
|
|
|
371 |
note.velocity = int(note.velocity * params.s8bit_high_pitch_velocity_scale)
|
372 |
if note.velocity < 1: note.velocity = 1
|
373 |
+
high_notes_tamed += 1
|
374 |
+
|
375 |
+
# Tame very low notes to reduce muddiness/rumble
|
376 |
+
if note.pitch < params.s8bit_low_pitch_threshold:
|
377 |
+
note.velocity = int(note.velocity * params.s8bit_low_pitch_velocity_scale)
|
378 |
+
if note.velocity < 1: note.velocity = 1
|
379 |
+
low_notes_tamed += 1
|
380 |
+
|
381 |
+
if high_notes_tamed > 0:
|
382 |
+
print(f" - Tamed {high_notes_tamed} individual high-pitched notes.")
|
383 |
+
if low_notes_tamed > 0:
|
384 |
+
print(f" - Tamed {low_notes_tamed} individual low-pitched notes.")
|
385 |
|
386 |
+
# Rule 3: Chord Compression
|
387 |
# This is a simplified approach: group notes by near-simultaneous start times
|
388 |
all_notes = sorted([note for instrument in midi_data.instruments for note in instrument.notes], key=lambda x: x.start)
|
389 |
+
|
390 |
time_window = 0.02 # 20ms window to group notes into a chord
|
391 |
i = 0
|
392 |
while i < len(all_notes):
|
|
|
680 |
print(" - No notes found to apply delay to. Skipping.")
|
681 |
return processed_midi
|
682 |
|
683 |
+
# --- Step 3: Generate echo notes with optional octave shift using the calculated delay time ---
|
684 |
echo_notes = []
|
685 |
+
bass_note_threshold = 48 # MIDI note for C3
|
686 |
+
treble_note_threshold = 84 # MIDI note for C6
|
687 |
+
|
688 |
for i in range(1, params.s8bit_delay_repeats + 1):
|
689 |
for original_note in notes_to_echo:
|
690 |
# Create a copy of the note for the echo
|
691 |
echo_note = copy.copy(original_note)
|
692 |
+
|
693 |
+
# --- Octave Shift Logic for both Bass and Treble ---
|
694 |
+
if params.s8bit_delay_bass_pitch_shift and original_note.pitch < bass_note_threshold:
|
695 |
+
echo_note.pitch += params.s8bit_delay_bass_pitch_shift
|
696 |
+
elif params.s8bit_delay_treble_pitch_shift and original_note.pitch > treble_note_threshold:
|
697 |
+
echo_note.pitch += params.s8bit_delay_treble_pitch_shift
|
698 |
|
699 |
# Use the tempo-synced time and velocity
|
700 |
time_offset = i * delay_time_s
|
|
|
725 |
return processed_midi
|
726 |
|
727 |
|
728 |
+
def butter_highpass(cutoff, fs, order=5):
|
729 |
+
nyq = 0.5 * fs
|
730 |
+
normal_cutoff = cutoff / nyq
|
731 |
+
b, a = signal.butter(order, normal_cutoff, btype='high', analog=False)
|
732 |
+
return b, a
|
733 |
+
|
734 |
+
def apply_butter_highpass_filter(data, cutoff, fs, order=5):
|
735 |
+
"""Applies a Butterworth highpass filter to a stereo audio signal."""
|
736 |
+
if cutoff <= 0:
|
737 |
+
return data
|
738 |
+
b, a = butter_highpass(cutoff, fs, order=order)
|
739 |
+
# Apply filter to each channel independently
|
740 |
+
filtered_data = np.zeros_like(data)
|
741 |
+
for channel in range(data.shape[1]):
|
742 |
+
filtered_data[:, channel] = signal.lfilter(b, a, data[:, channel])
|
743 |
+
return filtered_data
|
744 |
+
|
745 |
+
|
746 |
+
def butter_lowpass(cutoff, fs, order=5):
|
747 |
+
nyq = 0.5 * fs
|
748 |
+
normal_cutoff = cutoff / nyq
|
749 |
+
b, a = signal.butter(order, normal_cutoff, btype='low', analog=False)
|
750 |
+
return b, a
|
751 |
+
|
752 |
+
def apply_butter_lowpass_filter(data, cutoff, fs, order=5):
|
753 |
+
"""Applies a Butterworth lowpass filter to a stereo audio signal."""
|
754 |
+
# A cutoff at or above Nyquist frequency is pointless
|
755 |
+
if cutoff >= fs / 2:
|
756 |
+
return data
|
757 |
+
b, a = butter_lowpass(cutoff, fs, order=order)
|
758 |
+
filtered_data = np.zeros_like(data)
|
759 |
+
for channel in range(data.shape[1]):
|
760 |
+
filtered_data[:, channel] = signal.lfilter(b, a, data[:, channel])
|
761 |
+
return filtered_data
|
762 |
+
|
763 |
+
|
764 |
def one_pole_lowpass(x, cutoff_hz, fs):
|
765 |
"""Simple one-pole lowpass filter (causal), stable and cheap."""
|
766 |
if cutoff_hz <= 0 or cutoff_hz >= fs/2:
|
|
|
1897 |
arpeggiated_midi = arpeggiate_midi(base_midi, params)
|
1898 |
|
1899 |
# --- Step 2: Render the main (original) layer ---
|
1900 |
+
print(" - Rendering main synthesis layer (including echoes)...")
|
1901 |
# Synthesize the waveform, passing new FX parameters to the synthesis function
|
1902 |
+
main_and_echo_waveform = synthesize_8bit_style(
|
1903 |
midi_data=base_midi,
|
1904 |
fs=srate,
|
1905 |
params=params,
|
1906 |
progress=progress
|
1907 |
)
|
1908 |
+
|
1909 |
+
# --- Isolate and filter the echo part if it exists ---
|
1910 |
+
echo_instrument = None
|
1911 |
+
for inst in base_midi.instruments:
|
1912 |
+
if inst.name == "Echo Layer":
|
1913 |
+
echo_instrument = inst
|
1914 |
+
break
|
1915 |
|
1916 |
+
# --- Step 3: Render the delay layers (if enabled) ---
|
1917 |
+
if echo_instrument:
|
1918 |
+
print(" - Processing echo layer audio effects...")
|
1919 |
+
# Create a temporary MIDI object with ONLY the echo instrument
|
1920 |
+
echo_only_midi = pretty_midi.PrettyMIDI()
|
1921 |
+
echo_only_midi.instruments.append(echo_instrument)
|
1922 |
+
|
1923 |
+
# Render ONLY the echo layer to an audio waveform
|
1924 |
+
echo_waveform_raw = synthesize_8bit_style(midi_data=echo_only_midi, fs=srate, params=params)
|
1925 |
+
|
1926 |
+
# --- Start of the Robust Filtering Block ---
|
1927 |
+
# Apply both High-Pass and Low-Pass filters
|
1928 |
+
unfiltered_echo = echo_waveform_raw
|
1929 |
+
filtered_echo = echo_waveform_raw
|
1930 |
+
|
1931 |
+
# --- Apply Filters if requested ---
|
1932 |
+
# Convert to a format filter function expects (samples, channels)
|
1933 |
+
# This is inefficient, we should only do it once.
|
1934 |
+
# Let's assume the filter functions are adapted to take (channels, samples)
|
1935 |
+
# For now, we'll keep the transpose for simplicity.
|
1936 |
+
# We will apply filters on a temporary copy to avoid chaining issues.
|
1937 |
+
temp_filtered_echo = echo_waveform_raw.T
|
1938 |
+
|
1939 |
+
should_filter = False
|
1940 |
+
# Apply High-Pass Filter
|
1941 |
+
if params.s8bit_delay_highpass_cutoff_hz > 0:
|
1942 |
+
print(f" - Applying high-pass filter at {params.s8bit_delay_highpass_cutoff_hz} Hz...")
|
1943 |
+
temp_filtered_echo = apply_butter_highpass_filter(temp_filtered_echo, params.s8bit_delay_highpass_cutoff_hz, srate)
|
1944 |
+
should_filter = True
|
1945 |
+
|
1946 |
+
# Apply Low-Pass Filter
|
1947 |
+
if params.s8bit_delay_lowpass_cutoff_hz < srate / 2:
|
1948 |
+
print(f" - Applying low-pass filter at {params.s8bit_delay_lowpass_cutoff_hz} Hz...")
|
1949 |
+
temp_filtered_echo = apply_butter_lowpass_filter(temp_filtered_echo, params.s8bit_delay_lowpass_cutoff_hz, srate)
|
1950 |
+
should_filter = True
|
1951 |
+
|
1952 |
+
# Convert back and get the difference
|
1953 |
+
if should_filter:
|
1954 |
+
filtered_echo = temp_filtered_echo.T
|
1955 |
+
|
1956 |
+
# To avoid re-rendering, we subtract the unfiltered echo and add the filtered one
|
1957 |
+
# Ensure all waveforms have the same length before math ---
|
1958 |
+
target_length = main_and_echo_waveform.shape[1]
|
1959 |
+
|
1960 |
+
# Pad the unfiltered echo if it's shorter
|
1961 |
+
len_unfiltered = unfiltered_echo.shape[1]
|
1962 |
+
if len_unfiltered < target_length:
|
1963 |
+
unfiltered_echo = np.pad(unfiltered_echo, ((0, 0), (0, target_length - len_unfiltered)))
|
1964 |
+
|
1965 |
+
# Pad the filtered echo if it's shorter
|
1966 |
+
len_filtered = filtered_echo.shape[1]
|
1967 |
+
if len_filtered < target_length:
|
1968 |
+
filtered_echo = np.pad(filtered_echo, ((0, 0), (0, target_length - len_filtered)))
|
1969 |
+
|
1970 |
+
# Now that all shapes are guaranteed to be identical, perform the operation.
|
1971 |
+
main_and_echo_waveform -= unfiltered_echo[:, :target_length]
|
1972 |
+
main_and_echo_waveform += filtered_echo[:, :target_length]
|
1973 |
+
|
1974 |
+
final_waveform = main_and_echo_waveform
|
1975 |
|
1976 |
+
# --- Step 4: Render the arpeggiator layer (if enabled) ---
|
1977 |
if arpeggiated_midi and arpeggiated_midi.instruments:
|
1978 |
print(" - Rendering and mixing arpeggiator layer...")
|
1979 |
# Temporarily override panning for the arpeggiator synth call
|
|
|
3894 |
label="Number of Repeats",
|
3895 |
info="The total number of echoes to generate for each note."
|
3896 |
)
|
3897 |
+
# --- UI controls for low-end management ---
|
3898 |
+
s8bit_delay_highpass_cutoff_hz = gr.Slider(
|
3899 |
+
0, 500, value=100, step=10,
|
3900 |
+
label="Echo High-Pass Filter (Hz)",
|
3901 |
+
info="Filters out low frequencies from the echoes to prevent muddiness. Set to 0 to disable. 80-120Hz is a good range to clean up bass."
|
3902 |
+
)
|
3903 |
+
s8bit_delay_bass_pitch_shift = gr.Slider(
|
3904 |
+
-12, 24, value=12, step=1,
|
3905 |
+
label="Echo Pitch Shift for Low Notes (Semitones)",
|
3906 |
+
info="Shifts the pitch of echoes for very low notes (below C3). +12 is one octave up, +7 is a perfect fifth. 0 to disable."
|
3907 |
+
)
|
3908 |
+
# --- UI controls for high-end management ---
|
3909 |
+
s8bit_delay_lowpass_cutoff_hz = gr.Slider(
|
3910 |
+
1000, 20000, value=5000, step=500,
|
3911 |
+
label="Echo Low-Pass Filter (Hz)",
|
3912 |
+
info="Filters out high frequencies from the echoes to reduce harshness. Set to 20000 to disable. 4k-8kHz is a good range to make echoes sound 'darker'."
|
3913 |
+
)
|
3914 |
+
s8bit_delay_treble_pitch_shift = gr.Slider(
|
3915 |
+
-24, 12, value=-12, step=1,
|
3916 |
+
label="Echo Pitch Shift for High Notes (Semitones)",
|
3917 |
+
info="Shifts the pitch of echoes for very high notes (above C6). -12 is one octave down. 0 to disable."
|
3918 |
+
)
|
3919 |
|
3920 |
# --- Section 2: MIDI Pre-processing (Corrective Tool) ---
|
3921 |
with gr.Accordion("MIDI Pre-processing (Corrective Tool)", open=False):
|
|
|
3935 |
label="High Pitch Velocity Scale",
|
3936 |
info="Multiplier for high notes' velocity (e.g., 0.8 = 80% of original velocity)."
|
3937 |
)
|
3938 |
+
# --- UI controls for low-pitch management ---
|
3939 |
+
s8bit_low_pitch_threshold = gr.Slider(
|
3940 |
+
21, 60, value=36, step=1,
|
3941 |
+
label="Low Pitch Threshold (MIDI Note)",
|
3942 |
+
info="Notes below this pitch will have their velocity reduced to prevent muddiness. 36 = C2."
|
3943 |
+
)
|
3944 |
+
s8bit_low_pitch_velocity_scale = gr.Slider(
|
3945 |
+
0.1, 1.0, value=0.9, step=0.05,
|
3946 |
+
label="Low Pitch Velocity Scale",
|
3947 |
+
info="Multiplier for low notes' velocity. Use this to gently tame excessive sub-bass."
|
3948 |
+
)
|
3949 |
s8bit_chord_density_threshold = gr.Slider(
|
3950 |
2, 10, value=4, step=1,
|
3951 |
label="Chord Density Threshold",
|