Appendix G · Learn One Verb, Predict the Rest¶
The contract the whole API keeps — so you can guess an unfamiliar method instead of looking it up.
The guide hands you dozens of p. verbs, and §4.5 made
a promise about them: learn the shape of one and you can predict the rest. This
appendix is that promise written out in full — the small set of conventions every
verb obeys, gathered in one place. None of it is new; it’s the pattern under
everything you’ve already used. Read it once and an unfamiliar method stops being a
lookup and becomes a good guess.
G.2 (low, high) is one random draw per note¶
Anywhere a velocity= accepts a tuple, the two numbers are a range and each note
rolls its own value inside it — velocity=(60, 90) gives sixteen hi-hats sixteen
different dynamics (§1.5). A plain integer is fixed; the tuple is
the humanising draw. The reading never changes from verb to verb, so you never have
to wonder whether a given (low, high) means “ramp across the bar”, “pick one value
for the line”, or “draw per note” — it is always one independent draw per note.
G.3 One knob for repeatability: seed=¶
Every generator makes its random choices through a stream you can pin with a single
friendly knob: seed= (an integer) makes a call reproducible
(§4.6, §11.2). For the rarer
case where you want to share one stream across calls or steer it yourself, the
advanced rng= takes a random.Random you built. When more than one is in play the
precedence is fixed and worth holding onto:
rng=beatsseed=beats the pattern’s ownp.rng.
Pass both rng= and seed= on one call and Subsequence warns you that seed= was
ignored — express one intention, not two. Most of the time you want none of them and
let the whole-piece seed= on the Composition carry everything.
G.4 probability is the chance a note plays¶
Wherever a placement is gated by chance, probability is the likelihood a note
sounds — 1.0 is certain, 0.5 keeps about half (§4.6).
The one exception is the dropout() transform (§12.6): it
removes notes, so its probability is the chance a note is dropped. The verb
name carries the direction — dropout(0.1) loses about a tenth — so the inversion
never surprises you once you’ve read the name.
Three near-neighbours sit close to probability and each means something distinct;
keeping them straight is most of fluency with the generators:
Dial |
What it sets |
|---|---|
|
The chance a note plays ( |
|
The fill fraction of a texture — how much of the grid a |
|
Thinning depth — how aggressively |
|
Swing angle — |
G.5 Beats at the surface, steps on the grid¶
Everything you write speaks one of two time units, and which is which is
predictable. The API verbs work in beats — beat=, spacing=, and duration=
are all in quarter-note beats, fractions welcome (§3.1).
The step methods work in grid steps — hit_steps, sequence, and the
decorator’s steps= count 0-indexed slots on a fixed grid
(§1.4, §3.2). Underneath both the clock
runs at a fixed 24 pulses per beat, which you almost never touch — but it is why
a beat divides cleanly into halves, thirds, and quarters. To work on a non-quarter
grid, pair steps= with step_duration= on the pattern decorator.
G.6 The velocity buckets¶
When a verb places notes and you don’t name a velocity, it reaches for a sensible
default keyed to the kind of material — louder for a foreground hit, quieter for a
texture sitting under it. The five defaults live in subsequence.constants.velocity,
so you can read or reuse them by name instead of scattering magic numbers:
Material |
Constant |
Value |
|---|---|---|
Single notes and hits |
|
|
Chords |
|
|
Generative lines |
|
|
Cellular automata |
|
|
Ghost layers |
|
|
>>> from subsequence.constants import velocity
>>> (velocity.DEFAULT_VELOCITY, velocity.DEFAULT_CHORD_VELOCITY,
... velocity.DEFAULT_GENERATIVE_VELOCITY, velocity.DEFAULT_CA_VELOCITY,
... velocity.GHOST_FILL_VELOCITY)
(100, 90, 80, 60, 35)
These are starting points, not rules — every verb still takes an explicit
velocity= (a number, or a (low, high) tuple, §G.2) when you
want something else. But the defaults mean a generated line sits politely under a
hit, and a ghost layer under both, before you touch a single number.
G.7 Lenient with names, strict with numbers¶
Subsequence is forgiving about a name it doesn’t recognise and strict about a
number it can’t place — because the two failures mean different things. A drum or
voice name the current device’s map lacks is dropped with a one-time warning,
and the rest of the pattern plays on (§1.2): a mistyped "kick_99"
shouldn’t silence your whole kit. But an unmapped CC, NRPN, or RPN name raises
(§13.1, §13.3) — a wrong control
number is a genuine mistake, one that would write the wrong synth parameter, so it
fails loudly rather than quietly doing the wrong thing.
G.8 Builders chain; accessors return data¶
The last rule tells you, for any method, whether you can keep going. A verb that
places or transforms notes returns the builder, so it chains left to right —
p.euclidean(...).swing(...).legato() reads as a recipe
(§4.5). A method that reads something — p.capture(...),
p.held_notes(), p.section_motif(...) — returns plain data (a motif, a list, a
value), because there is nothing left to build onto. When you’re unsure whether a
call chains, ask what it does: place or shape, and it hands you the builder back;
read, and it hands you the answer.
That is the whole contract — one shared front for the chord verbs, one meaning for
(low, high), one knob for repeatability, a consistent reading of probability,
two clear time units, sensible velocity defaults, lenient names but strict numbers,
and a chain-or-read rule you can apply to any method. Hold these and the
API Reference reads less like a list to memorise and
more like a language you already speak. For the exhaustive signatures see
Appendix D; for this idea in its first, smallest form,
§4.5.