# 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](04-generators-euclidean) 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. (sec-appG-front)= ## G.1 The chord verbs share one front The four ways to voice a chord — block it, strum it, roll it, or spell it out — are deliberately the *same* call with one word changed. `p.chord`, `p.strum`, `p.arpeggio`, and `p.broken_chord` ([§6.4](06-harmony-context), [§7.7](07-progressions)) all lead with the same front: a chord (or a list of pitches), a `root` register, and the familiar `velocity`, `count`, `inversion`, and `beat`. So "play this chord as a block, a roll, or a strum" is a one-word swap: ```{testcode} appG import subsequence import subsequence.constants.midi_notes as notes composition = subsequence.Composition(bpm=120, key="A", scale="minor") composition.harmony(style="aeolian_minor", cycle_beats=4) # Same front — chord, root, count, velocity — two gestures, one verb apart. @composition.pattern(channel=3, beats=4) def block(p, chord): p.chord(chord, root=notes.A3, count=4, velocity=80) @composition.pattern(channel=4, beats=4) def rolled(p, chord): p.arpeggio(chord, root=notes.A3, count=4, velocity=80, spacing=0.25) composition.render(bars=2, filename="verb-swap.mid") ``` The tail diverges only where the *gesture* genuinely differs: `strum` adds a `spacing` and a `direction`, `arpeggio` a `span`/`spacing`/`direction`, and `broken_chord` an explicit `order` list in place of a `count`. Learn the front once and every chord verb is already half-familiar. One naming note: `p.arpeggio`'s first argument is `notes`, not `chord` — because an arpeggio is happy with *either* a chord or a plain list of pitches. The chord form voices the live chord ([§6.4](06-harmony-context)); handed a bare list — say the keys a player is holding ([§14.3](14-live-and-data)) — it rolls exactly those. The argument is named for the general case. ```{admonition} Reference :class: seealso {py:meth}`~subsequence.pattern_builder.PatternBuilder.chord`, {py:meth}`~subsequence.pattern_builder.PatternBuilder.strum`, {py:meth}`~subsequence.pattern_builder.PatternBuilder.arpeggio`, {py:meth}`~subsequence.pattern_builder.PatternBuilder.broken_chord` ``` (sec-appG-tuple)= ## 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](01-step-grid)). 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**. (sec-appG-seed)= ## 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](04-generators-euclidean), [§11.2](11-seeds-locks-freeze)). 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=` beats `seed=` beats the pattern's own `p.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. (sec-appG-probability)= ## 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](04-generators-euclidean)). The one exception is the `dropout()` transform ([§12.6](12-deep-generative)): 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: ```{list-table} Four dials that are easy to confuse :header-rows: 1 :widths: 22 78 * - Dial - What it sets * - `probability` - The chance a note **plays** (`1.0` = certain) — or, on `dropout()`, the chance it is **removed**. * - `density` - The **fill fraction** of a texture — how much of the grid a `ghost_fill` or `bresenham_poly` voice takes up. * - `amount` - **Thinning depth** — how aggressively `thin` prunes what is already there. * - `percent` - **Swing angle** — `50` straight, `57` a gentle shuffle, `67` hard triplet swing. ``` (sec-appG-units)= ## 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](03-notes-beats-durations)). 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](01-step-grid), [§3.2](03-notes-beats-durations)). 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. (sec-appG-velocity)= ## 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: ```{list-table} Default velocity by material :header-rows: 1 :widths: 34 40 26 * - Material - Constant - Value * - Single notes and hits - `DEFAULT_VELOCITY` - `100` * - Chords - `DEFAULT_CHORD_VELOCITY` - `90` * - Generative lines - `DEFAULT_GENERATIVE_VELOCITY` - `80` * - Cellular automata - `DEFAULT_CA_VELOCITY` - `60` * - Ghost layers - `GHOST_FILL_VELOCITY` - `35` ``` ```{doctest} appG >>> 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](#sec-appG-tuple)) 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. (sec-appG-names)= ## 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](01-step-grid)): a mistyped `"kick_99"` shouldn't silence your whole kit. But an unmapped CC, NRPN, or RPN **name** *raises* ([§13.1](13-expression-hardware), [§13.3](13-expression-hardware)) — 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. (sec-appG-chain)= ## 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](04-generators-euclidean)). 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](../api/subsequence/index) reads less like a list to memorise and more like a language you already speak. For the exhaustive signatures see [Appendix D](appendix-d-api-reference); for this idea in its first, smallest form, [§4.5](04-generators-euclidean).