Wrap the settings a piece is generated under.
The shared key, meter, tempo, and context; copied, never retained.
The resolved context, for handing back to the function API.
A copy of the settings this composer holds: the seed, the resolved algorithm version and the rest of the recipe, so a piece can be written again from what a project file stored.
The source a composer was handed is left out, being a live handle rather than plain data; a composer built with one keeps it across Composer.with and its siblings.
StaticofStart from a set of settings.
The shared key, meter, tempo, and context.
The composer.
StaticfromRebuild a composer from the plain data Composer.data hands out.
StaticfromRebuild a composer from its Composer.toJSON output.
The same settings with some of them replaced.
A chord progression, in time.
The chords are laid out one per bar of the composer's own meter, so a chord
change falls on a bar line of the piece and the timeline spans bars of it:
four beats each in 4/4, three in 3/4, and three in 6/8. Every other part is
written against that grid, and harmony that is not on it drifts further from
the bar line with every repeat.
Everything the progression generator takes but the key, the meter and the context, which are the composer's.
The chords over the beats they sound for.
If the composer names no key: the chords are degrees of one, and there is no melody here to read a key from the way Composer.harmonize does.
A drum part.
A drum onset is a note event carrying a General MIDI pitch, so the hits are a score like any other and read against the composer's meter and tempo. It carries no key: a kit is not in one, and naming a key here would invite the pitched analyses to read the kit map as melody.
Everything the drum generator takes but the meter and the context, which are the composer's.
The hits, as a score.
A bass line under a harmony.
The harmony to follow: a timeline, or the chord segments one is made of.
Optionalopts: BassLineSettings
Everything the bass generator takes but the segments, the key, the meter and the context; see BassLineSettings.
The line, as a score in the composer's key.
A second line against a melody.
Only the melody's notes cross over; the meter, the tempo and the key the line is written against are the composer's, so a counter line and the part it answers are read in one context.
The harmony crosses over the way Composer.bass takes it: a Timeline or the plain chord timeline, so a caller holding the class does not have to unwrap it for one of the two methods.
The lead line to write against.
Optionalopts: CounterMelodySettings
Everything the counter-melody generator takes but the melody, the key, the meter and the context; see CounterMelodySettings.
The counter line, as a score in the composer's key.
If the composer names no key: the line is written in one, and the melody it answers is not read for a key here — Composer.harmonize is the member that does that.
Chords under a melody, with the melody the chords read.
A composer that names a key harmonizes in it; one that names none has the key estimated from the melody, which is what the harmonizer does when it is asked for chords and given nothing else.
The melody to harmonize.
Optionalopts: Omit<HarmonizeOptions, "ts" | "key" | "melody" | "ctx">
Everything the harmonizer takes but the melody, the key, the meter and the context.
The chords, the melody as they read it, and the distance between that melody and the one handed in.
Whether another composer holds the same settings.
The comparison is made through the other composer's public data, so two composers built by different copies of the module still compare. Both sides are projected the same way: comparing the settings held here against the data the other hands out answered "different" for a composer holding a source and "same" for two that draw from different ones.
A source is compared by identity, being a handle on a stream rather than a setting: two composers drawing from different sources write different parts, and the numbers a source will hand out cannot be read from it.
The composer to compare.
True when both would generate the same parts.
The plain form of the settings, for JSON.stringify.
The settings a piece is generated under, held in one place and handed to every part.
The generation context — seed, tempo, complexity dials, instruments, vocabulary — was already designed to be shared across the parts of one piece, and
resolveContext(...).part(name)already gives each part its own stream from a single seed. What was missing was something to hold the shared half, so callers wrotekey,ts, andctx: { seed }again at every call. AComposeris that holder: set it once, and each generator inherits it.Every part a composer writes is scored against the composer's own meter and tempo, and the pitched ones in its key, so the scores it hands back line up with each other without being re-contextualized one at a time.
A composer that names no key writes no pitched part: Composer.bass, Composer.progression and Composer.counterMelody refuse rather than fall back on a key of the library's choosing, which would be a piece in a key nobody asked for with nothing to say so. Composer.drums carries no key and is written either way, and Composer.harmonize reads a key off the melody it is given;
with({ key })carries that key over to the parts written after it.Immutable like every other class here:
withSeedand friends return a new composer rather than reconfiguring this one, so a variation can be written beside the original instead of replacing it.Example