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: Enum

Note 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: object

A musical time signature like 4/4 or 6/8.

classmethod from_string(s: str) TimeSignature[source]

Parse ‘4/4’, ‘3/4’, ‘6/8’ etc.

property beats_per_measure: float

Total beats in one measure (in quarter-note units).

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: object

A pairing of a sound (Tone, Chord, or None for rest) with a duration.

The optional bend field specifies a pitch bend in semitones applied over the note’s duration. Positive = bend up, negative = down. For example, bend=2 bends the note up a whole step by the end.

tone: object
duration: Duration
velocity: int = 100
bend: float = 0.0
bend_type: str = 'smooth'
lyric: str = ''
articulation: str = ''
property beats: float
pytheory.rhythm.Rest(duration=Duration.QUARTER) Note[source]

Create a rest (silent note) with the given duration.

class pytheory.rhythm.DrumSound(*values)[source]

Bases: Enum

General 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: object

A 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.0 is beat 1, 0.5 the following eighth, 0.25 a 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: object

A 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 list_fills() list[str][source]

Return a list of all available fill names.

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 ...>
classmethod list_presets() list[str][source]

Return a list of all available preset names.

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: object

A 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)
notes: list[Note]
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 Duration enum 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=2 bends up a whole step, bend=-1 bends 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 DrumSound enum 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 DrumSound enum 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 DrumSound enum 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 DrumSound enum 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 * 4 for 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)
rest(duration=Duration.QUARTER) Part[source]

Add a rest. Returns self for chaining.

fade_in(bars: float = 4) Part[source]

Fade volume from 0 to current level over N bars.

fade_out(bars: float = 4) Part[source]

Fade volume from current level to 0 over N bars.

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=True and glide, 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 order

  • division – 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_start to velocity_end over 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)
property is_drums: bool

True if this part contains drum hits.

property total_beats: float
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), a Fretboard object, or a list of MIDI note numbers for custom tuning (low string first).

  • frets – Maximum fret number (default 24).

  • time_signature – A TimeSignature or None for 4/4.

Returns:

A multi-line ASCII tablature string.

class pytheory.rhythm.Section(name: str, score: Score)[source]

Bases: object

A named section of a Score (verse, chorus, bridge, etc.).

property beats: float
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: object

A 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)
notes: list[Note]
parts: dict[str, Part]
property bpm
property drum_effects: dict

drum effects are just the drums Part’s effect settings.

Type:

Proxy

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"). See INSTRUMENTS for 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 Part object. 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=True to 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 Pattern object.

  • 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 a Pattern object 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)
end_section() Score[source]

Close the current section explicitly.

Returns:

Self for chaining.

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.

property total_beats: float
property measures: float

Number of measures (may be fractional if incomplete).

property duration_ms: float

Total duration in milliseconds.

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 the K: 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 \header block.

  • key – Key signature root (e.g. "C", "D", "Bb").

  • mode – LilyPond mode string ("major", "minor", etc.).

  • chord_names – Add a ChordNames row (chord symbols C / G / Am / F) driven by the harmony part.

  • fretboards – Add a FretBoards row of fret-diagram grids. PyTheory’s own voicings are emitted via \storePredefinedDiagram (from fretboard, 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 TabStaff of 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 Fretboard supplying the explicit diagrams for fretboards (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")
save_midi(path, velocity=100)[source]

Export to Standard MIDI File, measure-aware.

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 None estimates it from the recording’s onset pattern (falling back to 120 for pulse-free rubato).

  • quantize – Optional grid in beats — 0.25 snaps 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=350 for bass).

Returns:

A Score of detected notes, rests, and velocities, with score.bpm set 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