# Chapter 7 · Progressions as Values: Building and Shaping Harmony In [Chapter 6](06-harmony-context) you let a chord engine *generate* harmony live and had your patterns follow it; now we write the chords down ourselves — as a plain, printable value you can inspect, voice, decorate, cap with a cadence, and hand to the engine or play directly. A **progression** in Subsequence is not a live thing that runs — it's a *value*, like a list of numbers. You build it once, look at it, transform it (each transform hands you a new progression, leaving the original untouched), and only *then* decide how it sounds. That separation — describe the harmony first, perform it second — is what this chapter is about. It carries the running example forward: the GM drum loop and bass from earlier chapters, now joined by chord parts that play a progression you wrote by hand. ```{testsetup} ch7 # Hidden per-chapter setup: the imports the first example shows in full, plus the # beat-duration constants, so later blocks can use them without repeating. import subsequence from subsequence import progression, parse_chord, between, Progression import subsequence.constants.durations as dur ``` (sec-progression-value)= ## 7.1 Writing a progression as a value The one verb you need is **`subsequence.progression(...)`**. Give it a list of chords and it returns a `Progression` — a frozen sequence of chords-with-durations that knows nothing about playback yet. The list is the *standard form*, and you can write each chord three different ways, mixing them freely: ```{testcode} ch7 import subsequence from subsequence import progression import subsequence.constants.durations as dur # Three ways to name the same idea — pick whichever reads clearest to you. by_degree = progression([1, 6, 4, 5]) # scale degrees, key-relative by_roman = progression(["i", "VI", "iv", "V"]) # roman numerals, quality explicit by_name = progression(["Am", "F", "Dm", "E"]) # concrete chord names ``` The three forms differ in *how much they pin down*: ```{list-table} :header-rows: 1 :widths: 22 30 48 * - Element - Example - What it means * - **Int degree** - `1`, `6`, `4` - A scale degree (1-based). Quality is inferred from the key and scale *later*, when the progression is bound — so `1` is major in a major key, minor in a minor key. * - **Roman string** - `"V"`, `"ii7"`, `"bVII7"` - A degree with its quality spelled out (uppercase major, lowercase minor, `7` for a seventh, `b`/`#` for chromatic degrees). Still key-relative. * - **Chord name** - `"Am"`, `"Cmaj7"`, `"F#"` - A concrete chord, with no key needed. Sounds the same in every key. ``` The first two are **key-relative**: they store a degree, not a pitch, so the same written progression transposes by changing one key. The third is **concrete**: `"Am"` is always A minor. (This is exactly the *specification-resolved-late* idea from [§5.5](05-pitch-and-scales) — a progression is a bundle of pitch specifications, resolved against a key when you ask.) The chord-name strings are read by **`parse_chord(name)`**, which you can also call on its own to turn a single name into a chord object — handy for checking that a name spells what you expect (it normalises enharmonics, so `Db` reads as `C#`): ```{testcode} ch7 from subsequence import parse_chord print(parse_chord("Cm7").name()) # C minor 7th print(parse_chord("Dbmaj7").name()) # spelled C#maj7 print(parse_chord("F#").name()) # plain major ``` ```{testoutput} ch7 Cm7 C#maj7 F# ``` By default each chord lasts one bar (4 beats in common time). To give chords different lengths — a *harmonic rhythm* that breathes — pair each one with a beat count using `(chord, beats)` tuples, or pass a `beats=` list that cycles: ```{testcode} ch7 # Two bars of Am, then one each of F and G — written as (chord, beats) tuples. shaped = progression([("Am", 8), ("F", 4), ("G", 4)]) # The same shape with a cycled beats= list. also_shaped = progression(["Am", "F", "G"], beats=[8, 4, 4]) ``` You can also *generate* a progression instead of writing one — pass `style=` and a `key=` and Subsequence walks a chord graph for you (the same engine from [Chapter 6](06-harmony-context), here producing a value rather than a live stream): ```{testcode} ch7 walk = progression(style="aeolian_minor", key="A", bars=8, seed=3) ``` ```{note} **Generation wants a `seed=`.** A generated progression with no seed is a fresh random walk every time the file reloads, which breaks live coding. Pass `seed=` (any number) to pin the walk so the value is the same on every run — exactly the repeatability habit from [§4.6](04-generators-euclidean). Hand-written lists need no seed; they're already fixed. ``` ```{admonition} Reference :class: seealso {py:func}`~subsequence.progressions.progression`, {py:class}`~subsequence.progressions.Progression`, {py:func}`~subsequence.chords.parse_chord` ``` (sec-inspecting)= ## 7.2 Inspecting it: `.describe()` and `print` Because a progression is a value, you can *look at it* before you ever make a sound. Every progression prints itself — `print(prog)` calls its `.describe()` — giving a one-chord-per-line summary with the running beat positions. This is the fastest way to check you wrote the harmony you meant. ```{testcode} ch7 verse = progression([1, 6, 4, 5]) print(verse.describe()) ``` ```{testoutput} ch7 Progression — 4 chords over 16 beats 0.00 … 4.00 1 (4 beats) 4.00 … 8.00 6 (4 beats) 8.00 … 12.00 4 (4 beats) 12.00 … 16.00 5 (4 beats) ``` Unbound, a key-relative progression prints its *degrees* — there's no key yet, so there are no chord names to show. Hand `.describe()` a key (and scale) and it resolves every degree to a concrete chord, so you can read the actual harmony: ```{testcode} ch7 print(verse.describe("A", "minor")) ``` ```{testoutput} ch7 Progression — 4 chords over 16 beats 0.00 … 4.00 Am (4 beats) 4.00 … 8.00 F (4 beats) 8.00 … 12.00 Dm (4 beats) 12.00 … 16.00 Em (4 beats) ``` Same value, two readings — `1 6 4 5` in the abstract, `Am F Dm Em` in A minor. A few more properties are handy when you're building progressions programmatically: ```{testcode} ch7 named = progression(["Am", "F", "C", "G"]) print(len(named)) # number of chords print(named.length) # total length in beats print(named.is_concrete) # True — no degrees/romans left to resolve ``` ```{testoutput} ch7 4 16.0 True ``` ```{tip} `print(prog)` is to harmony what the ASCII grid from [§1.7](01-step-grid) is to rhythm: a no-instrument-needed sanity check. If a chord is in the wrong place or the wrong quality, you'll see it in the printout long before you render a note. ``` ```{admonition} Reference :class: seealso {py:meth}`~subsequence.progressions.Progression.describe` ``` (sec-voicing)= ## 7.3 Voicing and voice-leading So far a progression only says *which* chords and *how long*. **Voicing** is the next layer — *how* each chord is stacked: which note is on the bottom, how spread out the notes are. Two transforms shape it, and each returns a brand-new progression (the original is never touched): - **`.inversions(spec)`** rotates which chord tone sits in the bass. `0` is root position; `1` puts the third on the bottom; `2` the fifth. Pass one int for all chords, or a list that cycles per chord. - **`.spread(style)`** sets how wide the voicing is: `"close"` (the default, notes bunched together), `"open"` (drop-2 — the second voice from the top drops an octave), or `"wide"` (drop-2-and-4, the most open). ```{testcode} ch7 base = progression(["Am", "F", "C", "G"]) smooth = base.inversions([0, 1, 2, 0]) # vary the bass note chord to chord airy = base.spread("open") # a wider, more open stack ``` Inversions and spread don't change the chord *names* (so they don't show in `.describe()`), but they very much change the notes that sound. You can see that by asking each chord for its `tones()` — the MIDI notes it would play around a given root: ```{testcode} ch7 for chord, start, length in base.inversions([0, 1, 2, 0]): print(chord.name(), chord.tones(root=60)) ``` ```{testoutput} ch7 Am [57, 60, 64] F [69, 72, 77] C [67, 72, 76] G [55, 59, 62] ``` ### Letting the placement smooth itself: `voice_leading=True` Choosing inversions by hand so the chords connect smoothly is fiddly. Often you'd rather just say *"keep each chord close to the last one"* and let Subsequence pick the inversion that moves the fewest notes. That's **voice leading**, and you turn it on where the chords are *placed*, not on the progression value itself: add `voice_leading=True` to the pattern decorator, and `p.chord(...)` automatically chooses the smoothest inversion each time. ```{testcode} ch7 import subsequence from subsequence import progression composition = subsequence.Composition(bpm=120, key="A", scale="minor") composition.harmony(progression=progression([1, 6, 4, 5])) @composition.pattern(channel=3, beats=4, voice_leading=True) def pad(p, chord): p.chord(chord, root=52, velocity=80, sustain=True) # inversion chosen for you composition.render(bars=8, filename="pad.mid") ``` That `chord` argument is the live chord injected into the pattern — declaring a parameter named `chord` is how a pattern asks to follow the harmony, exactly as in [§6.2](06-harmony-context). Here the harmony comes from a progression we *bound* to the engine; more on binding in [§7.6](#sec-placing). ```{important} **Inversion is set in two different places, and they don't fight — one wins.** `.inversions(...)` on the progression value is a fixed choice baked into each chord. `voice_leading=True` on the pattern *overrides* that, choosing the inversion dynamically to minimise movement. Use `.inversions(...)` when you want a specific shape every time; use `voice_leading=True` when you just want it to sound connected and don't care how. Setting both means voice leading wins. ``` ```{admonition} Reference :class: seealso {py:meth}`~subsequence.progressions.Progression.inversions`, {py:meth}`~subsequence.progressions.Progression.spread` ``` (sec-decoration)= ## 7.4 Decoration and substitution Beyond voicing, you can *decorate* the chords themselves — add colour tones, put the whole thing over a pedal bass, or swap individual chords out. Every one of these returns a new progression, so they **chain**: each call takes a progression and hands back a richer one, in the same fluent style as the builder verbs from [§4.5](04-generators-euclidean). **`.extend(...)`** adds extension tones to every chord — `7`, `9`, `11`, `13` for stacked sevenths and above, or `"sus2"`, `"sus4"`, `"add9"`, `"6"` for the named colours. Restrict it to particular chords with `only=` (a list of 1-based slots): ```{testcode} ch7 jazzy = progression([2, 5, 1]).extend(7) # ii7 V7 I7-style sevenths everywhere print(jazzy.describe("C", "ionian")) ``` ```{testoutput} ch7 Progression — 3 chords over 12 beats 0.00 … 4.00 Dm7 (4 beats) 4.00 … 8.00 G7 (4 beats) 8.00 … 12.00 C7 (4 beats) ``` Notice the qualities came out *diatonically correct*: extending the degrees `2 5 1` in C major gave `Dm7 G7 C7`, with G getting its dominant seventh — the engine knows the scale, so the extensions land in key. (Writing seventh chords by name — `["Dm7", "G7", "Cmaj7"]` — works too, and pins the quality yourself.) **`.over(bass)`** puts the whole progression over a slash/pedal bass — *the* trance and house move, a single low note held while the chords shift above it. Pass a note name (`"E"`), a pitch class, or `"tonic"` (which follows the key): ```{testcode} ch7 pedal = progression(["Am", "F", "C", "G"]).over("E") # Am/E, F/E, C/E, G/E print(pedal.describe()) ``` ```{testoutput} ch7 Progression — 4 chords over 16 beats 0.00 … 4.00 Am/E (4 beats) 4.00 … 8.00 F/E (4 beats) 8.00 … 12.00 C/E (4 beats) 12.00 … 16.00 G/E (4 beats) ``` **`.replace(slot, chord)`** swaps one chord for another, keeping its beats (slots are 1-based, the musician's way of counting chords). It's the surgical edit for trying a substitution: ```{testcode} ch7 swapped = progression(["Am", "F", "C", "G"]).replace(2, "Dm") # F → Dm print(swapped.describe()) ``` ```{testoutput} ch7 Progression — 4 chords over 16 beats 0.00 … 4.00 Am (4 beats) 4.00 … 8.00 Dm (4 beats) 8.00 … 12.00 C (4 beats) 12.00 … 16.00 G (4 beats) ``` For a bigger gesture, **`.elaborate(depth)`** approaches each chord by a chain of secondary dominants — the jazz/blues move of inserting `V7`-of-the-next-chord turnarounds. `depth=1` puts one dominant before each chord; `depth=2` a full ii–V. It needs a *concrete* progression (so `.resolve(key)` first, or write names): ```{testcode} ch7 blues = progression("twelve_bar_blues").resolve("C") chorus2 = blues.elaborate(2, seed=4) # ii–V turnarounds woven throughout print(len(blues), "→", len(chorus2), "chords") ``` ```{testoutput} ch7 12 → 36 chords ``` ```{note} `progression("twelve_bar_blues")` is a *preset* — a bare string names one of a small curated set of recognisable loops (`"pop_axis"`, `"doo_wop"`, `"ii_v_i"`, `"andalusian"`, and more). Presets are key-relative romans, so `.resolve("C")` pins them to a key. They're a quick starting point you then shape with the transforms above. ``` ```{admonition} Reference :class: seealso {py:meth}`~subsequence.progressions.Progression.extend`, {py:meth}`~subsequence.progressions.Progression.over`, {py:meth}`~subsequence.progressions.Progression.replace`, {py:meth}`~subsequence.progressions.Progression.elaborate`, {py:meth}`~subsequence.progressions.Progression.resolve` ``` (sec-cadences)= ## 7.5 Cadences: strong, soft, open, fakeout A **cadence** is how a phrase ends — the harmonic punctuation that makes it feel finished, half-finished, or deliberately swerved. **`.cadence(name)`** rewrites the *tail* of a progression with a named two-chord formula, keeping the beats of the chords it replaces. The names are plain-language: ```{list-table} :header-rows: 1 :widths: 18 22 60 * - Name - Formula - The feeling * - `"strong"` - V → I - The full stop. Lands home, conclusively. (Theory: *authentic*.) * - `"soft"` - IV → I - The gentle "amen" close — softer than V→I. (*Plagal*.) * - `"open"` - IV → V - Hangs on the dominant, unresolved — a question, a comma. (*Half*.) * - `"fakeout"` - V → vi - Sets up the full stop, then dodges to the submediant instead of home. (*Deceptive*.) ``` Take a four-chord loop and cap it three different ways — the first two chords stay, the last two become the cadence: ```{testcode} ch7 loop = progression(["Am", "F", "C", "G"]) print(loop.cadence("open").describe("A", "minor")) # … Dm E — hangs open ``` ```{testoutput} ch7 Progression — 4 chords over 16 beats 0.00 … 4.00 Am (4 beats) 4.00 … 8.00 F (4 beats) 8.00 … 12.00 Dm (4 beats) 12.00 … 16.00 E (4 beats) ``` The open cadence replaced `C G` with `Dm E` (IV → V in A minor) — the phrase now *hangs* on the E dominant instead of resolving. Swap `"open"` for `"fakeout"` and the tail becomes `E F` instead: the ear is set up for the home chord (Am) by that E dominant, then gets F — the submediant — and the close is dodged. (In a major key the same formula is the textbook V → vi, swerving onto the relative minor; under a minor key the sixth degree is major, so the swerve lands on a bright chord instead.) ```{tip} Cadences pair naturally with the *structure* tools coming later: an `"open"` cadence at the end of a verse leaves the door ajar for the chorus, and a `"strong"` cadence slams a section shut. You can also ask the live engine to *aim for* a cadence as it walks — that's the `cadence=` argument you'll meet on `composition.freeze(...)` in [§7.6](#sec-placing). ``` ```{admonition} Reference :class: seealso {py:meth}`~subsequence.progressions.Progression.cadence` ``` (sec-placing)= ## 7.6 Placing harmony: `p.progression`, `composition.chords`, `between()` A progression value makes no sound on its own — something has to *place* it. There are three seams, from most-hands-on to most-automatic. ### Inside a pattern, chord by chord: `p.progression(...)` `p.progression(source, harmonic_rhythm=...)` lays a progression end-to-end across the current pattern and hands you back the realised timeline to loop over. Each iteration gives `(chord, start, length)` — the chord, the beat it starts on, and how long it lasts — so you decide exactly how to play each one: ```{testcode} ch7 import subsequence from subsequence import progression import subsequence.constants.durations as dur composition = subsequence.Composition(bpm=120, key="A", scale="minor") @composition.pattern(channel=3, beats=16) def pads(p): for chord, start, length in p.progression([1, 6, 4, 5], harmonic_rhythm=dur.WHOLE): p.chord(chord, root=48, beat=start, duration=length - 0.25, velocity=80) composition.render(bars=4, filename="pads.mid") ``` The `harmonic_rhythm` is *how long each chord lasts*, in beats. `dur.WHOLE` (4 beats) gives one chord per bar. The duration constants from [§3.5](03-notes-beats-durations) read musically: `dur.HALF`, `dur.WHOLE`, etc. This is the **part-level** seam: it runs in its own little world, outside the global chord engine, so this part can have harmony nobody else follows. It re-realises fresh each cycle, which matters once the rhythm varies (below). ### Variable harmonic rhythm: `between()` For a static rhythm, pass a single number. For a *shaped* one, pass a list of lengths that cycles (`[dur.WHOLE, dur.HALF, dur.HALF]`). For an *irregular* one — each chord a fresh random length — pass **`between(low, high, step=...)`**. The `step=` snaps those lengths to a grid so they stay musical: ```{testcode} ch7 import subsequence from subsequence import progression, between import subsequence.constants.durations as dur composition = subsequence.Composition(bpm=120, key="A", scale="minor") @composition.pattern(channel=3, beats=16) def breathing(p): # Each chord lasts 1, 2, or 3 whole notes — irregular but always on a bar line. rhythm = between(dur.WHOLE, 3 * dur.WHOLE, step=dur.WHOLE) for chord, start, length in p.progression("phrygian_minor", harmonic_rhythm=rhythm, seed=7): p.chord(chord, root=48, beat=start, duration=length - 0.25, velocity=80) composition.render(bars=4, filename="breathing.mid") ``` ```{note} `between(low, high)` is the harmonic-rhythm cousin of the `(low, high)` velocity tuple from [§1.5](01-step-grid): a bounded random draw, one per chord. It's spelled `between(...)` rather than a bare tuple precisely so a plain list can keep its other meaning — a *sequence* of lengths to cycle through. ``` ### A whole chord part in one call: `composition.chords(...)` When you just want a block-chord part and don't need to shape each chord yourself, `composition.chords(...)` declares the entire pattern for you — progression, harmonic rhythm, voicing, and all. It returns the realised timeline so you can `print` it and see exactly what it chose: ```{testcode} ch7 import subsequence from subsequence import progression import subsequence.constants.durations as dur composition = subsequence.Composition(bpm=120, key="A", scale="minor") timeline = composition.chords( channel=3, progression=["Am", "F", "C", "G"], harmonic_rhythm=dur.WHOLE, bars=4, root=48, voicing=(3, 4), # 3 or 4 notes per chord velocity=80, ) print(timeline.describe()) composition.render(bars=4, filename="chord-part.mid") ``` ```{testoutput} ch7 Progression — 4 chords over 16 beats 0.00 … 4.00 Am (4 beats) 4.00 … 8.00 F (4 beats) 8.00 … 12.00 C (4 beats) 12.00 … 16.00 G (4 beats) ``` This needs no `composition.harmony(...)` call and — with an explicit chord list or a `key=` — no composition key either, so a drums-plus-one-chord-part sketch stays tiny. ### Driving the whole engine: `composition.harmony(progression=...)` The third seam binds a progression to the *global* chord engine, so it becomes the harmony that **every** chord-following pattern reads. This is the bridge back to [Chapter 6](06-harmony-context): instead of the engine generating chords, you hand it a fixed progression, and the injected `chord` parameter delivers it to your basses, pads, and arpeggios: ```{testcode} ch7 import subsequence from subsequence import progression composition = subsequence.Composition(bpm=120, key="A", scale="minor") composition.harmony(progression=progression([1, 6, 4, 5])) # loops forever @composition.pattern(channel=6, beats=4) def bass(p, chord): p.note(chord.bass_note(40), beat=0, duration=3.5, velocity=90) # root of each chord @composition.pattern(channel=3, beats=4, voice_leading=True) def pad(p, chord): p.chord(chord, root=52, velocity=80, sustain=True) composition.render(bars=8, filename="bound.mid") ``` ```{list-table} Which placement seam? :header-rows: 1 :widths: 32 68 * - Seam - Reach for it when * - `p.progression(...)` in a pattern - You want to play each chord *your* way (strummed, broken, voiced) inside one pattern — and possibly give that part its own private harmony. * - `composition.chords(...)` - You want a complete block-chord part in a single call, no loop to write. * - `composition.harmony(progression=...)` - You want this progression to be *the* harmony of the piece, followed by every pattern that declares a `chord` parameter. ``` ### Capturing live harmony: `composition.freeze()` (a preview) One more source of progressions: you can let the live engine generate harmony and then **freeze** a stretch of it into a progression value. `composition.freeze(bars)` runs the engine forward and captures the chords it produces — useful for keeping a section's harmony fixed while the rest of the piece keeps generating: ```{testcode} ch7 import subsequence composition = subsequence.Composition(bpm=120, key="A", scale="minor", seed=11) composition.harmony(style="aeolian_minor", cycle_beats=4) chorus = composition.freeze(4, cadence="strong") # 4 chords, ending V → i print(chorus.describe("A", "minor")) ``` ```{testoutput} ch7 Progression — 4 chords over 16 beats 0.00 … 4.00 Am (4 beats) 4.00 … 8.00 Dm (4 beats) 8.00 … 12.00 E (4 beats) 12.00 … 16.00 Am (4 beats) ``` Notice `cadence="strong"` steered the *walk* to arrive at V → i (E → Am) — the same cadence vocabulary from [§7.5](#sec-cadences), here aiming the engine instead of rewriting a tail. The full explore-then-capture workflow — seeds, locks, and what `freeze` does to engine state — is Chapter 11; this is just the preview that it hands you back an ordinary progression value, ready for everything in this chapter. ```{note} Binding a frozen (or hand-written) progression to a *section* of a larger form is a one-liner, `composition.section_chords("chorus", chorus)`, so different parts of a track can carry different harmony. Sections and forms are Chapter 10; we mention it here only so you know the captured value has somewhere to go. ``` ```{admonition} Reference :class: seealso {py:meth}`~subsequence.pattern_builder.PatternBuilder.progression`, {py:meth}`~subsequence.composition.Composition.chords`, {py:meth}`~subsequence.composition.Composition.harmony`, {py:meth}`~subsequence.composition.Composition.freeze`, {py:meth}`~subsequence.composition.Composition.section_chords` ``` (sec-strum-broken)= ## 7.7 Strummed and broken chords A block chord — all notes at once — is only one way to play harmony. Two verbs give you the others, and both take the injected `chord` (or a chord from a `p.progression` loop) exactly like `p.chord` does. **`p.strum(...)`** plays the chord tones one after another with a tiny delay between them — the guitar/harp gesture. `spacing` is the gap between notes (in beats), and `direction` is `"up"` (low to high) or `"down"`: ```{testcode} ch7 import subsequence from subsequence import progression import subsequence.constants.durations as dur composition = subsequence.Composition(bpm=120, key="A", scale="minor") @composition.pattern(channel=3, beats=16) def guitar(p): for chord, start, length in p.progression([1, 6, 4, 5], harmonic_rhythm=dur.WHOLE): p.strum(chord, root=48, beat=start, spacing=0.04, count=4, duration=length - 0.25, velocity=75) composition.render(bars=4, filename="strummed.mid") ``` **`p.broken_chord(...)`** spells the chord out as an arpeggio in an order you choose — `order` is a list of indices into the chord tones (`0` = root, `1` = third, `2` = fifth, and on up, cycling into higher octaves). `span` says how many beats to fill, so the figure repeats to cover the whole chord: ```{testcode} ch7 import subsequence from subsequence import progression import subsequence.constants.durations as dur composition = subsequence.Composition(bpm=120, key="A", scale="minor") @composition.pattern(channel=4, beats=16) def arp(p): for chord, start, length in p.progression([1, 6, 4, 5], harmonic_rhythm=dur.WHOLE): # root, third, fifth, third — a classic up-and-back arpeggio. p.broken_chord(chord, root=60, order=[0, 1, 2, 1], spacing=dur.EIGHTH, beat=start, span=length, velocity=70) composition.render(bars=4, filename="broken.mid") ``` Putting it together: the running drum loop, a root bassline, a strummed chord part, and an arpeggio — all four reading the same bound progression — is the full chord-following arrangement this chapter has been building toward. Each part chooses *how* to voice the harmony; the progression value decides *what* the harmony is. ```{tip} Both verbs accept a `(low, high)` velocity tuple for per-note humanising (each strum note or arp note gets its own dynamic, like the hi-hat trick in [§1.5](01-step-grid)). `p.strum` additionally has a `detached=` option that guarantees the figure clears before the next chord — handy on a synth with limited polyphony; `p.broken_chord` instead shapes its release with `duration` and `span`. ``` One last thing a progression *value* lets you do is build longer harmony out of shorter pieces: `+` joins two progressions end to end, and `*` repeats one. The chords are decided once, as values, before anything plays: ```{testcode} ch7 intro = progression(["Am", "F"]) chorus = progression(["C", "G"]) song = intro + chorus # Am F C G — concatenation vamp = intro * 4 # Am F Am F Am F Am F — repetition print(song.describe()) ``` ```{testoutput} ch7 Progression — 4 chords over 16 beats 0.00 … 4.00 Am (4 beats) 4.00 … 8.00 F (4 beats) 8.00 … 12.00 C (4 beats) 12.00 … 16.00 G (4 beats) ``` ```{note} Under the hood — sounding values and governing values Subsequence sorts its musical values into two families, and the difference explains both the `+`/`*` you just used and a rule you'll meet if you try the wrong operator. A **`Progression` is a governing value** — it describes *context*, the harmony that other parts sound *against*. The Motifs and Phrases of later chapters are **sounding values** — they describe actual notes. Both families compose the same way: sequentially with `+` and by repetition with `*`. But only *sounding* values can be merged in **parallel** with `&` (two melodies at once). Try to merge two progressions in parallel — `intro & chorus` — and you get a deliberate error: > *Progressions cannot be merged with `&` — there is one current chord. Sequence > them with `+`, or give a pattern its own part-level progression.* That's not a missing feature — it's the musical fact "there is only ever **one** chord sounding at a time" written into the type system. Harmony is a single context, not a stack of parallel parts. If you genuinely want two harmonic worlds at once (polytonality), you give each part its *own* `p.progression(...)` — the part-level seam from [§7.6](#sec-placing), which runs outside the one global chord. The rule, in a sentence: **sounding values stack; the governing chord does not.** ``` ```{admonition} Reference :class: seealso {py:meth}`~subsequence.pattern_builder.PatternBuilder.strum`, {py:meth}`~subsequence.pattern_builder.PatternBuilder.broken_chord` ``` --- You can now treat harmony as material you write down and shape: build a progression from degrees, romans, or names; inspect it with `print`; voice it, decorate it, and cap it with a cadence; and place it three ways — chord by chord, as a one-call part, or as the engine's global harmony — played as blocks, strums, or arpeggios. Next we do the same for *melody*: naming a short figure as a reusable Motif and developing it, so a whole part can grow from one idea.