# Chapter 2 · The Rebuild Loop: Patterns That Remember In [Chapter 1](01-step-grid) you painted a beat onto the step grid by hand. This chapter explains *when* that painting actually happens — and uses that one idea to make the loop change from bar to bar, and to let two patterns talk to each other. So far your `drums` function has felt like a recipe you wrote once. The surprise — and the whole engine, really — is that Subsequence **runs that function again at the start of every bar**. Once you can see that, a pattern stops being a fixed loop and becomes something that can react: a fill on the fourth bar, a busier hat on odd bars, a bass that ducks out of the way of a snare roll. Everything in the rest of the guide is built on this single mechanism. ## 2.1 Your function re-runs every cycle When you decorate a function with `@composition.pattern(...)`, you are not handing Subsequence a finished loop. You are handing it a *function* — and the engine **calls that function fresh at the start of every cycle**. A "cycle" is one pass of the pattern: for a 4-beat (one-bar) pattern, that's once per bar. The clearest way to see it is to print something each time the function runs. Add one line to the Chapter 0 drum loop: ```{testcode} ch2 import subsequence import subsequence.constants.instruments.gm_drums as gm_drums composition = subsequence.Composition(bpm=120) @composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP) def drums(p): print(f"building cycle {p.cycle}") # runs once per bar p.hit_steps("kick_1", [0, 4, 8, 12], velocity=100) p.hit_steps("snare_1", [4, 12], velocity=90) p.hit_steps("hi_hat_closed", range(16), velocity=70) composition.render(bars=4, filename="hello.mid") ``` Rendering four bars prints: ```{testoutput} ch2 building cycle 0 building cycle 1 building cycle 2 building cycle 3 building cycle 4 ``` The function ran **five times** — once to build each of the four bars, plus one more to have the next bar ready at the boundary. That last extra build is normal: the engine always builds *one cycle ahead* so the music never stutters. You can ignore it; just know that the count you see is "builds", not "bars heard". `p.cycle` is that **rebuild counter**: a plain integer, starting at `0`, that goes up by one every time your function is called. It's the engine's way of telling you "this is the *n*th time I've run you." ```{note} This is why Subsequence is called a *generative* sequencer rather than a looper. A looper records sixteen steps and replays the same sixteen for ever. Subsequence **re-derives** the bar each time from your code, so anything your code can compute — a random choice, a count, the current position — can change what plays. A loop that re-runs is a loop that can evolve. ``` (sec-fresh-canvas)= ## 2.2 `p` is a fresh canvas (values vs canvas) Here is the part that trips up everyone exactly once. Each time the engine calls your function, the `p` it hands you is **brand new** — a blank canvas with no notes on it. Nothing you painted last bar carries over. Every `p.hit_steps(...)` line you write is starting from an empty bar and filling it in again. That sounds wasteful, but it's the source of all the power: because the canvas is empty every time, *you* decide from scratch what goes on it, and you can decide differently depending on the cycle. It also means there are two very different kinds of "stuff" in a pattern, and keeping them straight is the key skill of this chapter: ```{list-table} :header-rows: 1 :widths: 22 78 * - Kind - Lifetime * - **The canvas** (`p` and everything you call on it) - Thrown away and rebuilt every cycle. Only good for *this* bar. * - **Values** (numbers, lists, strings — your data) - Last as long as you keep them. Put them where they'll survive. ``` A natural instinct, coming from other tools, is to try to *hold on to* `p` — to stash it in a variable and add more notes to it next time. Don't. The `p` from last cycle is gone; the engine has already discarded it and made a new one. Trying to reuse it does nothing useful. ```{warning} **Never hoard the builder.** Treat `p` as borrowed for the length of one function call — paint this bar and let it go. Anything you want to *remember* between bars must be a plain value, stored somewhere that outlives the call: a module-level variable ([§2.4](#sec-module-values)) or the shared `p.data` dict ([§2.5](#sec-data)). Store **values**, never the canvas. ``` For now, notice that the drum loop already obeys this rule perfectly — it builds the same fresh bar every cycle and keeps nothing: ```{testcode} ch2 composition = subsequence.Composition(bpm=120) @composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP) def drums(p): # p is a blank canvas this cycle; nothing carries over from last bar. p.hit_steps("kick_1", [0, 4, 8, 12], velocity=100) p.hit_steps("hi_hat_closed", range(16), velocity=70) composition.render(bars=4, filename="hello.mid") ``` Because the canvas is fresh and your code is in charge of filling it, the obvious next move is to fill it *differently* depending on where we are in the song. That needs a way to ask "where am I?" — which is exactly what the next section gives you. (sec-position)= ## 2.3 Reacting to position (`p.cycle`, `p.bar`, `p.bar_cycle`) Your function gets three readings of "where am I in time", all on `p`: ```{list-table} :header-rows: 1 :widths: 26 74 * - Reading - Meaning * - `p.cycle` - How many times **this pattern** has rebuilt — a counter from 0, +1 each cycle. * - `p.bar` - The **global bar number** of the whole composition, from 0. * - `p.bar_cycle(length)` - This bar's place inside a repeating group of `length` bars (see below). ``` For a one-bar pattern, `p.cycle` and `p.bar` happen to be the same number. They *differ* when a pattern is longer than a bar: a two-bar pattern rebuilds once every two bars, so its `p.cycle` ticks 0, 1, 2… while `p.bar` jumps 0, 2, 4…. The rule of thumb: **`p.cycle` counts your rebuilds; `p.bar` counts the song's bars.** Most of the time you'll reach for whichever reads more naturally. ### Branching on the cycle Since `p.cycle` is just a number, ordinary Python decides what to paint. To make odd bars busier than even ones, test whether the cycle is even with `% 2` (the remainder operator: `cycle % 2` is `0` on even cycles, `1` on odd): ```{testcode} ch2 composition = subsequence.Composition(bpm=120) @composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP) def drums(p): p.hit_steps("kick_1", [0, 4, 8, 12], velocity=100) if p.cycle % 2 == 0: p.hit_steps("hi_hat_closed", [0, 4, 8, 12], velocity=70) # even bars: sparse else: p.hit_steps("hi_hat_closed", range(16), velocity=70) # odd bars: busy composition.render(bars=4, filename="hello.mid") ``` The two branches paint onto the same fresh canvas; only one runs each cycle, so the bar alternates sparse, busy, sparse, busy. ### Phrasing with `bar_cycle` Music is rarely "every other bar" — it's "the last bar of every four", "the first bar of the section". Writing that with raw remainders (`p.bar % 4 == 3`) works but reads poorly. `p.bar_cycle(length)` says it in plain musical language. It returns a small object describing this bar's position within a repeating group of `length` bars, with three readable properties: - `.first` — `True` on the opening bar of the group (bar 0 of the cycle). - `.last` — `True` on the closing bar of the group. - `.progress` — how far through the group we are, from `0.0` rising toward `1.0` (for a 4-bar group: `0.0, 0.25, 0.5, 0.75`). Handy for gradual build-ups. The classic use is a fill on the fourth bar of every four-bar phrase: ```{testcode} ch2 composition = subsequence.Composition(bpm=120) @composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP) def drums(p): p.hit_steps("kick_1", [0, 4, 8, 12], velocity=100) p.hit_steps("snare_1", [4, 12], velocity=90) p.hit_steps("hi_hat_closed", range(16), velocity=70) # On the last bar of every four, add a snare roll into the next phrase. if p.bar_cycle(4).last: p.hit_steps("snare_1", [10, 11, 13, 14, 15], velocity=110) p.hit_steps("hi_hat_open", [14], velocity=100) composition.render(bars=8, filename="hello.mid") ``` Render eight bars and you'll hear the steady groove for three bars, a roll on the fourth, then the cycle resets and it happens again — the loop you wrote in Chapter 1, now phrased. ```{tip} `p.bar_cycle(4).first` and `p.bar_cycle(4).last` are the two you'll use constantly: the downbeat of a phrase and its turnaround. Reach for `.progress` when you want a *gradual* change rather than an on/off one — for example feeding it into a velocity ramp so the energy lifts across the four bars. ``` ```{admonition} Reference :class: seealso {py:meth}`~subsequence.pattern_builder.PatternBuilder.bar_cycle`, {py:class}`~subsequence.pattern_builder.BarCycle` ``` (sec-module-values)= ## 2.4 Module-level reusable values The branches above carry literal lists like `[10, 11, 13, 14, 15]` right where they're used. That's fine for one, but once the same step list appears in two patterns — or you want to tweak the kick pattern in one place — it's cleaner to **name the value once, outside the function.** A variable written at the top level of your file (not indented inside any function) is a **module-level value**. It is created once, when the file is first read, and it stays put. Every cycle your pattern function can read it — but, crucially, the engine never resets it, because it isn't part of the throwaway canvas. It's a value, so it lasts ([§2.2](#sec-fresh-canvas)). ```{testcode} ch2 composition = subsequence.Composition(bpm=120) # Named once, at module level — reused by the pattern on every cycle. FOUR_ON_FLOOR = [0, 4, 8, 12] BACKBEAT = [4, 12] SNARE_FILL = [10, 11, 13, 14, 15] @composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP) def drums(p): p.hit_steps("kick_1", FOUR_ON_FLOOR, velocity=100) p.hit_steps("snare_1", BACKBEAT, velocity=90) p.hit_steps("hi_hat_closed", range(16), velocity=70) if p.bar_cycle(4).last: p.hit_steps("snare_1", SNARE_FILL, velocity=110) composition.render(bars=8, filename="hello.mid") ``` This reads almost like a score: the names tell you *what* each list is, and the pattern body tells you *when* it plays. Names in capitals (`FOUR_ON_FLOOR`) are a Python convention for "this is a fixed value I set up front and don't change" — exactly what a reusable step list is. Because a module-level value survives between cycles, it can also *remember* across them. A running count is the simplest example. To change a module-level value from inside a function, Python needs one extra word — `global` — to confirm you mean the outer variable and not a new local one: ```{testcode} ch2 composition = subsequence.Composition(bpm=120) hit_total = 0 # lives at module level, so it survives every rebuild @composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP) def drums(p): global hit_total # "I mean the outer hit_total, not a new one" steps = [0, 4, 8, 12] hit_total += len(steps) # accumulates across cycles p.hit_steps("kick_1", steps, velocity=100) composition.render(bars=4, filename="hello.mid") print(hit_total) ``` ```{testoutput} ch2 20 ``` Five cycles built (four bars plus the build-ahead), four kicks each, gives `20`. The counter survived every rebuild precisely because it's a value living *outside* the canvas — the same reason your step lists do. ```{important} This is the practical shape of "store values, never hoard `p`": data you want to keep — step lists, counts, a chosen scale, anything — lives at module level (or in `p.data`, next). The builder `p` stays disposable. Get this division right and patterns that evolve become straightforward; get it wrong and you'll fight the engine resetting things you expected to persist. ``` (sec-data)= ## 2.5 Sharing state with `p.data` Module-level values let *one* pattern remember across cycles. But a composition usually has several patterns, and often one should react to another: a bass that gets out of the way when the drums play a fill, a hat that follows the kick's intensity. They need a shared place to leave notes for each other — and that place is **`p.data`**. `p.data` is a plain Python dictionary that **every pattern shares**. It is the very same object as `composition.data`, so a value one pattern writes is visible to the others. You write to it like any dict (`p.data["name"] = value`) and read from it with `p.data.get("name", default)` (the `default` is what you get back if no one has written that key yet). The timing that makes this work: **patterns rebuild top to bottom, in the order you defined them.** So a pattern defined *earlier* in your file runs *before* one defined later in the same cycle — which means an earlier writer's value is ready for a later reader to pick up, that same bar. Here the `drums` pattern decides each bar whether it's a fill bar and leaves that decision in `p.data`; the `bass`, defined below it, reads the flag and drops out on fill bars to leave room: ```{testcode} ch2 composition = subsequence.Composition(bpm=120) # Defined FIRST — so it writes p.data before the bass reads it this cycle. @composition.pattern(channel=10, beats=4, drum_note_map=gm_drums.GM_DRUM_MAP) def drums(p): fill_bar = p.bar_cycle(4).last # True on the 4th bar of each phrase p.data["fill_bar"] = fill_bar # leave the decision for later patterns p.hit_steps("kick_1", [0, 4, 8, 12], velocity=100) p.hit_steps("snare_1", [4, 12], velocity=90) if fill_bar: p.hit_steps("snare_1", [10, 11, 13, 14, 15], velocity=110) # Defined SECOND — reads the flag the drums just wrote this cycle. @composition.pattern(channel=1, beats=4) def bass(p): if p.data.get("fill_bar", False): return # silent this bar — let the fill breathe p.note(36, beat=0, velocity=100, duration=1) p.note(36, beat=2, velocity=90, duration=1) composition.render(bars=8, filename="hello.mid") ``` Render it and the bass plays its two notes for three bars, then sits out the fourth bar while the snare roll lands — every four bars. The two patterns never call each other; they coordinate purely through one shared value. ```{note} **Note `p.note(36, beat=0, ...)` always names a beat.** Unlike a drum hit on the grid, a melodic note needs to know *where* in the bar it lands, so `beat=` is required — beat `0` is the downbeat, `2` is the third quarter. We'll dig into beats and durations properly in [Chapter 3](03-notes-beats-durations); here the bass is just enough to hear two patterns react to each other. ``` ```{warning} The writer-before-reader guarantee depends on **definition order**: when two patterns rebuild together — same `beats`, so they share a cycle, as `drums` and `bass` do here — the one defined *above* runs first, and its fresh value is ready for the one below. If you instead read a key the writer hasn't set yet this cycle, you get the previous cycle's value (or the `default`). So when order matters, put the writer first — and always pass a sensible default to `.get()` for the very first cycle, before any key exists. ``` ```{tip} `p.data` isn't only for pattern-to-pattern talk. Because it's the same dict as `composition.data`, it's also where **outside** information lands — a value mapped from a hardware knob, or data you set before playback. The patterns don't care where a value came from; they just read the dict. That makes `p.data` the seam between your music and the world, a thread we pick up much later when we sonify external data. ``` --- You now have the engine's heartbeat: a pattern is a function the engine re-runs every cycle onto a fresh canvas, while your *values* — at module level or in `p.data` — persist and let bars differ and patterns coordinate. In [Chapter 3](03-notes-beats-durations) we leave the drum grid behind and write actual pitched material: notes at chosen beats, with real durations, including the hand-written bassline this chapter only sketched.