Song Structure
Flow provides section declarations and Song expressions to organize music into named parts with an arrangement order.
Sections
A section is a named block of musical code containing one or more sequences:
section intro {
Sequence melody = | C4 E4 G4 C5 |
}Open in playgroundMultiple Sequences
Sections can contain multiple sequences (e.g., a lead melody and a bass line):
section verse {
Sequence lead = | E4 E4 F4 G4 |
Sequence bass = | C3 C3 G3 G3 |
}Open in playgroundEmpty Sections
Sections can be empty (useful for rests or structural placeholders):
section bridge {
}Open in playgroundSections with Musical Context
Sections inherit the musical context they’re declared in:
key Cmajor {
section outro {
Sequence melody = | I IV V I |
}
}Open in playgroundSong Expressions
A Song arranges sections in order using bracket syntax:
section intro { Sequence mel = | C4 E4 G4 C5 | }
section verse { Sequence mel = | E4 E4 F4 G4 | }
section chorus { Sequence mel = | G4 A4 G4 E4 | }
Song mySong = [intro verse chorus]
(print (str mySong))Open in playgroundRepeat Counts
Use *N to repeat a section:
Song mySong = [intro verse*2 chorus verse chorus*2]Open in playgroundThis plays: intro, verse, verse, chorus, verse, chorus, chorus.
Parameterized Sections
Sections can declare typed parameters with optional default values, so the same musical idea can be reused with different roots, transpositions, or repeat counts. Parameters use the full pattern syntax (typed bindings, tuple destructure, music-aware extractors):
section verse(Note root, Int repeats = 2) {
Sequence mel = | root D4 E4 F4 |
}
section chorus(Chord on = Cmaj7) {
Sequence bigChord = | on |
}Open in playgroundCall them from a Song expression with either positional or named arguments — defaults are honored for any trailing argument that isn’t supplied:
Song s = [verse(C4) Note: uses default repeats=2
verse(D4, 4) Note: positional override
verse(root=E4, repeats=1) Note: named-arg form
chorus] Note: uses default on=Cmaj7Open in playgroundCombine with the *N repetition operator — it works on both zero-arg and parameterized calls:
Song looped = [verse(C4)*3 chorus*2]Open in playgroundSection Overloading
Multiple section verse(...) declarations with different signatures coexist — the overload resolver picks the highest-specificity match at call time:
section verse(Note root) {
Sequence mel = | root D4 E4 F4 |
}
section verse(Note root, Int reps) {
Sequence mel = | root D4 E4 F4 |
Sequence echo = | (root) |
}
Song s = [verse(C4) verse(C4, 3)] Note: dispatches to each overloadOpen in playgroundPer-call args bind in a fresh stack frame that inherits the call-site’s musical context, so a verse(C4) call inside a key Aminor { ... } block sees Aminor as its active key.
Song Functions
getSections
Get the section names from a song:
use "@std"
Strings sections = (getSections mySong)
(print (str sections)) Note: ["intro", "verse", "chorus"]Open in playgroundsectionSequences
Get the sequence names within a section:
use "@std"
section mySection {
Sequence lead = | C4 D4 E4 F4 |
Sequence bass = | C3 G3 C3 G3 |
}
Strings seqs = (sectionSequences mySection)
(print (str seqs))Open in playgroundstr
Convert a song or section to its string representation:
use "@std"
(print (str mySong))Open in playgroundRendering Songs
Use renderSong to convert a Song to an audio Buffer:
use "@std"
use "@audio"
tempo 120 {
timesig 4/4 {
key Cmajor {
section intro {
Sequence melody = | C4 E4 G4 C5 |
}
section chorus {
Sequence melody = | I IV V I |
}
Song song = [intro chorus]
Buffer buf = (renderSong song "piano")
(exportWav buf "song.wav")
}
}
}Open in playgroundAvailable Instruments
The second argument to renderSong selects the synthesizer:
| Name | Aliases | Character |
|---|---|---|
| Piano | "piano" | Percussive attack with warm decay |
| Brass | "brass", "horn" | Bold, sustained tone |
| Saxophone | "sax", "saxophone" | Reed-like character |
| Flute | "flute" | Pure, breathy tone |
| Organ | "organ" | Sustained, multi-partial timbre |
| Strings | "strings" | Smooth bowed-instrument-like timbre |
| Bell | "bell" | Inharmonic bell / chime character |
| Drums | "drums", "drum" | Percussive, pitched drums |
| Sine | "sine" | Clean sine wave |
Custom wavetables registered via oscillator(name, ...) also work here. You can also pass a Flow Function as a custom instrument — see Audio and Synthesis.
Complete Example
use "@std"
use "@audio"
tempo 120 {
timesig 4/4 {
key Cmajor {
section intro {
Sequence melody = | C4 E4 G4 C5 |
}
section verse {
Sequence lead = | E4 E4 F4 G4 |
Sequence bass = | C3 C3 G3 G3 |
}
section chorus {
Sequence lead = | G4 A4 G4 E4 |
}
section bridge {
Sequence mel = | A4 G4 F4 E4 |
}
section outro {
Sequence melody = | I IV V I |
}
Song fullSong = [intro verse*2 chorus bridge outro]
Buffer rendered = (renderSong fullSong "piano")
Buffer final = rendered -> fadeIn 0.3 -> fadeOut 0.5
(exportWav final "full_song.wav")
(print "Song exported!")
}
}
}Open in playgroundWhen to Use Sections
- Organizing composition: Break music into logical parts (intro, verse, chorus, bridge, outro)
- Reuse: Reference the same section multiple times with
*Nrepeats - Clarity: Named sections make the arrangement readable at a glance
Exporting to MIDI
A Song can also be exported to a Standard MIDI File, preserving tempo, time signature, and key:
(writeMidi "my_song.mid" fullSong)Open in playgroundSee Playback and Export.
See Also
- Note Streams - Writing sequences within sections
- Musical Context - Context blocks around sections
- Audio and Synthesis - Rendering and synthesizers
- Voices and Tracks - Lower-level multi-track rendering
- Chord Progressions - Voice-led chord sequences
- Playback and Export - Playing and exporting songs