Files
pytheory/docs/guide/effects.rst
T
kennethreitz 119dd2d921 Add conversational prose throughout all 5 guide pages
Sequencing: Score concept, time signatures for non-musicians, Parts as
DAW tracks, arpeggiator/legato/glide context with TB-303 and acid history.
Synths: synthesis philosophy, DX7/Juno/JP-8000 history, practical combos.
Effects: real-music context (The Edge, DJ knobs, shower reverb), why signal
chain order matters, automation as breathing, LFO as repeating automation.
Drums: drums-as-genre foundation, genre group cultural context, fills as
transition signals, drum synthesis as real drum machine techniques.
Playback: three output options context, MIDI as the working musician's path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 14:13:44 -04:00

368 lines
13 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 --> Distortion --> Chorus --> Lowpass Filter --> Delay --> Reverb --> Mix
- **Distortion** first: drives the raw signal before filtering (like
plugging a guitar into a fuzz pedal before the amp).
- **Chorus** second: thickens the distorted signal.
- **Lowpass** third: shapes the tone (like a tone knob on an amp).
- **Delay** fourth: 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:: pycon
>>> # 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)
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:: pycon
>>> # 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:: pycon
>>> # 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:: pycon
>>> # 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.
A Schroeder reverb using 4 parallel comb filters and 2 series allpass
filters. Simulates the natural reflections of a room, hall, or
cathedral.
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:: pycon
>>> # Jazz club ambience
>>> rhodes = score.part("rhodes", synth="fm", envelope="piano",
... reverb=0.4, reverb_decay=1.8)
>>> # Cathedral wash for ambient pads
>>> pad = score.part("pad", synth="supersaw", envelope="pad",
... reverb=0.7, reverb_decay=4.0)
Combining Effects
-----------------
Effects stack naturally. Here are some real-world combinations:
Dub
~~~
Distortion warmth into filtered delay into deep reverb:
.. code-block:: pycon
>>> 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:: pycon
>>> 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:: pycon
>>> 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:: pycon
>>> bass = score.part("bass", synth="sine", envelope="pluck",
... lowpass=200, lowpass_q=1.8,
... distortion=0.4, distortion_drive=2.0)
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:: pycon
>>> 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``, ``reverb``,
``reverb_decay``, ``delay``, ``delay_time``, ``delay_feedback``,
``distortion``, ``distortion_drive``, ``chorus``, ``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:: pycon
>>> 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:: pycon
>>> 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)