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:: 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, ) 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, ) 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``, ``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:: 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.