Virtual Subharmonic Generator — User Guide
Phantom-bass enhancement by bass-band extraction, tanh harmonic generation, filtered harmonic addition, and optional Mid/Side stereo-width processing.
What this does
Virtual Subharmonic Generator is a phantom-bass processor. It does not synthesize new frequencies below the input. Instead, it extracts a low-frequency band, passes that band through an odd-symmetric tanh waveshaper, filters the resulting upper harmonics, and adds those harmonics to a high-passed version of the source.
The perceptual idea is related to residue or “missing-fundamental” pitch: audible upper partials can reinforce the impression of a lower pitch even when a playback system reproduces the fundamental weakly. In this script, however, the original fundamental is not necessarily removed. With typical settings, Highpass_freq lies below Bass_high_freq, so some low-frequency energy remains physically present alongside the generated harmonics.
What “virtual subharmonic” means here
The script creates upper harmonics from bass content; it does not create a true subharmonic at f/2, f/3, or another lower frequency. “Virtual” refers to the intended perceptual bass reinforcement.
Core behavior:
- Bass-band extraction is performed independently on channels 1 and 2.
tanhis odd-symmetric. A balanced sinusoid therefore produces predominantly odd harmonics (3f, 5f, 7f...). Even harmonics may appear from asymmetry, DC offset, or complex program material, but they are not guaranteed by the transfer function.- Harmonic_level is an addition gain, not a dry/wet crossfade.
- Optional Mid/Side processing changes stereo width; an optional 200-Hz Side low-cut centers low-frequency stereo-difference information.
- The result is always stereo. Mono input is duplicated to stereo; multichannel input uses channels 1 and 2 and discards the rest with a warning.
- There is no Haas-delay stage in v0.6.1. It was removed in v0.3.
Quick start
- Select exactly one Sound object in Praat.
- Run
Virtual_Subharmonic_Generator.praat. - Choose a preset, or use Custom and set the bass band, Drive, Harmonic_level, M/S switch, and Stereo_width.
- Enable Advanced_settings only when you need filter, bass-reference, Side-low-cut, or output-level controls.
- Click OK. The output is named
originalname_subharm_presetname.
Processing pipeline
1. Channel preparation
Mono input is converted to stereo. Stereo input is used directly. If the selected Sound has more than two channels, only channels 1 and 2 are processed and the Info window reports how many channels were discarded.
2. Bass extraction
The same bass-band limits are used for both channels. Named presets define these limits explicitly.
3. Optional bass-reference normalization
With Preserve bass level, the extracted bass enters the waveshaper at its natural level, so Drive is level-dependent. With Normalize bass before waveshaping, each channel’s extracted bass is scaled to Bass_reference_peak before distortion, then its harmonic branch is divided by the same scale factor after filtering.
This makes the nonlinear operating point more consistent across sources of different loudness while restoring the harmonic branch approximately to the source’s original level scale.
4. Harmonic generation
The second filter retains the upper part of the waveshaped bass branch. Because the lower edge is Bass_high_freq, much of the extracted fundamental region is rejected from the harmonic branch.
5. High-pass source and add harmonics
Harmonic_level = 0 therefore returns the high-passed source, not the untouched dry source. Negative Harmonic_level is allowed and adds the harmonic branch with inverted polarity; values above 1 boost it beyond its generated level.
6. Optional M/S width processing
If Mono_bass_side_lowcut is enabled, the Side signal is high-passed at 200 Hz before reconstruction.
7. Output policy
The stereo result is measured once and processed according to Output_mode. See Output level below.
Controls
Main form
| Control | Default | Behavior |
|---|---|---|
| Preset | Custom | Named presets overwrite the musical controls and several Advanced filter/Side settings. |
| Bass_low_freq | 30 Hz | Lower edge of the extracted bass band. Must be below Bass_high_freq. |
| Bass_high_freq | 120 Hz | Upper edge of the extracted bass band and lower edge of the retained harmonic band. |
| Drive | 3.0 | Multiplies the extracted bass before tanh. Higher values push the branch further into saturation and increase upper-harmonic energy. |
| Harmonic_level | 0.6 | Addition gain for the filtered harmonic branch. Not a dry/wet control. Negative and >1 values are accepted and reported. |
| Apply_MS_widening | On | Enables the Mid/Side width stage. |
| Stereo_width | 0.5 | Side multiplier: 0 = mono, 1 = identity, <1 = narrower, >1 = wider. Negative values invert Side polarity. |
| Advanced_settings | Off | Opens the secondary dialog for filter/reference/output controls. |
| Draw_visualization | On | Draws the Praat Picture summary. |
| Play_result | On | Plays the result when processing finishes. |
Advanced settings
| Control | Default | Behavior |
|---|---|---|
| Highpass_freq | 100 Hz | High-pass applied to the original source before harmonic addition. Named presets overwrite it. |
| Harmonic_lowpass | 800 Hz | Upper edge of the retained harmonic band. Named presets overwrite it. |
| Bass_reference_mode | Preserve bass level | Chooses level-dependent Drive or reference-normalized waveshaping. Remains active with named presets. |
| Bass_reference_peak | 0.5 | Target peak used only when bass-reference normalization is selected. |
| Mono_bass_side_lowcut | On | High-passes Side at 200 Hz when M/S processing is active. Named presets overwrite this switch. |
| Output_mode | Normalize to target | Selects the final level policy. Remains active with named presets. |
| Normalize_target | 0.95 | Used only by output modes 2 and 3; must satisfy 0 < target ≤ 1. |
| Near_silence_dB | -80 dB | Below this peak threshold, modes that would normalize skip normalization instead of amplifying numerical residue or near-silence. |
Presets
| Preset | Bass band | Drive | Harmonic level | HP / Harmonic LP | M/S | Side low-cut |
|---|---|---|---|---|---|---|
| Custom | 30–120 Hz | 3.0 | 0.60 | 100 / 800 Hz | On, width 0.50 | On |
| Subtle Enhancement | 30–100 Hz | 2.0 | 0.40 | 90 / 600 Hz | On, width 0.30 | On |
| Moderate Effect | 30–120 Hz | 3.0 | 0.60 | 100 / 800 Hz | On, width 0.50 | On |
| Aggressive MaxxBass | 25–150 Hz | 5.0 | 0.80 | 120 / 1000 Hz | On, width 1.30 | Off |
| Bass-Centered Narrowing | 30–110 Hz | 2.5 | 0.50 | 100 / 700 Hz | On, width 0.40 | On |
| Wide Stereo | 30–130 Hz | 3.5 | 0.65 | 100 / 900 Hz | On, width 1.40 | Off |
The table shows the values that each named preset overwrites. Bass-reference and output-policy settings are intentionally not part of the presets.
M/S stereo processing
Width semantics
Stereo_width = 1 is the identity mapping. Values below 1 narrow the image; values above 1 widen it; 0 collapses the M/S stage to mono. Negative values reverse Side polarity.
200-Hz Side low-cut
When enabled and when M/S processing is active, the script applies:
This removes low-frequency stereo-difference information and therefore tends to center the bass. It is not a general “mono compatibility” switch: for this M/S reconstruction, the fold-down identity L' + R' = 2M holds regardless of Stereo_width and regardless of the Side low-cut.
Output level
The output is combined to stereo first, then its absolute peak is measured once. The selected Output mode determines whether that rendered result is left alone, normalized, or attenuated.
| Mode | Exact behavior |
|---|---|
| Preserve rendered level (safety attenuation only) | Leaves the rendered level unchanged when peak ≤ 0.999. If peak > 0.999, globally attenuates to 0.999. It never raises a quiet signal. |
| Normalize to target | Scales every non-near-silent result to Normalize_target. Near-silence is left unchanged. |
| Attenuate to target only if peak > target | Reduces an over-target result to Normalize_target; quieter signals are unchanged. This is conditional global attenuation, not a dynamics limiter. |
| Legacy (always normalize to 0.95) | Reproduces the old fixed 0.95 peak-normalization policy, except that v0.6.1 still protects near-silence from being amplified. |
The Info window reports the pre-output peak, the exact output action, the scale factor actually applied, and the final peak.
Visualization
When Draw_visualization is enabled, the 8-inch Praat Picture page contains:
- Processing chain — Input → Bass→Harmonics → HP + Mix → M/S Width → Output. The 200-Hz Side-HP badge appears only when that stage is actually active.
- Parameter report — bass band, Drive, Harmonic_level, bass-reference mode, filter settings, M/S status, Side-low-cut status, and Output mode.
- 30-ms zoom overlay — original channel 1 in gray and enhanced channel 1 in purple. The window is relative to the Sound’s actual start time, so non-zero-start Sounds are supported.
- Full output waveform — channel 1 in blue and channel 2 in orange.
- Summary strip — preset, source name, bass controls, bass-reference mode, M/S status, Side low-cut, Output mode, duration, and final peak.
Notes and limitations
- No true subharmonic synthesis: the processor generates upper harmonics from bass content; it does not create new lower fundamentals.
- Perceptual bass enhancement is source- and playback-dependent: the added harmonic pattern can reinforce low-pitch perception, but the effect is not guaranteed for every signal or loudspeaker.
- No Haas effect: any documentation referring to a channel delay, precedence effect, Haas mix, or level difference describes obsolete pre-v0.3 behavior.
- No oversampling:
tanhis evaluated at the source sample rate. The following harmonic band-pass limits what is retained, but extreme manual frequency/Drive settings can still produce nonlinear aliasing. - Highpass_freq does not necessarily remove the entire extracted bass band: with typical settings it is below Bass_high_freq, so part of the physical fundamental region remains in the output.
- Always stereo: mono is duplicated; multichannel input is reduced explicitly to channels 1 and 2.
- Frequency validation: Bass_low_freq must be below Bass_high_freq; Bass_high_freq must be below Harmonic_lowpass and Nyquist; Highpass_freq and Harmonic_lowpass must also be valid relative to Nyquist.