@libraz/libcantus - v1.1.0
    Preparing search index...

    Class Composer

    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 wrote key, ts, and ctx: { seed } again at every call. A Composer is 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: withSeed and friends return a new composer rather than reconfiguring this one, so a variation can be written beside the original instead of replacing it.

    import { Composer } from '@libraz/libcantus';
    const composer = Composer.of({ key: 'C major', bpm: 96, seed: 7 });
    composer.progression({ style: 'dance', bars: 4 }).totalBeats; // 16
    composer.withSeed(8).data.seed; // 8
    Index
    • Wrap the settings a piece is generated under.

      Parameters

      • options: ComposerOptions

        The shared key, meter, tempo, and context; copied, never retained.

      Returns Composer

      If the key names no key, the meter map is malformed, or the seed, tempo, dials, instruments or vocabulary carry a value the generators cannot hold.

    • get data(): ComposerOptions

      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.

      Returns ComposerOptions

    • 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.

      Parameters

      • opts: Omit<ProgressionOptions, "key" | "ctx" | "ts">

        Everything the progression generator takes but the key, the meter and the context, which are the composer's.

      Returns Timeline

      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.

      If the composer's meter changes: one chord per bar has no single bar length to be laid out on, and the chords would leave the bar lines at the first change rather than follow them.

    • 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.

      Parameters

      • opts: Omit<DrumsOptions, "ts" | "ctx">

        Everything the drum generator takes but the meter and the context, which are the composer's.

      Returns Score

      The hits, as a score.

      If the composer's meter is not 4/4 throughout, which is the only meter the drum patterns are written against: a piece that opens in 4/4 and changes later has bars the patterns cannot be laid out on, so the request is refused rather than answered with 4/4 bars over the change.

    • A bass line under a harmony.

      Parameters

      • source: readonly ChordSegment[] | Timeline

        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.

      Returns Score

      The line, as a score in the composer's key.

      If the composer names no key: the line is written in one, and a default key would put the part in a key the caller never asked for.

      If the instrument names a kit, which has no strings for a bass line to be placed on.

      import { Composer, Instrument } from '@libraz/libcantus';
      const composer = Composer.of({ key: 'C major', seed: 3 });
      const plan = composer.progression({ style: 'dance', bars: 2 });
      composer.bass(plan, { style: 'pop', instrument: Instrument.bass4() }).notes.length > 0; // true
    • 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.

      Parameters

      Returns Score

      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.

      Parameters

      • melody: Score

        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.

      Returns HarmonizedMelody

      The chords, the melody as they read it, and the distance between that melody and the one handed in.

      import { Composer, Score } from '@libraz/libcantus';
      const composer = Composer.of({ key: 'C major' });
      const melody = Score.of([{ pitch: 60, startBeat: 0, durationBeat: 4 }]);
      composer.harmonize(melody).transposeSemitones; // 0
    • 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.

      Parameters

      • other: Composer

        The composer to compare.

      Returns boolean

      True when both would generate the same parts.