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

    Class Rhythm

    A pattern of onsets over a meter, and the transformations that reshape one.

    The library generates a rhythm as a bare array and deforms one with a family of functions — thinning, syncopating, halving and doubling the rate — that each take an array and hand one back. They are shaped to chain and cannot: every step has to name the pattern again, and the meter it is counted in travels beside it by hand. A Rhythm holds the two together, so the family reads as the chain it always was.

    The transformations are written against a sixteenth grid, so the onsets are converted to it and read back afterwards. A rhythm carries no dynamics, and the softer stroke a transform gives an added note is not part of what comes back.

    import { parseTimeSignature } from '@libraz/libcantus';
    import { Rhythm } from '@libraz/libcantus';
    const rhythm = Rhythm.generate(parseTimeSignature('4/4'), { ctx: { seed: 42 } });
    rhythm.syncopate(0.3).toScore(38).notes.length >= rhythm.events.length; // true
    Index
    • Wrap plain rhythm data.

      Parameters

      • data: RhythmData

        The onsets and the meter they are counted in; copied, never retained.

      Returns Rhythm

      If an onset or the meter carries a value a pattern cannot hold.

    • get totalBeats(): number

      Where the pattern stops: the furthest point any onset sounds to.

      Returns number

    • Generate a pattern over a meter.

      Parameters

      • ts: MeterLike

        The time signature the bars are counted in, in any form that names one; a meter map is read as the signature it opens in.

      • Optionalopts: RhythmOptions

        Length, grid resolution and the context; see RhythmOptions. The context's complexity.rhythmic is the onset density, and its seed fixes which grid slots are taken.

      Returns Rhythm

      The generated pattern.

      import { Rhythm } from '@libraz/libcantus';
      const rhythm = Rhythm.generate('4/4', {
      bars: 2,
      ctx: { seed: 7, complexity: { rhythmic: 0.6 } },
      });
      rhythm.totalBeats; // 8
    • Build a pattern from onsets that already exist.

      The meter is asked for rather than assumed. A pattern in 6/8 read as 4/4 answers every metric question — which beats are strong, where the bars fall, how dense it is — on the wrong pulse, and nothing downstream can tell that reading from a meter the caller meant. Its two siblings, Rhythm.generate and generateRhythm, ask for it the same way.

      Parameters

      • events: readonly RhythmEvent[]

        The onsets, in any order.

      • ts: MeterLike

        The meter they are counted in, in any form that names one.

      Returns Rhythm

      The pattern.

      If the meter names no signature, or an onset carries a value a pattern cannot hold.

    • How busy the pattern is: the mean number of onsets per bar.

      Returns number

      The onset count divided by the number of bars it spans.

    • Drop the onsets carrying the least of the metre.

      The metre is the pattern's own: the ranks are read from Rhythm.ts, so a pattern in 6/8 keeps its dotted-quarter pulses and one in 3/4 keeps its three-beat downbeats rather than the beats a four-beat bar would have.

      Parameters

      • amount: number

        How much to thin, in [0, 1]; at 0 nothing goes, and a full turn leaves the downbeats alone.

      Returns Rhythm

      The thinned pattern.

      If the amount is outside [0, 1].

    • Anticipate beats, one sixteenth early.

      The beats anticipated are the ones Rhythm.ts counts as main pulses or stronger, so a pattern is not syncopated against accents its own meter does not have. The anticipations are added rather than displaced, so raising the amount only ever adds onsets and the ones already sounding stay where they are.

      Parameters

      • amount: number

        How much syncopation, in [0, 1].

      • Optionalctx: GenerationContextInput

        The context, or the seed alone; it fixes which beats are anticipated.

      Returns Rhythm

      The syncopated pattern.

      If the amount is outside [0, 1].

    • Compress the pattern to half its length and play it twice.

      The span is the pattern's own, so it stays as full as it was.

      Returns Rhythm

      The compressed pattern.

    • Stretch the pattern to twice its length: everything falls half as often.

      Onsets stretched past the span are dropped, since the span is the span however the pattern is felt.

      Returns Rhythm

      The stretched pattern.

    • Keep as much decoration as the ornament dial asks for.

      Each decorated onset survives on its own draw, so raising the amount brings more of them back without disturbing the ones already sounding. Onsets the predicate does not claim are untouched.

      Parameters

      • isOrnament: (event: RhythmEvent) => boolean

        Which onsets count as decoration.

      • amount: number

        The ornament dial, in [0, 1].

      • Optionalctx: GenerationContextInput

        The context, or the seed alone; it fixes which decoration stays.

      Returns Rhythm

      The pattern with the surviving decoration.

      If the amount is outside [0, 1].

    • Apply the complexity dials to the pattern in one pass.

      The dials are read from the context, which is where they live for every generator: complexity.rhythmic below its neutral middle thins the pattern and above it syncopates, and complexity.ornament decides how much of what isOrnament claims survives. The span the rate is measured against is the pattern's own.

      Parameters

      Returns Rhythm

      The deformed pattern.

      If a dial is outside [0, 1].

    • Whether one player can sustain the pattern at the context's tempo.

      The ceiling is measured against the closest pair of onsets, which is what actually stops a hand. A context naming no tempo or no ceiling has nothing to measure against, and the pattern passes.

      Parameters

      • Optionalctx: GenerationContextInput

        The context, or the seed alone; its bpm and its complexity.difficulty are what the answer is read from.

      Returns boolean

      True when the pattern is inside the ceiling.

    • The pattern as sounding notes at one pitch, in its own meter.

      This is where a rhythm reaches the rest of the class API: the score it hands back can be humanized, grooved, analysed, or written out like any other line.

      Parameters

      • pitch: number

        The MIDI pitch to give every onset.

      • Optionalvelocity: number

        Velocity for every onset; a mezzo-forte 96 by default.

      Returns Score

      The score holding the pattern.

      If the pitch or the velocity is not a whole MIDI value in [0, 127]; both are the numbers a note event carries, and a fractional one is not a note anything downstream can sound.

      import { parseTimeSignature } from '@libraz/libcantus';
      import { Rhythm } from '@libraz/libcantus';
      const score = Rhythm.generate(parseTimeSignature('4/4')).toScore(38);
      score.notes.every((note) => note.pitch === 38); // true
    • Whether another pattern holds the same onsets in the same meter.

      The comparison is made through the other pattern's public data, so two patterns built by different copies of the module still compare.

      Parameters

      • other: Rhythm

        The pattern to compare.

      Returns boolean

      True when the onsets and the meter match.