mirror of
https://github.com/kennethreitz/pytheory.git
synced 2026-06-05 14:50:18 +00:00
f06c6f77d1
- index.rst: 16 systems, 60+ presets, 41 waveforms, full feature list - synths.rst: 31 dedicated synths, 60+ presets, complete instrument list - drums.rst: 51 drum sounds, cajón section, bayan pitch bend - effects.rst: cabinet/analog_drift in automatable params - playback.rst: temperament, reference_pitch, KeyboardInterrupt - systems.rst: 16 systems, full microtonal section (shruti JI, maqam Zalzalian, slendro, pelog, thai, makam, carnatic, 19/31-TET, Bohlen-Pierce), TET factory, int tone names, System.tone() - sequencing.rst: Score tuning params documented - tones.rst: enharmonics (Cb/Fb/E#/B#, double sharps/flats, unicode), B#/Cb octave fix, tone validation - chords.rst: enharmonic support cross-reference Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
913 lines
27 KiB
ReStructuredText
913 lines
27 KiB
ReStructuredText
Effects
|
||
=======
|
||
|
||
Effects are how recorded music gets its character. A guitar without
|
||
reverb sounds like it's being played in a closet. A vocal without
|
||
compression sounds thin and amateur. A synth without filtering sounds
|
||
like a test signal. Effects are the difference between "notes" and
|
||
"music" -- they put the sound in a space, give it texture, and make it
|
||
feel alive.
|
||
|
||
Every record you've ever loved was shaped by effects. The cavernous
|
||
reverb on a Phil Collins drum hit. The tape delay on a reggae vocal.
|
||
The distortion on a Hendrix guitar. The chorus on an 80s synth pad.
|
||
These aren't decorations added after the fact; they're fundamental to
|
||
the sound itself.
|
||
|
||
Each part in a Score can have its own effects chain. Effects are set at
|
||
part creation and applied per-part before mixing, so every voice gets
|
||
independent processing.
|
||
|
||
Signal Chain
|
||
------------
|
||
|
||
The order of effects matters -- a lot. Distortion before a lowpass
|
||
filter means you're generating all those rich, crunchy harmonics and
|
||
then sculpting them with the filter. That's warm, controllable,
|
||
musical. Filter before distortion means you're distorting the already-
|
||
filtered signal -- a different, often harsher character. The fixed
|
||
order in PyTheory matches classic analog synth architecture, the same
|
||
signal path used by the Moog, the TB-303, and most hardware synths.
|
||
It's a well-tested order that sounds good by default.
|
||
|
||
Effects are applied in this fixed order::
|
||
|
||
Signal --> Saturation --> Tremolo --> Distortion --> Cabinet --> Chorus
|
||
--> Phaser --> Highpass --> Lowpass --> Delay --> Reverb --> Mix
|
||
|
||
Additionally, these per-note effects are applied before the part effects chain:
|
||
|
||
- **Sub-oscillator**: octave-below sine mixed in at the oscillator stage
|
||
- **Noise layer**: filtered noise mixed per-note for breath/transients
|
||
- **Filter envelope**: per-note lowpass sweep (attack/decay/sustain)
|
||
- **Velocity → brightness**: harder velocity = brighter filter cutoff
|
||
|
||
Part-level effects:
|
||
|
||
- **Saturation** first: subtle even-harmonic warmth (tape/tube color).
|
||
- **Tremolo** second: amplitude LFO modulation.
|
||
- **Distortion** third: drives the signal before filtering.
|
||
- **Cabinet** fourth: speaker cab simulation (rolloff + presence bump).
|
||
- **Chorus** fifth: thickens the signal.
|
||
- **Phaser** sixth: swept allpass notches.
|
||
- **Highpass** seventh: removes low-frequency mud.
|
||
- **Lowpass** eighth: shapes the tone (like a tone knob on an amp).
|
||
- **Delay** ninth: echoes the shaped signal (tap delay / tape echo).
|
||
- **Reverb** last: places everything in a space (room / hall).
|
||
|
||
Distortion
|
||
----------
|
||
|
||
You know what distortion sounds like -- it's the sound of rock and roll.
|
||
An electric guitar through a cranked amplifier. But at lower levels,
|
||
distortion is subtler: it adds warmth, presence, and harmonic richness.
|
||
This is why producers run clean signals through tape machines and tube
|
||
preamps. A little saturation makes everything sound more "real."
|
||
|
||
Soft-clip waveshaping using ``tanh`` -- models the warm saturation of an
|
||
overdriven tube amplifier. At low drive levels it adds harmonic warmth;
|
||
at high levels it becomes an aggressive fuzz.
|
||
|
||
Parameters:
|
||
|
||
- ``distortion``: Wet/dry mix, 0.0--1.0.
|
||
- ``distortion_drive``: Gain before clipping (default 3.0).
|
||
|
||
- 0.5--2 = subtle warmth (tube preamp)
|
||
- 3--8 = overdrive (cranked amp)
|
||
- 10+ = fuzz
|
||
|
||
.. code-block:: python
|
||
|
||
# Warm tube saturation on a bass
|
||
bass = score.part(
|
||
"bass",
|
||
synth="sine",
|
||
envelope="pluck",
|
||
distortion=0.3,
|
||
distortion_drive=2.0,
|
||
)
|
||
|
||
# Heavy fuzz on a lead
|
||
lead = score.part(
|
||
"lead",
|
||
synth="saw",
|
||
envelope="staccato",
|
||
distortion=0.8,
|
||
distortion_drive=10.0,
|
||
)
|
||
|
||
Cabinet Simulation
|
||
------------------
|
||
|
||
A real guitar amp doesn't just distort the signal -- the speaker
|
||
cabinet shapes the tone dramatically. A 12-inch speaker in a closed
|
||
cabinet rolls off the harsh high frequencies above 5 kHz and adds a
|
||
presence bump around 2--3 kHz that gives the sound its "in the room"
|
||
quality. Without a cabinet, distortion sounds thin and fizzy. With
|
||
one, it sounds like a real amp.
|
||
|
||
PyTheory's cabinet simulation applies a speaker rolloff curve (lowpass
|
||
at ~5 kHz) combined with a presence resonance bump, placed in the
|
||
signal chain immediately after distortion -- exactly where it sits in
|
||
a real amp.
|
||
|
||
Parameters:
|
||
|
||
- ``cabinet``: Wet/dry mix, 0.0--1.0 (default 0, off).
|
||
|
||
- 0.3--0.5 = subtle speaker coloring
|
||
- 0.6--0.8 = classic amp-in-a-room
|
||
- 1.0 = full cabinet, no dry signal
|
||
|
||
.. code-block:: python
|
||
|
||
# Classic rock amp tone: distortion into cabinet
|
||
guitar = score.part(
|
||
"guitar",
|
||
synth="saw",
|
||
envelope="pluck",
|
||
distortion=0.6,
|
||
distortion_drive=5.0,
|
||
cabinet=0.8,
|
||
)
|
||
|
||
# Clean amp with just cabinet warmth (no distortion)
|
||
clean = score.part(
|
||
"clean",
|
||
synth="triangle",
|
||
envelope="pluck",
|
||
cabinet=0.5,
|
||
)
|
||
|
||
Analog Drift
|
||
------------
|
||
|
||
Real analog synthesizers are never perfectly in tune. The voltage-
|
||
controlled oscillators drift slightly over time as components warm up
|
||
and temperature fluctuates. This imperfection is actually a big part
|
||
of why vintage analog synths sound so appealing -- the subtle pitch
|
||
wandering gives each note a unique, living quality that static digital
|
||
oscillators lack.
|
||
|
||
The ``analog_drift`` parameter adds slow, random pitch variation to
|
||
each oscillator, modeling this vintage behavior.
|
||
|
||
Parameters:
|
||
|
||
- ``analog_drift``: Drift amount, 0.0--1.0 (default 0, off).
|
||
|
||
- 0.05--0.1 = subtle warmth (studio-grade analog)
|
||
- 0.15--0.25 = noticeable drift (vintage gear warming up)
|
||
- 0.3+ = unstable, wobbly (broken tape machine)
|
||
|
||
.. code-block:: python
|
||
|
||
# Warm vintage pad
|
||
pad = score.part(
|
||
"pad",
|
||
synth="supersaw",
|
||
envelope="pad",
|
||
analog_drift=0.1,
|
||
chorus=0.3,
|
||
)
|
||
|
||
# Lo-fi detuned lead
|
||
lead = score.part(
|
||
"lead",
|
||
synth="saw",
|
||
envelope="pluck",
|
||
analog_drift=0.25,
|
||
)
|
||
|
||
Chorus
|
||
------
|
||
|
||
That shimmery, wide, slightly-out-of-focus sound that defined the
|
||
1980s? That's chorus. Think of the intro to "Come As You Are" by
|
||
Nirvana, or literally any synth pad from 1983 to 1989. It makes one
|
||
instrument sound like two or three playing together, slightly out of
|
||
tune with each other -- which is exactly how a real string section or
|
||
choir sounds rich and full.
|
||
|
||
A slightly detuned, LFO-modulated delayed copy mixed back in. Thickens
|
||
the sound like two musicians playing the same part -- the signature
|
||
effect of the Roland Juno synthesizers.
|
||
|
||
Parameters:
|
||
|
||
- ``chorus``: Wet/dry mix, 0.0--1.0.
|
||
- ``chorus_rate``: LFO speed in Hz. 0.5--1 = slow shimmer, 2--4 = vibrato.
|
||
- ``chorus_depth``: Modulation depth in seconds (default 0.003).
|
||
|
||
.. code-block:: python
|
||
|
||
# Juno-style pad chorus
|
||
pad = score.part(
|
||
"pad",
|
||
synth="supersaw",
|
||
envelope="pad",
|
||
chorus=0.5,
|
||
chorus_rate=1.5,
|
||
chorus_depth=0.003,
|
||
)
|
||
|
||
# Subtle thickening on a clean lead
|
||
lead = score.part(
|
||
"lead",
|
||
synth="triangle",
|
||
envelope="pluck",
|
||
chorus=0.2,
|
||
chorus_rate=0.8,
|
||
)
|
||
|
||
Lowpass Filter
|
||
--------------
|
||
|
||
You know that sound when a DJ turns the knob and everything goes
|
||
underwater? That's a lowpass filter closing down. It removes
|
||
high-frequency content, leaving only the warm, round, bassy
|
||
frequencies below the cutoff point. The lowpass filter is arguably the
|
||
most important effect in all of electronic music -- it's the entire
|
||
sound of acid house, the "wah" in auto-wah, and the reason analog
|
||
synths sound warm instead of harsh.
|
||
|
||
A 12 dB/octave biquad lowpass filter with resonance -- the sound of
|
||
analog synthesizers. Removes frequencies above the cutoff; the resonance
|
||
(Q) parameter adds a peak at the cutoff frequency for that classic
|
||
"acid squelch."
|
||
|
||
Parameters:
|
||
|
||
- ``lowpass``: Cutoff frequency in Hz (0 = off). Reference points:
|
||
|
||
- 200--400 Hz = deep sub bass
|
||
- 800--1500 Hz = warm / muffled
|
||
- 2000--4000 Hz = present lead
|
||
- 5000+ Hz = subtle rolloff
|
||
|
||
- ``lowpass_q``: Resonance / Q factor (default 0.707 = Butterworth flat).
|
||
|
||
- 1.0 = slight peak
|
||
- 2.0 = pronounced
|
||
- 5.0+ = aggressive acid squelch
|
||
|
||
.. code-block:: python
|
||
|
||
# Round bass with gentle filtering
|
||
bass = score.part(
|
||
"bass",
|
||
synth="sine",
|
||
envelope="pluck",
|
||
lowpass=400,
|
||
lowpass_q=1.5,
|
||
)
|
||
|
||
# Acid squelch on a saw lead
|
||
acid = score.part(
|
||
"acid",
|
||
synth="saw",
|
||
envelope="staccato",
|
||
lowpass=1500,
|
||
lowpass_q=5.0,
|
||
legato=True,
|
||
glide=0.03,
|
||
)
|
||
|
||
Delay
|
||
-----
|
||
|
||
Delay is echo. Literally. The Edge from U2 built his entire guitar
|
||
sound around dotted-eighth-note delays. Dub reggae producers like Lee
|
||
"Scratch" Perry and King Tubby turned delay into an art form, feeding
|
||
echoes back into themselves until they spiraled into infinity. At short
|
||
times with low feedback, delay adds rhythmic interest. At long times
|
||
with high feedback, it creates cascading, psychedelic soundscapes.
|
||
|
||
Tempo-synced echoes with feedback. Each repeat feeds back into the
|
||
delay line, creating rhythmic echo trails. High feedback values produce
|
||
the cascading, self-oscillating echoes of dub reggae.
|
||
|
||
Parameters:
|
||
|
||
- ``delay``: Wet/dry mix, 0.0--1.0.
|
||
- ``delay_time``: Time between echoes in seconds. Musically useful
|
||
values at 120 bpm: 0.25 (8th note), 0.375 (dotted 8th),
|
||
0.5 (quarter note).
|
||
- ``delay_feedback``: How much each echo feeds back (0.0--1.0).
|
||
|
||
- 0.3 = a few repeats
|
||
- 0.5 = many repeats
|
||
- 0.7+ = runaway (dub style)
|
||
|
||
.. code-block:: python
|
||
|
||
# Dotted-eighth slapback on a lead
|
||
lead = score.part(
|
||
"lead",
|
||
synth="triangle",
|
||
envelope="strings",
|
||
delay=0.3,
|
||
delay_time=0.375,
|
||
delay_feedback=0.4,
|
||
)
|
||
|
||
# Dub-style runaway echoes
|
||
melodica = score.part(
|
||
"melodica",
|
||
synth="triangle",
|
||
envelope="pluck",
|
||
delay=0.5,
|
||
delay_time=0.66,
|
||
delay_feedback=0.55,
|
||
)
|
||
|
||
Reverb
|
||
------
|
||
|
||
Everyone knows what reverb sounds like, even if they don't know the
|
||
word -- it's the sound of singing in the shower, or clapping in a
|
||
cathedral. It's the natural echo of a space. Without reverb, sounds
|
||
feel uncomfortably close and dry, like someone whispering directly into
|
||
your ear. With it, sounds feel like they exist in a real place. Reverb
|
||
is the most universally used effect in all of recorded music.
|
||
|
||
PyTheory offers two reverb engines: a fast **algorithmic** reverb for
|
||
general use, and **convolution** reverb for photorealistic acoustic
|
||
spaces.
|
||
|
||
Algorithmic Reverb
|
||
~~~~~~~~~~~~~~~~~~
|
||
|
||
A Schroeder reverb using 4 parallel comb filters and 2 series allpass
|
||
filters. Fast, lightweight, and good for general-purpose room
|
||
simulation.
|
||
|
||
Parameters:
|
||
|
||
- ``reverb``: Wet/dry mix, 0.0--1.0.
|
||
|
||
- 0.2--0.4 = subtle space
|
||
- 0.5--0.8 = ambient / dub
|
||
|
||
- ``reverb_decay``: Tail length in seconds.
|
||
|
||
- 0.5 = small room
|
||
- 1.5 = hall
|
||
- 3.0+ = cathedral / dub
|
||
|
||
.. code-block:: python
|
||
|
||
# Jazz club ambience
|
||
rhodes = score.part(
|
||
"rhodes",
|
||
synth="fm",
|
||
envelope="piano",
|
||
reverb=0.4,
|
||
reverb_decay=1.8,
|
||
)
|
||
|
||
Convolution Reverb
|
||
~~~~~~~~~~~~~~~~~~
|
||
|
||
Convolution reverb works by convolving your audio with an *impulse
|
||
response* -- a recording (or simulation) of a real acoustic space.
|
||
Where algorithmic reverb approximates the math of reflections,
|
||
convolution reverb *is* the space. You hear every surface, every
|
||
angle, every material.
|
||
|
||
PyTheory generates synthetic impulse responses that model the acoustic
|
||
properties of real spaces: early reflection patterns, exponential
|
||
decay envelopes, frequency-dependent absorption (high frequencies die
|
||
faster in stone), diffusion density, and subtle pitch modulation from
|
||
irregular surfaces. The result is dramatically more realistic than
|
||
algorithmic reverb, especially for long tails and large spaces.
|
||
|
||
Set ``reverb_type`` to any preset name instead of ``"algorithmic"``:
|
||
|
||
- ``"taj_mahal"`` -- Massive marble dome. 12-second tail, bright early
|
||
reflections, enormously dense and diffuse. The most dramatic verb
|
||
you've ever heard.
|
||
- ``"cathedral"`` -- Gothic stone cathedral. 6 seconds, strong early
|
||
reflections off parallel walls, dark reverberant tail.
|
||
- ``"plate"`` -- EMT 140 plate reverb. 4 seconds, dense, bright, smooth.
|
||
The studio classic that defined pop records from the 60s onward.
|
||
- ``"spring"`` -- Spring reverb tank. 3 seconds, metallic, boingy, lo-fi.
|
||
The sound of surf rock and guitar amps.
|
||
- ``"cave"`` -- Natural cave. 8 seconds, very dark, irregular reflections.
|
||
High frequencies are aggressively absorbed by rock.
|
||
- ``"parking_garage"`` -- Concrete box. 3 seconds, bright, flutter echoes
|
||
from parallel hard walls.
|
||
- ``"canyon"`` -- Open canyon. 5 seconds, sparse discrete echoes (the
|
||
walls are far apart) dissolving into a diffuse tail.
|
||
|
||
Parameters:
|
||
|
||
- ``reverb``: Wet/dry mix, 0.0--1.0.
|
||
- ``reverb_type``: Preset name (default ``"algorithmic"``).
|
||
|
||
.. code-block:: python
|
||
|
||
# FM flute through the Taj Mahal
|
||
flute = score.part(
|
||
"flute",
|
||
synth="fm",
|
||
envelope="bell",
|
||
reverb=0.85,
|
||
reverb_type="taj_mahal",
|
||
delay=0.65,
|
||
delay_time=0.375,
|
||
delay_feedback=0.55,
|
||
)
|
||
|
||
# Cathedral wash for ambient pads
|
||
pad = score.part(
|
||
"pad",
|
||
synth="supersaw",
|
||
envelope="pad",
|
||
reverb=0.7,
|
||
reverb_type="cathedral",
|
||
)
|
||
|
||
# Classic plate on a vocal-style lead
|
||
lead = score.part(
|
||
"lead",
|
||
synth="triangle",
|
||
envelope="strings",
|
||
reverb=0.5,
|
||
reverb_type="plate",
|
||
)
|
||
|
||
# Algorithmic reverb still works as before
|
||
rhodes = score.part(
|
||
"rhodes",
|
||
synth="fm",
|
||
envelope="piano",
|
||
reverb=0.4,
|
||
reverb_decay=1.8,
|
||
)
|
||
|
||
You can switch reverb types mid-song with automation:
|
||
|
||
.. code-block:: python
|
||
|
||
lead = score.part("lead", synth="fm", envelope="bell",
|
||
reverb=0.5, reverb_type="plate")
|
||
lead.add("C5", Duration.WHOLE)
|
||
|
||
# Switch to cathedral for the big section
|
||
lead.set(reverb_type="cathedral", reverb=0.8)
|
||
lead.add("E5", Duration.WHOLE)
|
||
|
||
Combining Effects
|
||
-----------------
|
||
|
||
Effects stack naturally. Here are some real-world combinations:
|
||
|
||
Dub
|
||
~~~
|
||
|
||
Distortion warmth into filtered delay into deep reverb:
|
||
|
||
.. code-block:: python
|
||
|
||
melodica = score.part(
|
||
"melodica",
|
||
synth="triangle",
|
||
envelope="pluck",
|
||
distortion=0.2,
|
||
distortion_drive=2.0,
|
||
lowpass=2000,
|
||
lowpass_q=1.2,
|
||
delay=0.5,
|
||
delay_time=0.66,
|
||
delay_feedback=0.55,
|
||
reverb=0.4,
|
||
reverb_decay=2.5,
|
||
)
|
||
|
||
Acid
|
||
~~~~
|
||
|
||
Resonant lowpass with distortion and delay:
|
||
|
||
.. code-block:: python
|
||
|
||
acid = score.part(
|
||
"acid",
|
||
synth="saw",
|
||
envelope="staccato",
|
||
lowpass=1500,
|
||
lowpass_q=3.0,
|
||
distortion=0.4,
|
||
distortion_drive=4.0,
|
||
delay=0.3,
|
||
delay_time=0.242,
|
||
delay_feedback=0.4,
|
||
)
|
||
|
||
Ambient
|
||
~~~~~~~
|
||
|
||
Wide chorus, long reverb, gentle delay:
|
||
|
||
.. code-block:: python
|
||
|
||
ambient = score.part(
|
||
"ambient",
|
||
synth="supersaw",
|
||
envelope="pad",
|
||
chorus=0.4,
|
||
chorus_rate=0.5,
|
||
delay=0.3,
|
||
delay_time=0.5,
|
||
delay_feedback=0.5,
|
||
reverb=0.7,
|
||
reverb_decay=4.0,
|
||
)
|
||
|
||
808 Bass
|
||
~~~~~~~~
|
||
|
||
Subtle saturation and deep filtering for hip-hop sub bass:
|
||
|
||
.. code-block:: python
|
||
|
||
bass = score.part(
|
||
"bass",
|
||
synth="sine",
|
||
envelope="pluck",
|
||
lowpass=200,
|
||
lowpass_q=1.8,
|
||
distortion=0.4,
|
||
distortion_drive=2.0,
|
||
)
|
||
|
||
Sidechain Compression
|
||
---------------------
|
||
|
||
If you've ever heard a house track where the pad *breathes* — gets
|
||
quiet every time the kick hits and swells back up between beats —
|
||
that's sidechain compression. It's the pumping effect that defines
|
||
modern electronic music. The kick drum triggers a compressor on
|
||
another part, ducking its volume in rhythm with the beat.
|
||
|
||
In PyTheory, the drum hits are the trigger. Any part with
|
||
``sidechain > 0`` gets ducked whenever the kick (or any drum) hits:
|
||
|
||
.. code-block:: python
|
||
|
||
# Classic EDM pump — pad ducks hard on every kick
|
||
pad = score.part(
|
||
"pad",
|
||
synth="supersaw",
|
||
envelope="pad",
|
||
sidechain=0.85,
|
||
sidechain_release=0.15,
|
||
)
|
||
|
||
# Bass breathes with the kick too, but less aggressively
|
||
bass = score.part(
|
||
"bass",
|
||
synth="sine",
|
||
lowpass=250,
|
||
sidechain=0.7,
|
||
sidechain_release=0.1,
|
||
)
|
||
|
||
Parameters:
|
||
|
||
- ``sidechain``: How much to duck, 0.0–1.0 (default 0, off).
|
||
0.5 = subtle pump, 0.7 = noticeable, 0.85 = classic EDM, 1.0 = full silence on hits.
|
||
- ``sidechain_release``: How fast the volume comes back, in seconds
|
||
(default 0.1). Shorter = tighter, longer = more dramatic pump.
|
||
|
||
The lead stays above the pump — don't sidechain everything or the
|
||
whole mix will gasp for air:
|
||
|
||
.. code-block:: python
|
||
|
||
# Lead cuts through — no sidechain
|
||
lead = score.part(
|
||
"lead",
|
||
synth="saw",
|
||
envelope="pluck",
|
||
delay=0.2,
|
||
)
|
||
|
||
Saturation
|
||
----------
|
||
|
||
Saturation is the warm, subtle harmonic enhancement of analog tape
|
||
machines and tube preamps. Unlike distortion (which uses ``tanh`` and
|
||
adds harsh odd harmonics), saturation uses a polynomial waveshaper
|
||
that adds even harmonics -- 2nd and 4th -- which the ear perceives as
|
||
warmth and fullness. It's why records mixed through a Neve console
|
||
sound "bigger" than the same mix done in the box.
|
||
|
||
Parameters:
|
||
|
||
- ``saturation``: Amount, 0.0--1.0 (default 0, off).
|
||
|
||
- 0.05--0.15 = subtle analog warmth (tape machine)
|
||
- 0.2--0.4 = noticeable color (tube preamp)
|
||
- 0.5+ = heavy coloring
|
||
|
||
.. code-block:: python
|
||
|
||
# Warm up a bass
|
||
bass = score.part("bass", synth="saw", saturation=0.2)
|
||
|
||
# Glue a string ensemble
|
||
strings = score.part("strings", instrument="string_ensemble",
|
||
saturation=0.1)
|
||
|
||
Tremolo
|
||
-------
|
||
|
||
Amplitude modulation by a sine LFO. The classic vibrating-amp sound.
|
||
Essential for vibraphone (the rotating discs in the resonator tubes),
|
||
Rhodes electric piano, and surf guitar. Not to be confused with
|
||
vibrato (pitch modulation).
|
||
|
||
Parameters:
|
||
|
||
- ``tremolo_depth``: Modulation depth, 0.0--1.0 (default 0, off).
|
||
- ``tremolo_rate``: LFO speed in Hz (default 5.0).
|
||
|
||
- 3--5 Hz = classic tremolo
|
||
- 5--7 Hz = vibraphone motor speed
|
||
- 8+ Hz = ring-mod territory
|
||
|
||
.. code-block:: python
|
||
|
||
# Classic Fender amp tremolo
|
||
guitar = score.part("guitar", synth="saw", envelope="pluck",
|
||
tremolo_depth=0.3, tremolo_rate=4.0)
|
||
|
||
# Vibraphone with motor
|
||
vib = score.part("vib", instrument="vibraphone") # built in
|
||
|
||
Phaser
|
||
------
|
||
|
||
A chain of allpass filters whose center frequencies are swept by an
|
||
LFO, creating moving notches in the spectrum. The classic "jet
|
||
engine" or "underwater" effect. Think Small Stone, MXR Phase 90, or
|
||
the intro to "Eruption." Different from chorus -- chorus adds a
|
||
detuned copy, phaser cancels specific frequencies.
|
||
|
||
Parameters:
|
||
|
||
- ``phaser``: Wet/dry mix, 0.0--1.0 (default 0, off).
|
||
- ``phaser_rate``: LFO sweep speed in Hz (default 0.5).
|
||
|
||
- 0.1--0.3 = slow, lush sweep
|
||
- 0.5--1.0 = classic phaser
|
||
- 2.0+ = fast, Leslie-like
|
||
|
||
.. code-block:: python
|
||
|
||
# Slow sweep on a pad
|
||
pad = score.part("pad", synth="supersaw", envelope="pad",
|
||
phaser=0.4, phaser_rate=0.2)
|
||
|
||
# Leslie sim on organ (built in)
|
||
organ = score.part("organ", instrument="organ")
|
||
|
||
Highpass Filter
|
||
---------------
|
||
|
||
The opposite of lowpass -- removes low-frequency content below the
|
||
cutoff. Useful for cleaning up mud from pads, keeping multiple bass
|
||
parts from masking each other, or thinning out a sound to sit better
|
||
in a mix.
|
||
|
||
Parameters:
|
||
|
||
- ``highpass``: Cutoff frequency in Hz (0 = off).
|
||
|
||
- 80--150 Hz = clean up sub rumble
|
||
- 200--400 Hz = thin out a pad
|
||
- 500+ Hz = telephone / radio effect
|
||
|
||
- ``highpass_q``: Resonance / Q factor (default 0.707).
|
||
|
||
.. code-block:: python
|
||
|
||
# Clean up sub rumble from a pad
|
||
pad = score.part("pad", synth="supersaw", highpass=120)
|
||
|
||
# Thin out rhythm guitar to leave room for bass
|
||
rhythm = score.part("rhythm", synth="saw", highpass=250)
|
||
|
||
Filter Envelope
|
||
---------------
|
||
|
||
A per-note lowpass filter whose cutoff sweeps over time. This is the
|
||
core of subtractive synthesis -- the reason a Moog bass goes "bwow"
|
||
instead of "boop." The filter opens on the attack and closes during
|
||
decay, giving each note a distinctive timbral shape.
|
||
|
||
Parameters:
|
||
|
||
- ``filter_amount``: Sweep range in Hz (0 = off). How far the filter
|
||
opens above the base cutoff.
|
||
- ``filter_attack``: Time to reach peak cutoff, in seconds (default 0.01).
|
||
- ``filter_decay``: Time to fall to sustain level (default 0.3).
|
||
- ``filter_sustain``: Sustain level as fraction of amount, 0.0--1.0
|
||
(default 0.0 = filter closes completely after decay).
|
||
|
||
.. code-block:: python
|
||
|
||
# Classic synth bass "bwow"
|
||
bass = score.part("bass", instrument="synth_bass") # built in
|
||
|
||
# Acid squelch
|
||
acid = score.part("acid", instrument="acid_bass") # built in
|
||
|
||
# Custom filter sweep on a lead
|
||
lead = score.part("lead", synth="saw",
|
||
filter_amount=4000, filter_attack=0.01,
|
||
filter_decay=0.4, filter_sustain=0.1)
|
||
|
||
Velocity to Brightness
|
||
~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Real instruments get brighter when played harder. ``vel_to_filter``
|
||
maps note velocity to filter cutoff boost, so louder notes have more
|
||
high-frequency content.
|
||
|
||
- ``vel_to_filter``: Cutoff boost in Hz at max velocity (default 0, off).
|
||
|
||
.. code-block:: python
|
||
|
||
# Piano: soft = mellow, loud = bright
|
||
piano = score.part("piano", instrument="piano") # built in
|
||
|
||
# Manual: custom velocity mapping on a lead
|
||
lead = score.part("lead", synth="saw", vel_to_filter=3000)
|
||
|
||
Sub-Oscillator
|
||
--------------
|
||
|
||
An octave-below sine wave mixed in with the main oscillator. Adds
|
||
low-end weight without muddiness -- the sub fills in the fundamental
|
||
while the main oscillator provides harmonic character above.
|
||
|
||
- ``sub_osc``: Mix level, 0.0--1.0 (default 0, off).
|
||
|
||
- 0.1--0.2 = subtle weight (tuba, bass guitar)
|
||
- 0.3--0.5 = heavy sub (808, synth bass)
|
||
|
||
.. code-block:: python
|
||
|
||
# Fat 808 kick-bass
|
||
bass = score.part("bass", instrument="808_bass") # built in
|
||
|
||
# Add weight to any part
|
||
lead = score.part("lead", synth="saw", sub_osc=0.3)
|
||
|
||
Noise Layer
|
||
-----------
|
||
|
||
White noise mixed into each note, following the same amplitude
|
||
envelope. Adds breath for woodwinds, hammer/felt noise for piano,
|
||
bow rosin for strings, and attack transients for percussion.
|
||
|
||
- ``noise_mix``: Mix level, 0.0--1.0 (default 0, off).
|
||
|
||
- 0.02--0.04 = subtle texture (strings, piano)
|
||
- 0.05--0.08 = noticeable breath (woodwinds)
|
||
- 0.1+ = heavy air/texture
|
||
|
||
.. code-block:: python
|
||
|
||
# Breathy flute
|
||
flute = score.part("flute", instrument="flute") # noise_mix=0.08
|
||
|
||
# Add air to any synth
|
||
pad = score.part("pad", synth="supersaw", noise_mix=0.05)
|
||
|
||
Configurable FM
|
||
---------------
|
||
|
||
The FM synth now accepts ``fm_ratio`` and ``fm_index`` parameters,
|
||
letting you dial in specific FM timbres instead of using the defaults.
|
||
|
||
- ``fm_ratio``: Modulator frequency as multiple of carrier (default 2.0).
|
||
Integer ratios = harmonic timbres; non-integer = metallic/inharmonic.
|
||
- ``fm_index``: Modulation depth (default 3.0). Higher = more harmonics.
|
||
|
||
.. code-block:: python
|
||
|
||
# Warm electric piano (low ratio, low index)
|
||
ep = score.part("ep", synth="fm", fm_ratio=1.0, fm_index=1.5)
|
||
|
||
# Bright metallic bell (high ratio, high index)
|
||
bell = score.part("bell", synth="fm", fm_ratio=3.5, fm_index=5.0)
|
||
|
||
# Glockenspiel
|
||
glock = score.part("glock", instrument="glockenspiel") # built in
|
||
|
||
Automation
|
||
----------
|
||
|
||
Static effects are fine for a loop, but music breathes. The filter
|
||
*opens* during the chorus. The reverb *swells* before the drop. The
|
||
distortion *kicks in* when the guitar solo starts. Automation is what
|
||
makes a track feel alive instead of robotic -- it's the difference
|
||
between a static loop and a piece of music that has dynamics, tension,
|
||
and release. If you've ever felt a song "build" toward something,
|
||
you're hearing automation at work.
|
||
|
||
``Part.set()`` changes effect parameters mid-song at the current beat
|
||
position. The renderer splits the audio at automation points and
|
||
processes each section independently:
|
||
|
||
.. code-block:: python
|
||
|
||
lead = score.part("lead", synth="saw", lowpass=400, lowpass_q=3.0)
|
||
|
||
# Verse: filtered and clean
|
||
lead.arpeggio("Cm", bars=4, pattern="up", octaves=2)
|
||
|
||
# Chorus: filter opens, chorus kicks in
|
||
lead.set(lowpass=2000, chorus=0.3)
|
||
lead.arpeggio("Fm", bars=4, pattern="updown", octaves=2)
|
||
|
||
# Drop: full send
|
||
lead.set(lowpass=4000, distortion=0.7, reverb=0.3)
|
||
lead.arpeggio("Gm", bars=4, pattern="updown", octaves=2)
|
||
|
||
Any parameter can be automated: ``lowpass``, ``lowpass_q``, ``highpass``,
|
||
``reverb``, ``reverb_decay``, ``reverb_type``, ``delay``, ``delay_time``,
|
||
``delay_feedback``, ``distortion``, ``distortion_drive``, ``chorus``,
|
||
``phaser``, ``phaser_rate``, ``saturation``, ``tremolo_depth``,
|
||
``tremolo_rate``, ``cabinet``, ``cabinet_brightness``, ``analog_drift``,
|
||
``volume``.
|
||
|
||
LFO Automation
|
||
--------------
|
||
|
||
An LFO -- Low Frequency Oscillator -- is just automation that repeats.
|
||
Instead of manually setting parameter changes, you let a wave shape do
|
||
it for you, cycling back and forth continuously. You already know what
|
||
LFOs sound like, even if you don't know the term. The wobble bass in
|
||
dubstep? That's an LFO on the filter cutoff. Tremolo on a guitar amp?
|
||
LFO on volume. Auto-wah? LFO on filter cutoff with resonance cranked
|
||
up. Vibrato? LFO on pitch. It's one simple concept that produces a
|
||
huge range of effects.
|
||
|
||
``Part.lfo()`` automates a parameter with a low-frequency oscillator,
|
||
generating smooth sweeps over time. This is how filter sweeps, tremolo,
|
||
and auto-wah effects work.
|
||
|
||
.. code-block:: python
|
||
|
||
lead = score.part("lead", synth="saw", lowpass=400)
|
||
|
||
# Slow filter sweep: 400 -> 3000 Hz over 8 bars
|
||
lead.lfo("lowpass", rate=0.125, min=400, max=3000, bars=8)
|
||
lead.arpeggio("Cm", bars=8, pattern="up", octaves=2)
|
||
|
||
Parameters:
|
||
|
||
- ``param``: Parameter name to modulate (``"lowpass"``, ``"reverb"``,
|
||
``"distortion"``, ``"volume"``, ``"chorus"``, ``"delay"``).
|
||
- ``rate``: LFO speed in cycles per bar (default 0.5 = one sweep
|
||
every 2 bars). 0.25 = very slow, 1 = once per bar, 4 = four times
|
||
per bar.
|
||
- ``min`` / ``max``: Parameter value range.
|
||
- ``bars``: Number of bars to run the LFO over (default 4).
|
||
- ``shape``: Waveform shape.
|
||
|
||
- ``"sine"`` -- smooth, natural sweep
|
||
- ``"triangle"`` -- linear up/down
|
||
- ``"saw"`` -- ramp up, snap back
|
||
- ``"square"`` -- abrupt on/off
|
||
|
||
- ``resolution``: How often to insert automation points, in beats
|
||
(default 0.25 = every 16th note). Lower values = smoother curves.
|
||
|
||
Stacking Multiple LFOs
|
||
~~~~~~~~~~~~~~~~~~~~~~~
|
||
|
||
Call ``.lfo()`` multiple times to modulate different parameters
|
||
simultaneously:
|
||
|
||
.. code-block:: python
|
||
|
||
lead = score.part("lead", synth="saw", lowpass=800, reverb=0.1)
|
||
|
||
# Filter opens over 8 bars
|
||
lead.lfo("lowpass", rate=0.125, min=400, max=4000, bars=8)
|
||
# Reverb swells in and out every 2 bars
|
||
lead.lfo("reverb", rate=0.5, min=0.1, max=0.6, bars=8, shape="triangle")
|
||
# Volume tremolo
|
||
lead.lfo("volume", rate=2, min=0.3, max=0.6, bars=8, shape="sine")
|
||
|
||
lead.arpeggio("Cm", bars=8, pattern="updown", octaves=2)
|
||
|
||
Effects are what turn notes into music -- the space, the movement, the character. A dry signal is just information; reverb, delay, and filtering are what make it feel like something. Experiment freely, trust your ears.
|