Sequencing (Score, Part, Pattern)¶
The composition layer: Score holds an arrangement,
Part holds one instrument’s notes, Pattern holds
drum patterns. See the Sequencing and Drums
guides for tutorials.
Rhythm and duration primitives for PyTheory.
- class pytheory.rhythm.Duration(*values)[source]¶
Bases:
EnumNote durations in beats (quarter note = 1 beat).
- WHOLE = 4.0¶
- HALF = 2.0¶
- QUARTER = 1.0¶
- EIGHTH = 0.5¶
- SIXTEENTH = 0.25¶
- DOTTED_HALF = 3.0¶
- DOTTED_QUARTER = 1.5¶
- TRIPLET_QUARTER = 0.6666666666666666¶
- class pytheory.rhythm.TimeSignature(beats: int = 4, unit: int = 4)[source]¶
Bases:
objectA musical time signature like 4/4 or 6/8.
- classmethod from_string(s: str) TimeSignature[source]¶
Parse ‘4/4’, ‘3/4’, ‘6/8’ etc.
- class pytheory.rhythm.Note(tone: object, duration: Duration, velocity: int = 100, bend: float = 0.0, bend_type: str = 'smooth', lyric: str = '', articulation: str = '', _hold: bool = False)[source]¶
Bases:
objectA pairing of a sound (Tone, Chord, or None for rest) with a duration.
The optional
bendfield specifies a pitch bend in semitones applied over the note’s duration. Positive = bend up, negative = down. For example,bend=2bends the note up a whole step by the end.
- pytheory.rhythm.Rest(duration=Duration.QUARTER) Note[source]¶
Create a rest (silent note) with the given duration.
- class pytheory.rhythm.DrumSound(*values)[source]¶
Bases:
EnumGeneral MIDI percussion note numbers (channel 10).
These map to the GM drum map standard supported by virtually all MIDI devices and DAWs.
- KICK = 36¶
- SNARE = 38¶
- RIMSHOT = 37¶
- CLAP = 39¶
- CLOSED_HAT = 42¶
- OPEN_HAT = 46¶
- PEDAL_HAT = 44¶
- LOW_TOM = 45¶
- MID_TOM = 47¶
- HIGH_TOM = 50¶
- CRASH = 49¶
- RIDE = 51¶
- RIDE_BELL = 53¶
- COWBELL = 56¶
- CLAVE = 75¶
- SHAKER = 70¶
- TAMBOURINE = 54¶
- CONGA_HIGH = 63¶
- CONGA_LOW = 64¶
- BONGO_HIGH = 60¶
- BONGO_LOW = 61¶
- TIMBALE_HIGH = 65¶
- TIMBALE_LOW = 66¶
- AGOGO_HIGH = 67¶
- AGOGO_LOW = 68¶
- GUIRO = 73¶
- MARACAS = 70¶
- TABLA_NA = 86¶
- TABLA_TIN = 87¶
- TABLA_GE = 88¶
- TABLA_DHA = 89¶
- TABLA_TIT = 90¶
- TABLA_KE = 91¶
- DHOL_DAGGA = 92¶
- DHOL_TILLI = 93¶
- DHOL_BOTH = 94¶
- DHOLAK_GE = 95¶
- DHOLAK_NA = 96¶
- DHOLAK_TIT = 97¶
- MRIDANGAM_THAM = 98¶
- MRIDANGAM_NAM = 99¶
- MRIDANGAM_DIN = 100¶
- MRIDANGAM_THA = 101¶
- TABLA_GE_BEND = 108¶
- DJEMBE_BASS = 102¶
- DJEMBE_TONE = 103¶
- DJEMBE_SLAP = 104¶
- DOUMBEK_DUM = 112¶
- DOUMBEK_TEK = 113¶
- DOUMBEK_KA = 114¶
- CAJON_BASS = 108¶
- CAJON_SLAP = 109¶
- CAJON_TAP = 110¶
- CAJON_SLAP_SNARE = 111¶
- METAL_KICK = 105¶
- METAL_SNARE = 106¶
- METAL_HAT = 107¶
- MARCH_SNARE = 115¶
- MARCH_RIMSHOT = 116¶
- MARCH_CLICK = 118¶
- QUAD_1 = 119¶
- QUAD_2 = 120¶
- QUAD_3 = 121¶
- QUAD_4 = 122¶
- QUAD_SPOCK = 123¶
- BASS_1 = 124¶
- BASS_2 = 125¶
- BASS_3 = 126¶
- BASS_4 = 127¶
- BASS_5 = 80¶
- RAINSTICK = 81¶
- RAINSTICK_SLOW = 128¶
- OCEAN_DRUM = 82¶
- CABASA = 83¶
- WIND_CHIMES = 84¶
- FINGER_CYMBAL = 85¶
- class pytheory.rhythm.Hit(sound: DrumSound, position: float, velocity: int = 100)[source]¶
Bases:
objectA single drum hit at a specific position in a pattern.
- Parameters:
sound – A
DrumSound(e.g.DrumSound.KICK).position – Position within the pattern, in beats.
0.0is beat 1,0.5the following eighth,0.25a sixteenth, and so on.velocity – MIDI velocity, 1–127. Lower values make ghost notes.
Example:
>>> from pytheory import Hit, DrumSound, Pattern >>> beat = Pattern("my beat", [Hit(DrumSound.KICK, 0.0), ... Hit(DrumSound.SNARE, 2.0)])
- sound¶
- position¶
- velocity¶
- class pytheory.rhythm.Pattern(name: str, hits: list[Hit], beats: float = 4.0, time_signature: str = '4/4')[source]¶
Bases:
objectA drum pattern — a repeating rhythmic figure.
Patterns are defined as a list of hits within a fixed number of beats. They can be rendered to a Score for MIDI export, or combined with chord progressions.
Example:
>>> pattern = Pattern.preset("rock") >>> print(pattern) <Pattern 'rock' 4/4 4.0 beats 12 hits>
- to_score(repeats: int = 4, bpm: int = 120) Score[source]¶
Render this pattern to a Score for MIDI export.
- Parameters:
repeats – Number of times to repeat the pattern.
bpm – Tempo in beats per minute.
- Returns:
A Score containing drum hits as MIDI percussion notes.
- classmethod fill(name: str) Pattern[source]¶
Load a named 1-bar drum fill.
Available fills: rock, rock crash, jazz, jazz brush, salsa, samba, funk, metal, blast, buildup, breakdown.
Example:
>>> Pattern.fill("rock") <Pattern 'rock fill' 4/4 4.0 beats ...>
- classmethod preset(name: str) Pattern[source]¶
Load a named drum pattern preset.
Available presets:
rock — standard 4/4 rock beat (kick-snare-hat)
jazz — swing ride pattern with ghost notes
bebop — fast jazz ride with syncopated kick/snare
bossa nova — Brazilian 2-bar pattern with cross-stick
salsa — clave-driven Afro-Cuban pattern
funk — syncopated 16th-note groove
reggae — one-drop pattern (snare on 3)
waltz — 3/4 pattern (oom-pah-pah)
12/8 blues — slow blues shuffle
samba — Brazilian carnival pattern
son clave 3-2 — the Afro-Cuban rhythmic key
son clave 2-3 — reversed clave
Example:
>>> Pattern.preset("salsa") <Pattern 'salsa' 4/4 8.0 beats ...>
- class pytheory.rhythm.Part(name: str, *, synth: str = 'sine', envelope: str = 'piano', volume: float = 0.5, reverb: float = 0.0, reverb_decay: float = 1.0, reverb_type: str = 'algorithmic', delay: float = 0.0, delay_time: float = 0.375, delay_feedback: float = 0.4, highpass: float = 0.0, highpass_q: float = 0.707, lowpass: float = 0.0, lowpass_q: float = 0.707, distortion: float = 0.0, distortion_drive: float = 3.0, legato: bool = False, glide: float = 0.0, chorus: float = 0.0, chorus_rate: float = 1.5, chorus_depth: float = 0.003, swing: float | None = None, humanize: float = 0.0, sidechain: float = 0.0, sidechain_release: float = 0.1, detune: float = 0.0, pan: float = 0.0, spread: float = 0.0, sub_osc: float = 0.0, noise_mix: float = 0.0, filter_attack: float = 0.01, filter_decay: float = 0.3, filter_sustain: float = 0.0, filter_amount: float = 0.0, vel_to_filter: float = 0.0, saturation: float = 0.0, tremolo_depth: float = 0.0, tremolo_rate: float = 5.0, phaser: float = 0.0, phaser_rate: float = 0.5, cabinet: float = 0.0, cabinet_brightness: float = 0.5, analog: float = 0.0, ensemble: int = 1, fm_ratio: float = 2.0, fm_index: float = 3.0, synth_kw: dict = None)[source]¶
Bases:
objectA named voice within a Score, with its own synth, envelope, and effects.
Parts allow layering multiple instruments — lead, bass, pads, etc. — each with independent synth settings and effects, mixed together on playback.
Don’t instantiate directly — use
Score.part()instead.Example:
score = Score("4/4", bpm=140) lead = score.part("lead", synth="saw", envelope="pluck", reverb=0.3, delay=0.25) lead.add("E5", Duration.QUARTER).add("D5", Duration.EIGHTH) bass = score.part("bass", synth="triangle", envelope="pluck") bass.add("A2", Duration.HALF)
- add(tone_or_string, duration=Duration.QUARTER, *, velocity: int = 100, bend: float = 0.0, bend_type: str = 'smooth', lyric: str = '', articulation: str = '') Part[source]¶
Add a note. Accepts Tone/Chord objects or note strings like
"E5".Duration can be a
Durationenum or a raw float (beats). Velocity controls loudness (1-127, default 100). Bend specifies a pitch bend in semitones over the note duration (e.g.bend=2bends up a whole step,bend=-1bends down a half step). Used for guitar bends, sitar meends, slides. Articulation changes how the note is played:"staccato"(short, ~40% duration),"legato"(overlaps next note),"marcato"(heavy accent),"tenuto"(full duration, soft attack),"accent"(velocity bump),"fermata"(held ~50% longer).Returns self for chaining.
- hold(tone_or_string, duration=Duration.QUARTER, *, velocity: int = 100, bend: float = 0.0, bend_type: str = 'smooth', lyric: str = '', articulation: str = '') Part[source]¶
Add a note without advancing the beat position.
The note plays at the current position but the next note starts at the same time — enabling polyphonic overlap on a single part.
Use this for: piano sustain pedal (bass note rings while melody plays above), guitar strumming with individual string timing, held drone notes under a melody.
Example:
>>> piano = score.part("piano", instrument="piano") >>> piano.hold("C3", Duration.WHOLE) # bass rings for 4 beats >>> piano.add("E4", Duration.HALF) # starts at same time as C3 >>> piano.add("G4", Duration.HALF) # starts at beat 2
- hit(sound, duration=Duration.EIGHTH, *, velocity: int = 100, articulation: str = '') Part[source]¶
Add a drum hit to this part.
Places a drum sound into the note stream so it goes through the normal renderer — meaning articulations, humanize, and effects all work on individual hits.
- Parameters:
sound – A
DrumSoundenum member (e.g.DrumSound.KICK) or its name as a string ("kick","snare","closed_hat"— case-insensitive).duration – How long the hit occupies in the timeline (default 8th note).
velocity – Hit loudness 1-127.
articulation –
"accent","staccato","marcato", etc.
Example:
>>> drums = score.part("kit", synth="sine") >>> drums.hit("kick", Duration.QUARTER, articulation="accent") >>> drums.hit(DrumSound.CLOSED_HAT, Duration.EIGHTH)
- flam(sound, duration=Duration.QUARTER, *, velocity: int = 110, gap: float = 0.015, grace_vel: float = 0.3, articulation: str = '') Part[source]¶
Add a flam — a grace note immediately before the main hit.
The grace note is nearly simultaneous with the main hit, thickening the attack. Tighter gap = more like one fat hit, wider gap = audible double.
- Parameters:
sound – A
DrumSoundenum member.duration – Total duration the flam occupies.
velocity – Main hit velocity.
gap – Beats between grace and main (default 0.008 ≈ 4ms at 120).
grace_vel – Grace note velocity as fraction of main (default 0.3).
articulation – Optional articulation for the main hit.
Example:
>>> p.flam(DrumSound.MARCH_SNARE, Duration.QUARTER, velocity=120)
- diddle(sound, duration=Duration.EIGHTH, *, velocity: int = 70) Part[source]¶
Add a diddle — two equal strokes in the space of one note.
A double-stroke roll building block. Two hits split evenly across the duration.
- Parameters:
sound – A
DrumSoundenum member.duration – Total duration (default 8th note). Each stroke gets half.
velocity – Velocity for both strokes.
Example:
>>> p.diddle(DrumSound.MARCH_SNARE, Duration.EIGHTH, velocity=60)
- cheese(sound, duration=Duration.QUARTER, *, velocity: int = 110, gap: float = 0.008, grace_vel: float = 0.3) Part[source]¶
Add a cheese — a flam followed by a diddle.
Common marching rudiment: grace-MAIN-tap-tap.
- Parameters:
sound – A
DrumSoundenum member.duration – Total duration.
velocity – Main hit velocity.
- crescendo(notes, duration=Duration.QUARTER, *, start_vel: int = 40, end_vel: int = 110, articulation: str = '') Part[source]¶
Add notes with velocity ramping up (getting louder).
- Parameters:
notes – List of note strings (e.g.
["C4", "D4", "E4"]).duration – Duration for each note.
start_vel – Starting velocity (quiet).
end_vel – Ending velocity (loud).
articulation – Optional articulation for all notes.
Example:
>>> piano.crescendo(["C4","D4","E4","F4","G4"], Duration.QUARTER, ... start_vel=40, end_vel=110)
- decrescendo(notes, duration=Duration.QUARTER, *, start_vel: int = 110, end_vel: int = 40, articulation: str = '') Part[source]¶
Add notes with velocity ramping down (getting quieter).
- Parameters:
notes – List of note strings.
duration – Duration for each note.
start_vel – Starting velocity (loud).
end_vel – Ending velocity (quiet).
articulation – Optional articulation for all notes.
Example:
>>> piano.decrescendo(["G4","F4","E4","D4","C4"], Duration.QUARTER, ... start_vel=110, end_vel=40)
- dynamics(notes, duration=Duration.QUARTER, *, velocities=None, articulation: str = '') Part[source]¶
Add notes with a velocity curve.
- Parameters:
notes – List of note strings or Tone/Chord objects.
duration – Duration for each note (or list of durations).
velocities – Velocity curve — either a
(start, end)tuple for a linear ramp, or a list of ints (one per note).articulation – Optional articulation for all notes (or list).
Example:
>>> # Linear ramp >>> piano.dynamics(["C4","E4","G4","C5"], Duration.QUARTER, ... velocities=(50, 120)) >>> # Custom curve (swell and fade) >>> piano.dynamics(["C4","D4","E4","F4","G4","F4","E4","D4"], ... Duration.EIGHTH, ... velocities=[50, 70, 90, 110, 110, 90, 70, 50])
- swell(notes, duration=Duration.QUARTER, *, low_vel: int = 40, peak_vel: int = 110, articulation: str = '') Part[source]¶
Add notes that swell up then fade back down (< > shape).
The velocity ramps up to the midpoint then back down, creating the classic orchestral swell.
- Parameters:
notes – List of note strings.
duration – Duration for each note.
low_vel – Velocity at start and end.
peak_vel – Velocity at the peak (midpoint).
articulation – Optional articulation.
Example:
>>> strings.swell(["C4","D4","E4","F4","G4","F4","E4","D4"], ... Duration.QUARTER, low_vel=40, peak_vel=110)
- set(**params) Part[source]¶
Change effect parameters at the current beat position.
Inserts an automation marker — from this point forward, the specified parameters take new values. Use this to open filters, add reverb, kick in distortion, or change volume mid-song.
- Parameters:
**params – Any Part parameter —
lowpass,lowpass_q,reverb,reverb_decay,delay,delay_time,delay_feedback,distortion,distortion_drive,volume,chorus,chorus_rate,chorus_depth.- Returns:
Self for chaining.
Example:
>>> lead = score.part("lead", synth="saw", lowpass=800) >>> lead.add("C5", Duration.WHOLE) # filtered >>> lead.set(lowpass=3000, reverb=0.4) # filter opens >>> lead.add("E5", Duration.WHOLE) # bright + reverb >>> lead.set(distortion=0.6, lowpass=1500) # grit >>> lead.add("G5", Duration.WHOLE)
- ramp(over: float = 4.0, resolution: float = 0.25, curve: str = 'linear', **params) Part[source]¶
Smoothly ramp parameters from their current values to new targets.
Generates interpolated automation points — like turning a knob gradually instead of jumping to a new position. Works for any parameter that
.set()accepts.- Parameters:
over – Duration of the ramp in beats (default 4.0 = 1 bar). Use
Duration.WHOLE * 4for a 4-bar ramp, etc.resolution – How often to insert points, in beats (default 0.25). Lower = smoother but more points.
curve – Interpolation shape —
"linear"(default),"ease_in"(slow start, fast end),"ease_out"(fast start, slow end),"ease_in_out"(slow start and end).**params – Target values for any parameter. The ramp starts from the parameter’s current value at this beat position.
- Returns:
Self for chaining.
Example:
>>> lead = score.part("lead", synth="saw", lowpass=200) >>> # Open the filter over 4 bars >>> lead.ramp(over=Duration.WHOLE * 4, lowpass=8000) >>> # Fade reverb in over 2 bars >>> pad.ramp(over=Duration.WHOLE * 2, reverb=0.5) >>> # Multiple params at once with easing >>> lead.ramp(over=8.0, curve="ease_in", lowpass=6000, distortion=0.4)
- lfo(param: str, *, rate: float = 0.5, min: float = 0.0, max: float = 1.0, bars: float = 4, shape: str = 'sine', resolution: float = 0.25) Part[source]¶
Automate a parameter with an LFO (low-frequency oscillator).
Generates automation points at regular intervals, sweeping a parameter smoothly between min and max values. This is how filter sweeps, tremolo, and auto-wah effects work.
- Parameters:
param – Parameter name to modulate (e.g.
"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 – Minimum parameter value.
max – Maximum parameter value.
bars – Number of bars to run the LFO over (default 4).
shape – Waveform shape —
"sine"(smooth),"triangle"(linear),"saw"(ramp up),"square"(on/off).resolution – How often to insert automation points, in beats (default 0.25 = every 16th note). Lower = smoother.
- Returns:
Self for chaining.
Example:
>>> 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)
- arpeggio(chord, *, bars: float = 1, pattern: str = 'up', division=Duration.SIXTEENTH, octaves: int = 1, velocity: int = 100) Part[source]¶
Arpeggiate a chord into a rhythmic pattern.
Takes a chord and sequences through its notes automatically, like a hardware arpeggiator on a synth. Combined with
legato=Trueandglide, this produces classic acid and trance arpeggiated lines.- Parameters:
chord – A Chord object (or string like
"Am").bars – Number of bars to fill (default 1).
pattern – Arpeggio pattern: -
"up"— low to high, repeat -"down"— high to low, repeat -"updown"— up then down (bounce) -"downup"— down then up -"random"— random note orderdivision – Note length for each step (default
Duration.SIXTEENTH).octaves – Number of octaves to span (default 1). With 2, the pattern repeats one octave higher before cycling.
- Returns:
Self for chaining.
Example:
>>> lead = score.part("lead", synth="saw", legato=True, glide=0.03) >>> lead.arpeggio(Chord.from_symbol("Am"), bars=2, pattern="updown")
- strum(chord_name: str, duration=Duration.QUARTER, *, direction: str = 'down', velocity: int = 100, strum_time: float = 0.05) Part[source]¶
Strum a chord using the part’s fretboard fingering.
Looks up the chord on the fretboard, gets the fingering, and adds each string as a rapid sequence with tiny time offsets — like a real guitar strum. Muted strings are skipped.
- Parameters:
chord_name – Chord name (e.g.
"Am","G","D").duration – Total duration of the strum (default QUARTER).
direction –
"down"(low→high, default) or"up"(high→low).velocity – Base velocity (each string gets slight variation).
strum_time – Time in beats for the full strum sweep (default 0.03 = very fast). Larger values = slower, more audible strum. Try 0.1 for a lazy strum.
- Returns:
Self for chaining.
Example:
>>> guitar = score.part("guitar", instrument="acoustic_guitar", ... fretboard=Fretboard.guitar()) >>> guitar.strum("Am", Duration.HALF) >>> guitar.strum("G", Duration.HALF, direction="up")
- roll(tone_or_string, duration=Duration.WHOLE, *, velocity_start: int = 40, velocity_end: int = 100, speed=Duration.SIXTEENTH) Part[source]¶
Play a roll — rapid repeated notes with velocity ramp.
Perfect for timpani rolls, snare rolls, tremolo on any instrument. The velocity ramps from
velocity_starttovelocity_endover the duration for crescendo/decrescendo.- Parameters:
tone_or_string – The note to repeat.
duration – Total duration of the roll.
velocity_start – Velocity of the first hit (default 40).
velocity_end – Velocity of the last hit (default 100).
speed – How fast to repeat (default SIXTEENTH notes).
- Returns:
Self for chaining.
Example:
>>> timp = score.part("timp", instrument="timpani") >>> timp.roll("C3", Duration.WHOLE, velocity_start=30, velocity_end=110)
- to_tab(*, tuning='guitar', frets=24, time_signature=None)[source]¶
Generate ASCII guitar/bass tablature from this part’s notes.
- Parameters:
tuning –
"guitar"(6-string standard),"bass"(4-string),"drop_d"(guitar drop D), aFretboardobject, or a list of MIDI note numbers for custom tuning (low string first).frets – Maximum fret number (default 24).
time_signature – A
TimeSignatureorNonefor 4/4.
- Returns:
A multi-line ASCII tablature string.
- class pytheory.rhythm.Section(name: str, score: Score)[source]¶
Bases:
objectA named section of a Score (verse, chorus, bridge, etc.).
- class pytheory.rhythm.Score(time_signature='4/4', bpm=120, swing: float = 0.0, drum_humanize: float = 0.15, system: str = 'western', temperament: str = 'equal', reference_pitch: float = 440.0)[source]¶
Bases:
objectA multi-part arrangement with drums, chords, and instrument voices.
A Score combines:
Drum patterns via
add_pattern()Chord/tone notes via
add()(backwards-compatible default part)Named parts via
part()— each with its own synth and envelope
Example:
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") bass = score.part("bass", synth="triangle", envelope="pluck") for chord in key.progression("i", "iv", "V", "i"): chords.add(chord, Duration.WHOLE) lead.add("E5", Duration.QUARTER).add("D5", Duration.EIGHTH) bass.add("A2", Duration.HALF).add("D2", Duration.HALF) play_score(score)
- property bpm¶
- set_drum_effects(**kwargs) Score[source]¶
Set effects on all drum parts.
When drums are split, applies to every drum Part (kick, snare, hats, etc.). When not split, applies to the single drums Part.
Example:
score.set_drum_effects(reverb=0.2, reverb_type="plate")
- ring_out(seconds: float = None) Score[source]¶
Append trailing silence so reverb/delay tails ring out fully.
Without this, the rendered audio stops exactly at the last beat, which clips the tail of any reverb or delay on the final hit. Call
ring_out()once (typically just before playing or exporting) to pad the end with enough silence for the effects to decay naturally.- Parameters:
seconds – Length of trailing silence in seconds. When omitted, it’s computed automatically from the longest reverb/delay tail across all parts (including drums). Pass an explicit value to override.
- Returns:
Self for chaining.
Example:
score.set_drum_effects(reverb=0.4, reverb_type="cave", delay=0.3, delay_time=0.25) score.drums("funk", repeats=8) score.ring_out() # auto-sized tail play_score(score)
Note
The added silence makes the score longer, so avoid calling this on a pattern you intend to loop seamlessly.
- part(name: str, *, instrument: str = None, synth: str = None, envelope: str = None, volume: float = None, reverb: float = None, reverb_decay: float = None, reverb_type: str = None, delay: float = None, delay_time: float = None, delay_feedback: float = None, highpass: float = None, highpass_q: float = None, lowpass: float = None, lowpass_q: float = None, distortion: float = None, distortion_drive: float = None, legato: bool = None, glide: float = None, chorus: float = None, chorus_rate: float = None, chorus_depth: float = None, swing: float | None = None, humanize: float = None, sidechain: float = None, sidechain_release: float = None, detune: float = None, pan: float = None, spread: float = None, sub_osc: float = None, noise_mix: float = None, filter_attack: float = None, filter_decay: float = None, filter_sustain: float = None, filter_amount: float = None, vel_to_filter: float = None, saturation: float = None, tremolo_depth: float = None, tremolo_rate: float = None, phaser: float = None, phaser_rate: float = None, cabinet: float = None, cabinet_brightness: float = None, analog: float = None, ensemble: int = None, fm_ratio: float = None, fm_index: float = None, fretboard=None) Part[source]¶
Create a named part with its own synth voice and effects.
- Parameters:
name – Part name (e.g.
"lead","bass","pads").instrument – Instrument preset name (e.g.
"piano","violin","808_bass"). SeeINSTRUMENTSfor the full list. When set, the preset’s synth, envelope, and effects are used as defaults; any explicit keyword argument still overrides the preset value.synth – Waveform —
"sine","saw","triangle","square","pulse","fm","noise","supersaw","pwm_slow","pwm_fast".envelope – ADSR preset name —
"piano","pluck","pad","organ","bell","strings","staccato", or"none".volume – Mix level from 0.0 to 1.0 (default 0.5).
reverb – Reverb wet/dry mix, 0.0–1.0 (default 0, off).
reverb_decay – Reverb tail length in seconds (default 1.0).
reverb_type – Reverb algorithm —
"algorithmic"(Schroeder, default) or a convolution IR preset:"taj_mahal","cathedral","hall","plate","spring","cave","parking_garage","canyon".delay – Delay wet/dry mix, 0.0–1.0 (default 0, off).
delay_time – Delay time in seconds (default 0.375, dotted 8th).
delay_feedback – Delay feedback 0.0–1.0 (default 0.4).
lowpass – Lowpass filter cutoff in Hz (default 0, off). Try 800 for muffled bass, 2000 for warm lead, 5000 for subtle brightness rolloff.
lowpass_q – Filter resonance/Q factor (default 0.707, flat). Higher values add a resonant peak at the cutoff — 1.0 = slight peak, 2.0 = pronounced, 5.0+ = aggressive.
distortion – Distortion wet/dry mix, 0.0–1.0 (default 0, off).
distortion_drive – Gain before soft clipping (default 3.0). 0.5–2 = subtle warmth, 3–8 = overdrive, 10+ = fuzz.
legato – If True, notes share a continuous waveform instead of retriggering the envelope on each note (default False).
glide – Portamento time in seconds between consecutive pitches (default 0, instant). 0.03–0.05 = quick 303 slide, 0.1–0.2 = slow glide.
humanize – Random timing and velocity variation, 0.0–1.0 (default 0, off). Adds micro-imperfections that make programmed parts feel like a real player. 0.1 = subtle, 0.3 = natural, 0.5+ = loose/drunk.
sidechain – Sidechain compression amount, 0.0–1.0 (default 0, off). How much the drum hits duck this part’s volume. 0.8 = typical EDM pumping effect.
sidechain_release – How fast the volume comes back after ducking, in seconds (default 0.1).
- Returns:
A
Partobject. Add notes with.add()and.rest().
Example:
lead = score.part("lead", synth="saw", envelope="pluck", reverb=0.3, delay=0.25, lowpass=3000) # Or use an instrument preset: piano = score.part("keys", instrument="piano")
- classmethod list_instruments() list[source]¶
Return a sorted list of available instrument preset names.
Example:
Score.list_instruments() # ['808_bass', 'acid_bass', 'acoustic_guitar', ...]
- add_pattern(pattern, repeats: int = 1, layer: bool = False) Score[source]¶
Add a drum pattern to this score.
By default each call is placed after the previously added drums, so successive calls play in sequence. Pass
layer=Trueto overlay this pattern on top of what’s already there (starting from the beginning), which is how you stack grooves for polyrhythms.- Parameters:
pattern – A
Patternobject.repeats – Number of times to repeat.
layer – If True, overlay starting at beat 0 instead of appending, and leave the running position unchanged.
- Returns:
Self for chaining.
- fill(name: str = 'rock') Score[source]¶
Insert a 1-bar drum fill at the current position.
Replaces what would be the next bar of drums with a genre-appropriate fill.
- drums(preset, repeats: int = 4, fill: str = None, fill_every: int = None, split: bool = False, layer: bool = False) Score[source]¶
Add a drum pattern, with optional auto-fills.
- Parameters:
preset – A pattern preset name (e.g.
"bossa nova","rock") or aPatternobject you built yourself.repeats – Number of times to repeat (default 4).
fill – Optional fill name (preset names only).
fill_every – Replace every Nth bar with a fill.
split – If True, create separate Parts for kick, snare, hats, toms, cymbals, and percussion — each with independent effects. Access via
score.parts["kick"], etc.layer – If True, overlay these drums on top of what’s already there (starting at the beginning) instead of appending them in sequence — use this to stack grooves for polyrhythms.
- Returns:
Self for chaining.
Example:
>>> score.drums("rock", repeats=4, split=True) >>> score.parts["snare"].reverb_mix = 0.3 >>> score.parts["hats"].lowpass = 6000 >>> # Layer a clave on top of the rock beat >>> score.drums("son clave 3-2", repeats=4, layer=True)
- add(tone_or_chord, duration=Duration.QUARTER) Score[source]¶
Add a note to the default (unnamed) part.
For simple scores without named parts. Returns self for chaining.
- rest(duration=Duration.QUARTER) Score[source]¶
Add a rest to the default part. Returns self for chaining.
- set_tempo(bpm: int) Score[source]¶
Insert a tempo change at the current beat position.
The new tempo takes effect from the current total_beats position and remains until the next tempo change.
- Parameters:
bpm – New tempo in beats per minute.
- Returns:
Self for chaining.
- section(name: str) Section[source]¶
Begin a named section. Everything added after this call until the next section() or end_section() belongs to this section.
Example:
score.section("verse") chords.add(chord, Duration.WHOLE) lead.add("C5", Duration.QUARTER) score.section("chorus") chords.add(chord, Duration.WHOLE) score.repeat("verse") score.repeat("chorus", times=2)
- repeat(name: str, times: int = 1) Score[source]¶
Repeat a previously defined section.
Copies all notes, drum hits, and automation from the named section and appends them at the current position.
- Parameters:
name – Name of a section defined with
section().times – Number of times to repeat (default 1).
- Returns:
Self for chaining.
- to_abc(*, title='Untitled', key='C', html=False)[source]¶
Export the score as ABC notation.
- Parameters:
title – Tune title for the
T:field.key – Key signature (e.g.
"C","Gm","D") for theK:field.html – If True, wrap the ABC string in a self-contained HTML page that renders sheet music via abcjs.
- Returns:
An ABC notation string, or a full HTML document string when html is True.
- to_lilypond(*, title='Untitled', key='C', mode='major', chord_names=False, fretboards=False, tab=False, chord_part=None, fretboard=None)[source]¶
Export the score as a LilyPond source string.
With no chord flags this emits one notation staff per pitched voice (the default, unchanged). The chord flags turn the harmony part into guitar-oriented contexts on top of the staves:
- Parameters:
title – Title for the
\headerblock.key – Key signature root (e.g.
"C","D","Bb").mode – LilyPond mode string (
"major","minor", etc.).chord_names – Add a
ChordNamesrow (chord symbols C / G / Am / F) driven by the harmony part.fretboards – Add a
FretBoardsrow of fret-diagram grids. PyTheory’s own voicings are emitted via\storePredefinedDiagram(fromfretboard, default a standard guitar) so the diagrams match what it would actually play; non-standard tunings fall back to LilyPond’s computed diagrams.tab – Add a
TabStaffof the progression.chord_part – Name of the part supplying the harmony. If None, the first part whose notes are chords (e.g. one built from
Key().progression()) is used.fretboard – A
Fretboardsupplying the explicit diagrams forfretboards(defaults to a standard guitar).
- Returns:
A complete LilyPond source string.
Example:
>>> score = Score("4/4", bpm=120) >>> chords = score.part("chords") >>> for c in Key("C", "major").progression("I", "V", "vi", "IV"): ... _ = chords.add(c, Duration.WHOLE) >>> ly = score.to_lilypond(chord_names=True, fretboards=True) >>> "c1 g1 a1:m f1" in ly and "\\new ChordNames" in ly True
- to_musicxml(*, title='Untitled')[source]¶
Export the score as a MusicXML string.
- Parameters:
title – Work title embedded in the
<work-title>element.- Returns:
A MusicXML 4.0 partwise document as a pretty-printed XML string.
- to_tab(part_name=None, **kwargs)[source]¶
Generate ASCII tablature for a part in this score.
- Parameters:
part_name – Name of the part to tab. If None, tabs the first non-drum part that has notes.
**kwargs – Passed through to
Part.to_tab()(e.g.tuning,frets,time_signature).
- Returns:
An ASCII tablature string.
- Raises:
ValueError – If no suitable part is found.
- render()[source]¶
Render this score to audio.
Mixes every part and drum track, runs the master bus, and returns the finished stereo audio. This is the headless equivalent of
play_score— handy for tests, export, or further processing.- Returns:
A float32 NumPy array of shape
(n_samples, 2)at 44.1 kHz, with samples in roughly[-1, 1].
Example:
buf = score.render() # (N, 2) float32 score.to_wav("song.wav") # or save straight to disk
- to_wav(path)[source]¶
Render this score and save it as a 16-bit stereo WAV file.
- Parameters:
path – Output path, e.g.
"song.wav".- Returns:
The path written (so calls can be chained or logged).
Example:
score.to_wav("demo.wav")
- classmethod from_wav(path, *, bpm=None, quantize=None, split=False, part_name='melody', synth='piano_synth', fmin=50.0, fmax=1500.0) Score[source]¶
Transcribe an audio recording into a Score.
Hum a melody, whistle a hook, record a bass line — load the recording and get editable notes back. Pitch tracking is the YIN algorithm; transcription is monophonic per pass (voice, whistle, a single instrument line), not chords.
Reads WAV directly; .m4a voice memos, .mp3, and anything else convert automatically through afconvert (macOS) or ffmpeg.
- Parameters:
path – Path to an audio file.
bpm – Tempo to interpret the timing against. Default
Noneestimates it from the recording’s onset pattern (falling back to 120 for pulse-free rubato).quantize – Optional grid in beats —
0.25snaps starts and durations to sixteenths. Default keeps the timing as performed.split – If True, separate harmonics from drums first and transcribe a
"bass"part and a"melody"part. For full mixes — the bassline comes out well; the melody only as well as it dominates the mix.part_name – Name of the created part (default “melody”).
synth – Playback synth for the transcription.
fmin/fmax – Pitch search range in Hz. Tighten for better results (e.g.
fmin=60, fmax=350for bass).
- Returns:
A Score of detected notes, rests, and velocities, with
score.bpmset to the estimated (or given) tempo.
Example:
>>> score = Score.from_wav("hum.m4a", quantize=0.25) >>> print(score.to_abc(title="My Hum")) >>> score.save_midi("hum.mid")
- classmethod from_midi(path, synth='sine', envelope='pluck') Score[source]¶
Import a Standard MIDI File into a Score.
Reads notes, tempo, and time signature from any Type 0 or Type 1 MIDI file. Each MIDI channel becomes a named Part. Channel 10 (drums) becomes drum hits.
- Parameters:
path – Path to a .mid file.
synth – Default synth for all parts (default “sine”).
envelope – Default envelope for all parts (default “pluck”).
- Returns:
A Score with Parts populated from the MIDI data.
Example:
>>> score = Score.from_midi("song.mid") >>> score.parts["ch1"].synth = "saw" >>> score.parts["ch1"].reverb_mix = 0.3