Audio Playback¶
Note
This module requires PortAudio to be installed on your system.
- pytheory.play.sine_wave(hz, peak=4096, n_samples=44100)[source]¶
Compute N samples of a sine wave with given frequency and peak amplitude. Defaults to one second.
- pytheory.play.sawtooth_wave(hz, peak=4096, n_samples=44100)[source]¶
Compute N samples of a sawtooth wave with given frequency and peak amplitude. Defaults to one second.
- pytheory.play.triangle_wave(hz, peak=4096, n_samples=44100)[source]¶
Compute N samples of a triangle wave with given frequency and peak amplitude. Defaults to one second.
- pytheory.play.square_wave(hz, peak=4096, n_samples=44100)[source]¶
Compute N samples of a square wave — classic chiptune / 8-bit sound.
Hollow and buzzy, containing only odd harmonics (1, 3, 5, 7…) each at amplitude 1/n. The building block of NES and Game Boy music.
- pytheory.play.pulse_wave(hz, peak=4096, n_samples=44100, duty=0.25)[source]¶
Compute N samples of a pulse wave with variable duty cycle.
A generalized square wave. Duty cycle controls the timbre:
50% = square wave (hollow)
25% = nasal, reedy (NES pulse channel default)
12.5% = thin, buzzy (NES narrow pulse)
Width changes dramatically affect the harmonic content — narrower pulses emphasize higher harmonics, producing a brighter, more cutting sound.
- pytheory.play.fm_wave(hz, peak=4096, n_samples=44100, mod_ratio=2.0, mod_index=3.0)[source]¶
Compute N samples of an FM synthesis wave.
One sine wave (the carrier) has its frequency modulated by another sine wave (the modulator). This is the basis of the Yamaha DX7 — the most commercially successful synthesizer ever made.
- Parameters:
hz – Carrier frequency.
mod_ratio – Modulator frequency as a multiple of carrier. Integer ratios (1, 2, 3) produce harmonic timbres (bells, electric piano). Non-integer ratios (1.41, 2.76) produce inharmonic, metallic sounds.
mod_index – Modulation depth. Higher = more harmonics = brighter. 0 = pure sine. 1-3 = warm. 5+ = harsh/metallic.
- Common presets:
Electric piano: ratio=1, index=1.5
Bell: ratio=3.5, index=5
Brass: ratio=1, index=3
Metallic: ratio=1.41, index=8
- pytheory.play.noise_wave(hz=0, peak=4096, n_samples=44100)[source]¶
Compute N samples of white noise.
Unpitched — the
hzparameter is accepted for API compatibility but ignored. Useful for percussion textures, wind effects, and hi-hat-like sounds in melodic parts.
- pytheory.play.supersaw_wave(hz, peak=4096, n_samples=44100, voices=7, detune_cents=15)[source]¶
Compute N samples of a supersaw — multiple detuned saws summed.
The signature sound of trance and EDM. Multiple sawtooth oscillators are slightly detuned from each other, creating a fat, shimmering, chorus-like wall of sound. The Roland JP-8000 (1997) popularized this as “SuperSaw.”
- Parameters:
voices – Number of saw oscillators (default 7). More = fatter.
detune_cents – Maximum detune spread in cents (default 15). Each voice is spread evenly across ±detune_cents.
- pytheory.play.pwm_wave(hz, peak=4096, n_samples=44100, lfo_rate=0.3)[source]¶
Compute N samples of a pulse-width modulated wave.
A pulse wave whose duty cycle sweeps back and forth via an LFO, creating a rich, evolving timbre. This is the signature sound of the Roland Juno-106 and many classic analog polysynths.
As the pulse width changes, different harmonics fade in and out, producing a natural chorus-like shimmer without any detuning. At 50% width it’s a square wave; at narrow widths it thins to a bright, reedy tone. The constant motion between these extremes is what makes PWM so alive-sounding.
- Parameters:
lfo_rate – Speed of the width sweep in Hz. 0.1–0.5 = slow, lush pads. 1–5 = faster, more vibrato/chorus-like.
- pytheory.play.pwm_slow_wave(hz, peak=4096, n_samples=44100)[source]¶
PWM with slow LFO (0.3 Hz) — lush Juno-style pads.
- pytheory.play.pwm_fast_wave(hz, peak=4096, n_samples=44100)[source]¶
PWM with fast LFO (3 Hz) — chorused, vibrato-like texture.
- pytheory.play.hard_sync_wave(hz, peak=4096, n_samples=44100, slave_ratio=1.5)[source]¶
Hard-sync oscillator — slave saw reset by master clock.
The quintessential analog lead sound. A “slave” oscillator runs at a different frequency but is forced to restart its cycle every time the “master” oscillator completes one. The abrupt restart creates bright, harmonically complex formant peaks that sweep as the slave ratio changes.
This is THE sound of the Prophet-5, Moog Prodigy, and every screaming analog lead since 1978.
- Parameters:
slave_ratio – Slave frequency as a multiple of master. 1.0 = unison (plain saw). 1.5–3.0 = sweet spot for leads. Higher = more metallic, ring-mod-like.
- pytheory.play.ring_mod_wave(hz, peak=4096, n_samples=44100, mod_ratio=1.5)[source]¶
Ring modulation — two oscillators multiplied together.
Multiplying two signals produces sum and difference frequencies, creating inharmonic, metallic, bell-like tones. Unlike FM, ring mod produces only sidebands — no carrier or modulator in the output.
Classic Dalek voice, Stockhausen elektronische Musik, and the metallic clang of every sci-fi soundtrack.
- Parameters:
mod_ratio – Modulator frequency as a multiple of carrier. Integer ratios (2, 3) = harmonic (bell-like). Non-integer (1.5, 2.1) = inharmonic (metallic, alien).
- pytheory.play.wavefold_wave(hz, peak=4096, n_samples=44100, folds=3.0)[source]¶
Wavefolding — signal folded back on itself for complex harmonics.
The heart of west coast synthesis (Buchla, Make Noise, Verbos). A sine wave is amplified past ±1.0, then “folded” — the overflow bounces back instead of clipping. Each fold adds a new pair of harmonics. At low fold counts it’s warm and round; crank it up and it gets buzzy, gnarly, and alive.
Sounds completely different from subtractive synthesis — instead of removing harmonics with a filter, you’re generating them by shaping the wave. Pairs beautifully with a lowpass filter.
- Parameters:
folds – Drive amount. 1.0 = clean sine. 2–4 = sweet spot. 6+ = harsh, buzzy territory.
- pytheory.play.drift_wave(hz, peak=4096, n_samples=44100, shape='saw', drift_amount=0.15)[source]¶
Analog VCO with pitch drift, instability, and soft noise floor.
Real analog oscillators are never perfectly stable. Capacitor charging, thermal variations, and component tolerances make the pitch wander slightly. This is what makes a Minimoog sound “fat” and a VST sound “thin” — the constant micro-motion of imperfect hardware.
Models: - Slow pitch drift (< 1 Hz wander, like warming up) - Fast jitter (subtle per-cycle randomness) - Soft analog noise floor (faint hiss blended in) - Slightly rounded edges (no mathematically perfect transitions)
- Parameters:
shape – Base oscillator — “saw”, “square”, “triangle”, or “pulse”.
drift_amount – How unstable the oscillator is. 0.05 = studio-grade (Sequential, Oberheim). 0.15 = classic vintage (Minimoog, ARP). 0.3 = barely-holding-it-together (old SH-101).
- pytheory.play.pluck_wave(hz, peak=4096, n_samples=44100)[source]¶
Karplus-Strong plucked string synthesis.
A burst of noise is fed into a short delay line with feedback — the delay length determines the pitch, and the feedback filter determines the decay. This is how every physical modeling synth since 1983 does plucked strings. It sounds genuinely like a real guitar, harp, or koto — not a synth approximation.
The algorithm: fill a buffer with random noise the length of one period, then repeatedly average adjacent samples. The averaging acts as a lowpass filter, gradually removing high harmonics — exactly what a real vibrating string does as energy dissipates.
- pytheory.play.organ_wave(hz, peak=4096, n_samples=44100)[source]¶
Hammond organ — additive synthesis with drawbar harmonics.
A real Hammond B3 has 9 drawbars that mix sine waves at different harmonics. This models the classic “full” registration with all drawbars pulled: fundamental, 2nd, 3rd, 4th, 5th, 6th, and 8th harmonics at musical levels.
The result is warm, rich, and unmistakably organ — somewhere between a sine wave and a square wave, with that characteristic hollow roundness.
- pytheory.play.strings_wave(hz, peak=4096, n_samples=44100)[source]¶
Bowed string — additive synthesis with natural harmonic rolloff.
Models bowed string physics: - Additive harmonics with 1/n rolloff shaped by body resonance - Delayed vibrato (develops ~200ms in, like a real player) - Subtle bow pressure variation (amplitude modulation) - Per-harmonic phase randomization for natural timbre - Gentle spectral tilt to avoid synthetic brightness
- pytheory.play.piano_wave(hz, peak=4096, n_samples=44100)[source]¶
Piano — steel strings struck by felt hammer.
The piano sound has three key qualities: 1. Metallic ring — steel strings produce clear, ringing harmonics
(especially 2nd and 3rd) that sustain for seconds
Smooth attack — felt hammer absorbs the initial transient, no pick noise or pluck character
Two-stage decay — fast initial drop (~0.3s) as hammer energy dissipates, then a long slow ring as the string sustains
Two slightly detuned strings per note create the natural chorus/shimmer that makes piano sound like piano.
- pytheory.play.rhodes_wave(hz, peak=4096, n_samples=44100)[source]¶
Rhodes electric piano — tine struck by hammer, electromagnetic pickup.
The Rhodes sound comes from a rubber-tipped hammer hitting a thin steel tine next to a tonebar. The tine vibrates near an electromagnetic pickup (like a guitar pickup), producing a warm, bell-like tone with: 1. Strong fundamental + 2nd harmonic (tine character) 2. Bright metallic attack that mellows quickly (hammer on tine) 3. Bell-like inharmonic partials on soft hits, bark on hard hits 4. Asymmetric waveform from the pickup’s nonlinear response
- pytheory.play.wurlitzer_wave(hz, peak=4096, n_samples=44100)[source]¶
Wurlitzer electric piano — vibrating steel reed over a pickup.
Unlike the Rhodes (tine + tonebar), the Wurlitzer uses a flat steel reed that vibrates near an electrostatic pickup. The result is more nasal, reedy, and biting — especially when driven hard. Think Supertramp, Ray Charles, early Billy Joel. It barks and growls in a way the Rhodes never does.
- pytheory.play.mellotron_wave(hz, peak=4096, n_samples=44100, tape='strings')[source]¶
Mellotron — tape-replay keyboard from the 1960s.
Each key triggers a strip of magnetic tape with a pre-recorded instrument — the original “sampler.” The mechanical transport gives it a lo-fi, haunted quality that no digital emulation fully captures:
Tape flutter: pitch wobbles from uneven capstan speed
Limited bandwidth: 300 Hz–6 kHz, like a worn cassette
Tape saturation: soft compression, rounded transients
8-second limit: tapes physically run out (we model the fadeout)
Head noise: faint hiss baked into the character
The Mellotron defined the sound of Strawberry Fields Forever, Stairway to Heaven, and every prog rock record from 1969–1977.
- Parameters:
tape – Which tape bank to simulate. “strings” — the iconic MkII string section “flute” — breathy, haunting solo flute “choir” — ghostly vocal pad
- pytheory.play.vibraphone_wave(hz, peak=4096, n_samples=44100)[source]¶
Vibraphone — struck aluminum bars with motor-driven tremolo.
Metal bars hit with soft mallets, resonator tubes underneath, and a spinning disc (motor) that modulates the sound creating the signature vibraphone shimmer/tremolo.
- pytheory.play.pipe_organ_wave(hz, peak=4096, n_samples=44100)[source]¶
Pipe organ — air through ranks of pipes, multiple stops.
The pipe organ is additive synthesis incarnate — each stop adds a rank of pipes at a specific harmonic. We model a classic registration: principal 8’, octave 4’, fifteenth 2’, mixture. Constant air pressure means no dynamics — always full and sustained.
- pytheory.play.choir_wave(hz, peak=4096, n_samples=44100, lyric='ah')[source]¶
Choir — voices singing vowels shaped by strong formant filters.
The key to vocal sound is FORMANTS — resonant peaks from the vocal tract shape. We generate a rich glottal source then filter it hard through formant bandpass filters. The formants are what make “ah” sound different from “oo”.
- pytheory.play.bass_guitar_wave(hz, peak=4096, n_samples=44100)[source]¶
Bass guitar — plucked thick string with magnetic pickup.
Heavier Karplus-Strong with: 1. Thicker initial burst (roundwound string character) 2. More fundamental, less high harmonics 3. Pickup emphasizes low-mids
- pytheory.play.flute_wave(hz, peak=4096, n_samples=44100)[source]¶
Flute — breath noise through a resonant tube.
Models an air jet exciting a cylindrical tube: 1. Breath noise — bandpass filtered around the fundamental 2. Tube resonance — mostly fundamental + odd harmonics 3. Vibrato that develops over time 4. Breathy attack
- pytheory.play.trumpet_wave(hz, peak=4096, n_samples=44100)[source]¶
Trumpet — lip buzz through a brass bell.
Models the key trumpet characteristics: 1. Lip buzz — rich in harmonics (like a saw but with specific spectral shape) 2. Bell resonance — boosts 1-2kHz “brightness” range 3. Brass warmth — even harmonics stronger than clarinet 4. Slight vibrato
- pytheory.play.clarinet_wave(hz, peak=4096, n_samples=44100)[source]¶
Clarinet — reed vibration in a cylindrical bore.
A cylindrical bore produces mostly odd harmonics (like a square wave but with a specific spectral envelope). The reed adds a nasal quality.
- pytheory.play.marimba_wave(hz, peak=4096, n_samples=44100)[source]¶
Marimba — struck wooden bar with resonator tube.
The bar produces a fundamental plus inharmonic partials (the bar modes are NOT integer multiples). The tubular resonator under each bar amplifies the fundamental.
- pytheory.play.oboe_wave(hz, peak=4096, n_samples=44100)[source]¶
Oboe — double reed through a conical bore.
The conical bore (unlike clarinet’s cylinder) produces both odd AND even harmonics, but with a nasal, reedy quality from the double reed. Brighter and more piercing than clarinet.
- pytheory.play.harpsichord_wave(hz, peak=4096, n_samples=44100)[source]¶
Harpsichord — quill plucking a metal string.
Distinctive bright, metallic pluck with no sustain control (unlike piano, you can’t play soft). Rich in harmonics with a sharp attack and moderate decay.
- pytheory.play.cello_wave(hz, peak=4096, n_samples=44100)[source]¶
Cello — deep bowed string with large body resonance.
Like strings_wave but with stronger low-frequency body resonance (the cello body is much larger than violin) and a darker, warmer harmonic profile.
- pytheory.play.harp_wave(hz, peak=4096, n_samples=44100)[source]¶
Harp — pure, singing tone with gentle pluck and long sustain.
Nylon/gut strings on a large resonant frame. The tone is warm and clean — mostly fundamental with gentle upper harmonics that decay faster, leaving a pure singing sustain.
- pytheory.play.upright_bass_wave(hz, peak=4096, n_samples=44100)[source]¶
Upright bass — thick gut/steel string pizzicato with wooden body.
Deep, round, woody. The large hollow body gives a warm resonance that electric bass can’t match. Pizzicato (plucked) by default.
- pytheory.play.timpani_wave(hz, peak=4096, n_samples=44100)[source]¶
Timpani — large kettle drum with definite pitch.
The copper kettle creates a tuned resonance with inharmonic overtones. The head modes are at ratios 1.0, 1.5, 1.99, 2.44 (not integer multiples like strings). The felt mallet gives a soft attack with a deep, booming body.
- pytheory.play.saxophone_wave(hz, peak=4096, n_samples=44100)[source]¶
Saxophone — single reed driving a conical brass bore.
Models the key acoustic properties of a saxophone: 1. Reed-bore interaction — nonlinear clipping creates the characteristic
bright, edgy tone (not just additive sines)
Conical bore formants — vocal-like resonances at ~500, ~1400, ~2300, ~3200 Hz that give sax its singing quality
Breath noise — turbulent airflow through the mouthpiece, strongest at attack and blending into sustained tone
Sub-harmonic warmth — the conical bore’s coupling creates warmth below the fundamental
Vibrato — delayed onset, ~5 Hz, characteristic of jazz/classical sax
- pytheory.play.vocal_wave(hz, peak=4096, n_samples=44100, lyric='ah')[source]¶
Vocal/formant synthesis — sings vowel sounds at a given pitch.
Models the human voice with: 1. LF glottal model — asymmetric pulse with sharp closure (not just sines) 2. 5 parallel resonant formant filters (real voice has 5 formant peaks) 3. Jitter + shimmer (natural pitch/amplitude irregularity) 4. Aspiration noise mixed with the glottal source 5. Consonant onsets (plosives, sibilants, nasals, etc.)
- pytheory.play.granular_wave(hz, peak=4096, n_samples=44100, grain_size=0.04, density=50, scatter=0.5, pitch_var=12, source='saw')[source]¶
Granular synthesis — clouds of tiny sound grains.
Chops a source waveform into overlapping micro-grains (10-200ms), each independently windowed and optionally pitch/time scattered. Creates textures impossible with other synthesis: frozen tones, shimmering clouds, evolving pads, glitchy stutters.
- Parameters:
hz – Base frequency.
grain_size – Duration of each grain in seconds (default 0.05 = 50ms).
density – Grains per second (default 20). Higher = denser cloud.
scatter – Random position jitter 0-1 (default 0.3). How much each grain’s read position varies from sequential order.
pitch_var – Random pitch variation per grain in cents (default 5).
source – Base waveform —
"saw","sine","triangle","square","noise"(default"saw").
- pytheory.play.pedal_steel_wave(hz, peak=4096, n_samples=44100)[source]¶
Pedal steel guitar — the Nashville crying sound.
Sustained steel string with natural portamento character, very smooth, lots of harmonics, and a singing quality from the bar sliding on the strings.
- pytheory.play.theremin_wave(hz, peak=4096, n_samples=44100)[source]¶
Theremin — pure sine with natural wobble.
The theremin’s sound is a nearly pure sine wave with slight pitch instability from hand position. The eerie, sci-fi sound comes from this purity combined with continuous pitch.
- pytheory.play.kalimba_wave(hz, peak=4096, n_samples=44100)[source]¶
Kalimba/thumb piano — metal tines on a wooden body.
Bright, bell-like attack with inharmonic overtones from the metal tines. The wooden resonator gives warmth underneath.
- pytheory.play.steel_drum_wave(hz, peak=4096, n_samples=44100)[source]¶
Steel drum/pan — hammered metal with bright, ringing tone.
The steel pan has specific inharmonic partials from the hand-hammered notes. Bright, tropical, bell-like.
- pytheory.play.harmonium_wave(hz, peak=4096, n_samples=44100)[source]¶
Harmonium — Indian pump organ, single free reed per note.
Unlike accordion (doubled musette reeds), the harmonium has one reed per note — no beating, just a pure, nasal, reedy tone. Constant bellows pressure, warm but slightly buzzy. The sound of kirtan, qawwali, and devotional music.
- pytheory.play.accordion_wave(hz, peak=4096, n_samples=44100)[source]¶
Accordion — bellows-driven free reeds.
Two reeds per note slightly detuned (musette tuning) create the characteristic beating/tremolo. Rich in harmonics from the reed vibration.
- pytheory.play.didgeridoo_wave(hz, peak=4096, n_samples=44100)[source]¶
Didgeridoo — circular breathing drone through a wooden tube.
Deep fundamental with strong odd harmonics from the cylindrical bore. The overtone singing technique creates shifting formants. Buzzy, droning, primal.
- pytheory.play.bagpipe_wave(hz, peak=4096, n_samples=44100)[source]¶
Bagpipes — chanter reed with constant drone pressure.
The chanter (melody pipe) uses a double reed like an oboe but with more buzz and brightness. The constant air pressure from the bag means no dynamics — always ff.
- pytheory.play.banjo_wave(hz, peak=4096, n_samples=44100)[source]¶
Banjo — steel strings on a drum-head body.
The banjo’s distinctive twang comes from the membrane head (like a drum skin) instead of a wooden soundboard. This gives a sharp attack, bright tone, and fast decay with a nasal, metallic quality. The 5th string drone adds shimmer.
- pytheory.play.mandolin_wave(hz, peak=4096, n_samples=44100)[source]¶
Mandolin — paired steel strings, bright and ringing.
The mandolin has 4 courses of paired strings, tuned in unison. The doubled strings create natural chorus. Bright attack from the plectrum, small body with high-frequency resonance.
- pytheory.play.ukulele_wave(hz, peak=4096, n_samples=44100)[source]¶
Ukulele — nylon strings on a small resonant body.
Brighter and thinner than guitar, shorter sustain. The small body gives a mid-heavy resonance (no deep bass). Nylon strings have a softer, warmer attack than steel.
- pytheory.play.acoustic_guitar_wave(hz, peak=4096, n_samples=44100)[source]¶
Acoustic guitar — Karplus-Strong with wooden body resonance.
Models a steel string exciting a resonant wooden body: 1. Karplus-Strong plucked string (softer initial noise than pure KS) 2. Body resonance — bandpass filters at the guitar body’s natural
frequencies (~100Hz air cavity, ~250Hz top plate, ~500Hz back)
Warmer, rounder attack than electric (fingers vs pickup)
- pytheory.play.electric_guitar_wave(hz, peak=4096, n_samples=44100)[source]¶
Electric guitar — Karplus-Strong through magnetic pickup simulation.
Models a steel string vibrating over a magnetic pickup: 1. Karplus-Strong plucked string (brighter than acoustic) 2. Pickup comb filter — a magnetic pickup at 1/4 string length
cancels the 4th harmonic and boosts the 2nd, creating the characteristic electric guitar “honk”
Slightly longer sustain than acoustic (no body absorption)
- pytheory.play.sitar_wave(hz, peak=4096, n_samples=44100)[source]¶
Sitar — Karplus-Strong with jawari bridge buzz and sympathetic strings.
The sitar’s distinctive sound comes from three things: 1. The jawari (bridge) — a wide, curved bridge where the string
buzzes against the surface, creating rich harmonics and that characteristic “zzz” tone. Modeled as a soft-clip nonlinearity applied inside the delay loop.
Sympathetic strings (taraf) — strings that resonate in sympathy with the played note, adding a shimmering halo.
Sharp mizrab (plectrum) attack with moderate decay — plucky, not sustained like a bowed instrument.
- pytheory.play.crotales_wave(hz, peak=4096, n_samples=44100)[source]¶
Crotales — small tuned bronze discs struck with brass mallets.
Antique cymbals. Bright, crystalline, bell-like tone that rings for a very long time. The partials are nearly harmonic (closer to a bell than a bar) with strong upper harmonics that give crotales their penetrating brilliance. Played in the octave above written — they cut through any orchestra.
- pytheory.play.tingsha_wave(hz, peak=4096, n_samples=44100)[source]¶
Tingsha — two small Tibetan cymbals clashed together on a cord.
When the pair strikes, both discs ring simultaneously at slightly different frequencies (no two are identical), producing a bright ping with pronounced beating. The sound is thinner and higher than a singing bowl — a clear, cutting tone that fades over a few seconds. The two-disc interference is the whole character.
- pytheory.play.singing_bowl_strike_wave(hz, peak=4096, n_samples=44100)[source]¶
Singing bowl strike — mallet hit that excites all modes at once.
The initial hit produces a bright chirp as the higher partials ring momentarily, then the sound settles into the fundamental with slow beating. Higher modes decay fast, the fundamental rings for seconds.
- pytheory.play.singing_bowl_ring_wave(hz, peak=4096, n_samples=44100)[source]¶
Singing bowl ring — sustained rubbing around the rim with a mallet.
When you rub the rim, the bowl builds up slowly as the mallet continuously feeds energy into the resonance. The fundamental dominates with strong beating. Higher partials come and go as the mallet catches different modes. The sound has a pulsing, breathing quality from the slow amplitude modulation.
- class pytheory.play.Envelope(*values)[source]¶
Bases:
EnumADSR envelope presets for shaping note amplitude over time.
Each preset is a tuple of
(attack, decay, sustain, release)in seconds (sustain is a 0–1 level, not a time).Example:
>>> play(tone, envelope=Envelope.PIANO) >>> play(chord, envelope=Envelope.PAD, t=3_000)
- NONE = (0.0, 0.0, 1.0, 0.0)¶
- PIANO = (0.005, 0.1, 0.4, 0.15)¶
- ORGAN = (0.02, 0.0, 1.0, 0.02)¶
- PLUCK = (0.002, 0.15, 0.0, 0.1)¶
- PAD = (0.4, 0.2, 0.7, 0.5)¶
- STRINGS = (0.15, 0.1, 0.8, 0.3)¶
- BOWED = (0.04, 0.08, 0.75, 0.25)¶
- BELL = (0.001, 0.3, 0.0, 0.5)¶
- MALLET = (0.002, 0.05, 0.6, 0.8)¶
- STACCATO = (0.005, 0.05, 0.0, 0.02)¶
- class pytheory.play.Synth(*values)[source]¶
Bases:
EnumWaveform types for synthesis.
Each waveform has a distinct timbre based on its harmonic content:
SINE — pure tone, no harmonics. Smooth and clean.
SAW — all harmonics at 1/n amplitude. Bright, buzzy, aggressive.
TRIANGLE — odd harmonics at 1/n². Mellow, woody, hollow.
SQUARE — odd harmonics at 1/n. Hollow, chiptune, 8-bit.
PULSE — variable duty cycle square. Nasal, reedy (NES sound).
FM — frequency modulation synthesis. Bell-like, metallic, DX7.
NOISE — white noise, unpitched. Percussion, wind, texture.
SUPERSAW — 7 detuned saws. Fat, shimmery, trance/EDM pads.
- SINE = 'sine'¶
- SAW = 'saw'¶
- TRIANGLE = 'triangle'¶
- SQUARE = 'square'¶
- PULSE = 'pulse'¶
- FM = 'fm'¶
- NOISE = 'noise'¶
- SUPERSAW = 'supersaw'¶
- PWM_SLOW = 'pwm_slow'¶
- PWM_FAST = 'pwm_fast'¶
- PLUCK = 'pluck_synth'¶
- ORGAN = 'organ_synth'¶
- STRINGS = 'strings_synth'¶
- PIANO = 'piano_synth'¶
- BASS_GUITAR = 'bass_guitar_synth'¶
- FLUTE = 'flute_synth'¶
- TRUMPET = 'trumpet_synth'¶
- CLARINET = 'clarinet_synth'¶
- MARIMBA = 'marimba_synth'¶
- OBOE = 'oboe_synth'¶
- HARPSICHORD = 'harpsichord_synth'¶
- CELLO = 'cello_synth'¶
- HARP = 'harp_synth'¶
- UPRIGHT_BASS = 'upright_bass_synth'¶
- TIMPANI = 'timpani_synth'¶
- SAXOPHONE = 'saxophone_synth'¶
- GRANULAR = 'granular_synth'¶
- VOCAL = 'vocal_synth'¶
- PEDAL_STEEL = 'pedal_steel_synth'¶
- THEREMIN = 'theremin_synth'¶
- KALIMBA = 'kalimba_synth'¶
- STEEL_DRUM = 'steel_drum_synth'¶
- HARMONIUM = 'harmonium_synth'¶
- ACCORDION = 'accordion_synth'¶
- DIDGERIDOO = 'didgeridoo_synth'¶
- BAGPIPE = 'bagpipe_synth'¶
- BANJO = 'banjo_synth'¶
- MANDOLIN = 'mandolin_synth'¶
- UKULELE = 'ukulele_synth'¶
- ACOUSTIC_GUITAR = 'acoustic_guitar_synth'¶
- SITAR = 'sitar_synth'¶
- ELECTRIC_GUITAR = 'electric_guitar_synth'¶
- CROTALES = 'crotales_synth'¶
- TINGSHA = 'tingsha_synth'¶
- SINGING_BOWL_STRIKE = 'singing_bowl_strike_synth'¶
- SINGING_BOWL_RING = 'singing_bowl_ring_synth'¶
- RHODES = 'rhodes_synth'¶
- WURLITZER = 'wurlitzer_synth'¶
- VIBRAPHONE = 'vibraphone_synth'¶
- PIPE_ORGAN = 'pipe_organ_synth'¶
- CHOIR = 'choir_synth'¶
- MELLOTRON = 'mellotron_synth'¶
- HARD_SYNC = 'hard_sync'¶
- RING_MOD = 'ring_mod'¶
- WAVEFOLD = 'wavefold'¶
- DRIFT = 'drift'¶
- pytheory.play.play(tone_or_chord, temperament='equal', synth=Synth.SINE, t=1000, envelope=Envelope.PIANO, **synth_kw)[source]¶
Play a tone or chord through the speakers.
- Parameters:
tone_or_chord – A
ToneorChordto play.temperament – Tuning temperament (
"equal","pythagorean", or"meantone").synth – Waveform type —
Synth.SINE,Synth.SAW, orSynth.TRIANGLE.t – Duration in milliseconds (default 1000).
envelope – ADSR envelope preset (default
Envelope.PIANO). UseEnvelope.NONEfor raw waveform.**synth_kw – Extra keyword arguments forwarded to the synth wave function (e.g.
tape="flute"for Mellotron).
Example:
>>> play(Tone.from_string("A4"), t=1_000) >>> play(Chord.from_name("Am7"), synth=Synth.TRIANGLE, t=2_000) >>> play(tone, envelope=Envelope.PAD, t=3_000) >>> play(tone, synth=Synth.MELLOTRON, tape="choir", t=2_000)
- pytheory.play.save(tone_or_chord, path, temperament='equal', synth=Synth.SINE, t=1000, envelope=Envelope.PIANO, **synth_kw)[source]¶
Render a tone or chord and save it as a WAV file.
- Parameters:
tone_or_chord – A
ToneorChordto render.path – Output file path (e.g.
"chord.wav").temperament – Tuning temperament.
synth – Waveform type.
t – Duration in milliseconds (default 1000).
envelope – ADSR envelope preset (default
Envelope.PIANO).**synth_kw – Extra keyword arguments forwarded to the synth wave function.
Example:
>>> save(Chord.from_name("C"), "c_major.wav", t=2_000) >>> save(tone, "bell.wav", envelope=Envelope.BELL, t=3_000)
- pytheory.play.play_progression(chords, *, t=1000, synth=Synth.SINE, gap=100, envelope=Envelope.PIANO)[source]¶
Play a list of chords in sequence.
- Parameters:
chords – List of Chord objects to play in order.
t – Duration of each chord in milliseconds.
synth – Waveform type (Synth.SINE, etc). Defaults to sine.
gap – Silence between chords in milliseconds.
envelope – ADSR envelope preset (default
Envelope.PIANO).
Example:
>>> from pytheory import Key, play_progression >>> chords = Key("C", "major").progression("I", "V", "vi", "IV") >>> play_progression(chords, t=800) >>> play_progression(chords, t=2000, envelope=Envelope.PAD)
- pytheory.play.play_pattern(pattern, repeats=1, bpm=120)[source]¶
Play a drum pattern through the speakers.
Synthesizes each drum sound in real-time and mixes them into a single audio buffer. Every
DrumSoundhas its own synthesized voice — kicks have pitch sweeps, snares have noise bursts, hats are filtered noise, etc.- Parameters:
pattern – A
Patternobject.repeats – Number of times to loop the pattern (default 1).
bpm – Tempo in beats per minute (default 120).
Example:
>>> from pytheory import Pattern >>> play_pattern(Pattern.preset("rock"), repeats=4, bpm=120) >>> play_pattern(Pattern.preset("bossa nova"), repeats=4, bpm=140)
- pytheory.play.render_score(score)[source]¶
Render a Score to a float32 audio buffer.
Mixes all parts (named and default), plus drum hits, into a single normalized buffer.
- Parameters:
score – A
Scoreobject.- Returns:
Float32 stereo numpy array (N, 2).
- pytheory.play.play_score(score)[source]¶
Play an entire Score through the speakers.
Renders drums, default notes, and all named parts — each with its own synth voice and envelope — mixed into one audio buffer.
- Parameters:
score – A
Scoreobject with notes, parts, and/or drum hits.
Example:
>>> from pytheory import Pattern, Key, Duration, Score >>> key = Key("A", "minor") >>> score = Score("4/4", bpm=140) >>> score.add_pattern(Pattern.preset("bossa nova"), repeats=4) >>> chords = score.part("chords", synth="sine", envelope="pad") >>> lead = score.part("lead", synth="saw", envelope="pluck") >>> for chord in key.progression("i", "iv", "V", "i"): ... chords.add(chord, Duration.WHOLE) >>> lead.add("E5", Duration.QUARTER).add("D5", Duration.QUARTER) >>> play_score(score)
- pytheory.play.save_midi(tone_or_chords, path, *, t=500, velocity=100, bpm=120, gap=0)[source]¶
Save a tone, chord, or progression as a Standard MIDI File.
Writes a Type 0 (single-track) MIDI file that any DAW, notation software, or MIDI player can open. Far more useful than WAV for musicians — you can edit the notes, change the tempo, transpose, and assign any instrument.
- Parameters:
tone_or_chords – A Tone, Chord, or list of Tones/Chords. A single Tone or Chord is written as one event. A list is written as a sequence (progression).
path – Output file path (e.g.
"progression.mid").t – Duration of each note/chord in milliseconds (default 500).
velocity – MIDI velocity 1-127 (default 100).
bpm – Tempo in beats per minute (default 120).
gap – Silence between chords in milliseconds (default 0).
Example:
>>> from pytheory import Key, save_midi >>> chords = Key("C", "major").progression("I", "V", "vi", "IV") >>> save_midi(chords, "pop.mid", t=500, bpm=120) >>> save_midi(Tone.from_string("C4"), "middle_c.mid", t=1000)