@libraz/libcantus 1.0.1 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +27 -65
- package/README_ja.md +28 -66
- package/dist/analyze/index.cjs +11 -7
- package/dist/analyze/index.cjs.map +1 -1
- package/dist/analyze/index.d.cts +79 -144
- package/dist/analyze/index.d.ts +79 -144
- package/dist/analyze/index.js +20 -16
- package/dist/chunk-35NBV2FT.cjs +145 -0
- package/dist/chunk-35NBV2FT.cjs.map +1 -0
- package/dist/{chunk-UADYAYAT.cjs → chunk-3R3QVKY5.cjs} +1353 -580
- package/dist/chunk-3R3QVKY5.cjs.map +1 -0
- package/dist/{chunk-WWURE4XT.cjs → chunk-76ZBTRG2.cjs} +624 -729
- package/dist/chunk-76ZBTRG2.cjs.map +1 -0
- package/dist/{chunk-FVSI36UV.js → chunk-7KG3RTYS.js} +197 -110
- package/dist/chunk-7KG3RTYS.js.map +1 -0
- package/dist/{chunk-VTI6FOLB.js → chunk-AGOVW7U6.js} +709 -133
- package/dist/chunk-AGOVW7U6.js.map +1 -0
- package/dist/{chunk-E3F4AHAH.cjs → chunk-CGBADIO5.cjs} +221 -134
- package/dist/chunk-CGBADIO5.cjs.map +1 -0
- package/dist/{chunk-6XH6QZYQ.cjs → chunk-DSMJRXCZ.cjs} +1455 -849
- package/dist/chunk-DSMJRXCZ.cjs.map +1 -0
- package/dist/{chunk-PKFNUHDY.js → chunk-FBH75DAY.js} +1218 -612
- package/dist/chunk-FBH75DAY.js.map +1 -0
- package/dist/chunk-H2KJHBW4.js +3329 -0
- package/dist/chunk-H2KJHBW4.js.map +1 -0
- package/dist/chunk-HPVSH7SF.cjs +2294 -0
- package/dist/chunk-HPVSH7SF.cjs.map +1 -0
- package/dist/{chunk-MXO72PLY.cjs → chunk-HWHGOVO6.cjs} +21 -19
- package/dist/chunk-HWHGOVO6.cjs.map +1 -0
- package/dist/chunk-HYJJ2V2I.js +2294 -0
- package/dist/chunk-HYJJ2V2I.js.map +1 -0
- package/dist/{chunk-AFM2ULJH.cjs → chunk-J7FHVZII.cjs} +1567 -1067
- package/dist/chunk-J7FHVZII.cjs.map +1 -0
- package/dist/chunk-K6OZXIR6.js +1 -0
- package/dist/{chunk-H3VORETI.js → chunk-M5RYMAFI.js} +487 -592
- package/dist/chunk-M5RYMAFI.js.map +1 -0
- package/dist/chunk-NB25P3EH.js +363 -0
- package/dist/chunk-NB25P3EH.js.map +1 -0
- package/dist/{chunk-TTCLJX6D.js → chunk-NFJ4P6ZZ.js} +10 -8
- package/dist/chunk-NFJ4P6ZZ.js.map +1 -0
- package/dist/chunk-NM7UAMC3.cjs +457 -0
- package/dist/chunk-NM7UAMC3.cjs.map +1 -0
- package/dist/chunk-PM4XJE2Y.cjs +363 -0
- package/dist/chunk-PM4XJE2Y.cjs.map +1 -0
- package/dist/chunk-PUKJP2Z5.js +145 -0
- package/dist/chunk-PUKJP2Z5.js.map +1 -0
- package/dist/{chunk-VOHZB45S.js → chunk-Q6LFSAKV.js} +1321 -821
- package/dist/chunk-Q6LFSAKV.js.map +1 -0
- package/dist/{chunk-INHVXP6Z.js → chunk-RFIBF2RR.js} +1203 -430
- package/dist/chunk-RFIBF2RR.js.map +1 -0
- package/dist/{chunk-Q2WGKJNN.cjs → chunk-S5BKZZGM.cjs} +699 -123
- package/dist/chunk-S5BKZZGM.cjs.map +1 -0
- package/dist/chunk-SGLEQVJH.cjs +3329 -0
- package/dist/chunk-SGLEQVJH.cjs.map +1 -0
- package/dist/chunk-UGLFDG7L.cjs +1 -0
- package/dist/chunk-UGLFDG7L.cjs.map +1 -0
- package/dist/{chunk-YH3BIKHH.js → chunk-UMWZ4ZET.js} +146 -49
- package/dist/chunk-UMWZ4ZET.js.map +1 -0
- package/dist/{index-szBv3496.d.ts → coerce-DP0vYu-x.d.ts} +286 -199
- package/dist/{index-C_8o--VE.d.cts → coerce-DRERzdve.d.cts} +286 -199
- package/dist/core/index.cjs +9 -5
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +122 -108
- package/dist/core/index.d.ts +122 -108
- package/dist/core/index.js +32 -28
- package/dist/generate/index.cjs +8 -8
- package/dist/generate/index.d.cts +168 -84
- package/dist/generate/index.d.ts +168 -84
- package/dist/generate/index.js +7 -7
- package/dist/{index-CGpRv5sl.d.cts → index-9eoQs2C1.d.ts} +34 -19
- package/dist/{index-CCNJRG-P.d.cts → index-B9QLziDe.d.cts} +293 -56
- package/dist/{index-Dzr13q3d.d.cts → index-BBWNZS0f.d.cts} +135 -58
- package/dist/{index-9wcMhG73.d.ts → index-BHIUyjhZ.d.ts} +293 -56
- package/dist/{index-BGFW-8P2.d.cts → index-BJgP97N1.d.ts} +196 -114
- package/dist/{index-CunYKLiu.d.ts → index-BL5N2Oyz.d.cts} +1 -1
- package/dist/{index-Cj49F892.d.ts → index-BMweUlm5.d.cts} +63 -23
- package/dist/{index-CgHqxdps.d.ts → index-BNSM-DSk.d.cts} +196 -114
- package/dist/{index-D57QRbVn.d.ts → index-BRMbPkHE.d.ts} +480 -254
- package/dist/{index-Chbst3Jx.d.ts → index-CJBBiAmU.d.ts} +135 -58
- package/dist/{index-BkGmlyI3.d.cts → index-CL4b4gUQ.d.ts} +63 -23
- package/dist/{index-B9MNZboQ.d.ts → index-Cy15oTvh.d.cts} +52 -22
- package/dist/{index-B9MNZboQ.d.cts → index-Cy15oTvh.d.ts} +52 -22
- package/dist/{index-BO6xE9YU.d.cts → index-DGCNKAUr.d.cts} +480 -254
- package/dist/{index-B4o55NDr.d.cts → index-Dw2DP7vm.d.ts} +1 -1
- package/dist/{index-G1uwGGWQ.d.ts → index-cHvbYgJI.d.cts} +34 -19
- package/dist/index.cjs +42 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -14
- package/dist/index.d.ts +14 -14
- package/dist/index.js +82 -54
- package/dist/{masks-DYbE485J.d.cts → masks-Cc7-MzzY.d.ts} +49 -2
- package/dist/{masks-DYbE485J.d.ts → masks-pADfTOiD.d.cts} +49 -2
- package/dist/model/index.cjs +19 -12
- package/dist/model/index.cjs.map +1 -1
- package/dist/model/index.d.cts +504 -182
- package/dist/model/index.d.ts +504 -182
- package/dist/model/index.js +18 -11
- package/dist/{positional-Cptlj-xs.d.ts → positional-DJgs6ltD.d.ts} +85 -8
- package/dist/{positional-DvH_sd8u.d.cts → positional-DMZer-FM.d.cts} +85 -8
- package/dist/theory/index.cjs +35 -6
- package/dist/theory/index.cjs.map +1 -1
- package/dist/theory/index.d.cts +257 -62
- package/dist/theory/index.d.ts +257 -62
- package/dist/theory/index.js +41 -12
- package/package.json +7 -5
- package/dist/chunk-2LCPM3EG.js +0 -3224
- package/dist/chunk-2LCPM3EG.js.map +0 -1
- package/dist/chunk-6XH6QZYQ.cjs.map +0 -1
- package/dist/chunk-6XPTBE7Y.cjs +0 -49
- package/dist/chunk-6XPTBE7Y.cjs.map +0 -1
- package/dist/chunk-AFM2ULJH.cjs.map +0 -1
- package/dist/chunk-B6XNKWZV.cjs +0 -3224
- package/dist/chunk-B6XNKWZV.cjs.map +0 -1
- package/dist/chunk-E3F4AHAH.cjs.map +0 -1
- package/dist/chunk-ELCTZUJJ.js +0 -379
- package/dist/chunk-ELCTZUJJ.js.map +0 -1
- package/dist/chunk-FVSI36UV.js.map +0 -1
- package/dist/chunk-H3VORETI.js.map +0 -1
- package/dist/chunk-H42UHPYW.js +0 -1577
- package/dist/chunk-H42UHPYW.js.map +0 -1
- package/dist/chunk-HFPS7JFD.js +0 -1
- package/dist/chunk-INHVXP6Z.js.map +0 -1
- package/dist/chunk-K4ZJUGF4.cjs +0 -379
- package/dist/chunk-K4ZJUGF4.cjs.map +0 -1
- package/dist/chunk-KNJEKLKV.js +0 -49
- package/dist/chunk-KNJEKLKV.js.map +0 -1
- package/dist/chunk-MXO72PLY.cjs.map +0 -1
- package/dist/chunk-NLEU4QSE.cjs +0 -360
- package/dist/chunk-NLEU4QSE.cjs.map +0 -1
- package/dist/chunk-OE6WK2Y2.cjs +0 -1577
- package/dist/chunk-OE6WK2Y2.cjs.map +0 -1
- package/dist/chunk-PKFNUHDY.js.map +0 -1
- package/dist/chunk-Q2WGKJNN.cjs.map +0 -1
- package/dist/chunk-RHIJ3S4I.cjs +0 -1
- package/dist/chunk-RHIJ3S4I.cjs.map +0 -1
- package/dist/chunk-TTCLJX6D.js.map +0 -1
- package/dist/chunk-UADYAYAT.cjs.map +0 -1
- package/dist/chunk-VOHZB45S.js.map +0 -1
- package/dist/chunk-VTI6FOLB.js.map +0 -1
- package/dist/chunk-WWURE4XT.cjs.map +0 -1
- package/dist/chunk-YH3BIKHH.js.map +0 -1
- /package/dist/{chunk-HFPS7JFD.js.map → chunk-K6OZXIR6.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/instrument/articulation.ts","../src/core/instrument/profile.ts","../src/core/instrument/playability.ts","../src/core/instrument/profiles.ts","../src/core/random/internal.ts","../src/core/random/positional.ts","../src/core/random/seed.ts","../src/core/random/version.ts","../src/core/random/index.ts"],"sourcesContent":["/**\n * Every playing technique a note can carry, in declaration order.\n *\n * These name intent, not a rendering: how `flam` becomes a grace stroke or\n * `slide` becomes pitch bend is the caller's decision, because it depends on the\n * sampler, key switches, and controller layout in use. Carrying the intent\n * instead of a baked note pair is what lets any phrase be flammed, ghosted, or\n * muted without the generator having to know how it will be voiced.\n *\n * @category Core\n */\nexport const ARTICULATIONS = Object.freeze([\n 'accent',\n 'ghost',\n 'staccato',\n 'legato',\n 'slide',\n 'hammer',\n 'mute',\n 'flam',\n 'drag',\n 'roll',\n 'choke',\n 'open',\n] as const);\n\n/**\n * How a note is played, independent of its pitch and length.\n *\n * @category Core\n */\nexport type Articulation = (typeof ARTICULATIONS)[number];\n","import { InvalidInputError } from '../errors/index.js';\nimport type { Articulation } from './articulation.js';\n\n/**\n * Every limb a percussionist strikes with, in declaration order.\n *\n * @category Core\n */\nexport const LIMBS = Object.freeze(['rightHand', 'leftHand', 'rightFoot', 'leftFoot'] as const);\n\n/**\n * One limb of a percussionist.\n *\n * @category Core\n */\nexport type Limb = (typeof LIMBS)[number];\n\n/**\n * Fields every instrument profile carries, whatever its family.\n *\n * @category Core\n */\nexport type InstrumentProfileCommon = {\n /** Instrument name, for issue messages and for the caller's own display. */\n name: string;\n /** Techniques this instrument can produce; anything else it cannot play. */\n articulations: readonly Articulation[];\n /** Greatest number of notes that can sound at once. */\n polyphony: number;\n};\n\n/**\n * A fretted or fretless string instrument, described by what it is rather than\n * by the range it happens to have.\n *\n * The sounding range follows from `tuning` and `frets`: string `s` sounds\n * `tuning[s] .. tuning[s] + frets`. Storing `[min, max]` instead would make a\n * five-string bass, a drop tuning, a seven-string guitar and an extended-range\n * neck four unrelated special cases, and would leave \"the neck is not long\n * enough\" indistinguishable from \"the note is below the instrument\".\n *\n * @category Core\n */\nexport type StringedProfile = InstrumentProfileCommon & {\n kind: 'stringed';\n /** Open-string MIDI pitch per string, lowest-sounding string first. */\n tuning: readonly number[];\n /** Highest fret number; fret 0 is the open string. */\n frets: number;\n /** Greatest fret span one hand can hold down at once. */\n maxStretch: number;\n /**\n * The neck has no frets. Positions are continuous, so intonation is the\n * player's and a slide is unbroken; in twelve-tone equal temperament the\n * positions coincide with fret numbers, so range and stretch are read the\n * same way.\n */\n fretless?: boolean;\n};\n\n/**\n * A percussion setup, described by the voices within reach of each limb.\n *\n * A kit's constraint is not range but limbs: a voice no limb can reach is not\n * on the instrument, and two voices needing the same limb at the same instant\n * cannot both sound.\n *\n * @category Core\n */\nexport type PercussionProfile = InstrumentProfileCommon & {\n kind: 'percussion';\n /** The limbs the player has available. */\n limbs: readonly Limb[];\n /** Limbs that can strike each voice, in preference order, keyed by MIDI note. */\n reach: Readonly<Record<number, readonly Limb[]>>;\n};\n\n/**\n * An instrument the library can check a passage against.\n *\n * @category Core\n */\nexport type InstrumentProfile = StringedProfile | PercussionProfile;\n\n/**\n * Anything that names an instrument: a plain {@link InstrumentProfile}, or a\n * value that serializes to one such as the `Instrument` class.\n *\n * Every entry point that takes an instrument takes this, so a caller holding\n * the class hands it over as it is instead of unwrapping it for one call and\n * wrapping it again for the next.\n *\n * @category Core\n */\nexport type InstrumentProfileLike =\n | InstrumentProfile\n | {\n /** The profile data this value stands for. */\n toJSON(): InstrumentProfile;\n };\n\n/**\n * Resolve any instrument-shaped value to a plain {@link InstrumentProfile}.\n *\n * The counterpart of {@link toNoteData} for instruments: an entry point takes\n * whatever form the caller has — the profile a project file holds, one of the\n * built-in profiles, or an `Instrument` instance — and gets one shape back. An\n * instance is accepted through its `toJSON` method rather than by its type,\n * because the core layer cannot import the model layer that defines the class.\n *\n * @param value A plain profile, or a value whose `toJSON` returns one.\n * @param name What the instrument is called in an error message, so a caller\n * holding several of them hears which one was malformed.\n * @returns The validated profile.\n * @throws If the value names no instrument, or the instrument it names is\n * contradictory — a neck with no strings, a fret count that is no count, or a\n * kit no limb reaches.\n * @category Core\n */\nexport function toInstrumentProfile(\n value: InstrumentProfileLike,\n name = 'instrument',\n): InstrumentProfile {\n if (typeof value !== 'object' || value === null) {\n throw new InvalidInputError(`${name} must be an instrument profile; received ${typeof value}`);\n }\n const data =\n 'toJSON' in value && typeof value.toJSON === 'function'\n ? value.toJSON()\n : (value as InstrumentProfile);\n const kind: unknown = data?.kind;\n if (kind !== 'stringed' && kind !== 'percussion') {\n throw new InvalidInputError(\n `${name} must be a stringed or percussion profile; received kind ${String(kind)}`,\n );\n }\n assertProfile(data);\n return data;\n}\n\n/**\n * Resolve any instrument-shaped value to a plain {@link StringedProfile}.\n *\n * What {@link toInstrumentProfile} is for an entry point that reads any\n * instrument, this is for one that only has a neck to work with: a bass line\n * placed on strings and frets cannot be written for a kit, and saying so by\n * name is more use than a missing `tuning` surfacing later as a note the\n * generator could not place.\n *\n * @param value A plain profile, or a value whose `toJSON` returns one.\n * @param name What the instrument is called in an error message.\n * @returns The validated profile, narrowed to the stringed family.\n * @throws If the value names no instrument, or names one with no strings.\n * @category Core\n */\nexport function toStringedProfile(\n value: InstrumentProfileLike,\n name = 'instrument',\n): StringedProfile {\n const profile = toInstrumentProfile(value, name);\n if (profile.kind !== 'stringed') {\n throw new InvalidInputError(\n `${name} must be a stringed instrument; ${profile.name} has no strings`,\n );\n }\n return profile;\n}\n\n/**\n * A fretting-hand position: which string, and how far up the neck.\n *\n * @category Core\n */\nexport type StringFingering = {\n /** Index into the `tuning` of a {@link StringedProfile}. */\n string: number;\n /** Fret number; 0 is the open string. */\n fret: number;\n};\n\n/** Reject a profile whose own description is contradictory. */\nfunction assertProfile(profile: InstrumentProfile): void {\n if (profile.kind === 'stringed') {\n if (profile.tuning.length === 0) {\n throw new InvalidInputError(`${profile.name} must have at least one string`);\n }\n if (!Number.isInteger(profile.frets) || profile.frets < 0) {\n throw new InvalidInputError(`${profile.name} frets must be a non-negative integer`);\n }\n return;\n }\n if (Object.keys(profile.reach).length === 0) {\n throw new InvalidInputError(`${profile.name} must have at least one voice within reach`);\n }\n}\n\n/**\n * The limbs a kit can actually strike a voice with.\n *\n * A `reach` entry names the limbs that are placed to hit a voice; `limbs` names\n * the limbs the player has. A voice reachable only by a limb the player has not\n * got is not on the instrument, so the two are read together everywhere rather\n * than the reach alone.\n *\n * @param profile The kit.\n * @param pitch MIDI pitch, or `undefined` for a note that is not there.\n * @returns The available limbs in preference order; empty when none can strike.\n * @category Core\n */\nexport function reachOf(profile: PercussionProfile, pitch: number | undefined): readonly Limb[] {\n if (pitch === undefined) {\n return [];\n }\n return (profile.reach[pitch] ?? []).filter((limb) => profile.limbs.includes(limb));\n}\n\n/**\n * The lowest and highest pitch an instrument sounds.\n *\n * For a string instrument this is derived from the tuning and the fret count,\n * never stored; for a kit it is the extent of the voices the player's limbs\n * reach. The range is not necessarily gapless — use {@link canSound} to ask\n * about one pitch.\n *\n * @param profile The instrument.\n * @returns The extreme sounding pitches, inclusive.\n *\n * @example\n * ```ts\n * import { BASS_5_STRING, instrumentRange } from '@libraz/libcantus';\n * instrumentRange(BASS_5_STRING).low; // 23, low B\n * ```\n * @category Core\n */\nexport function instrumentRange(profile: InstrumentProfile): { low: number; high: number } {\n assertProfile(profile);\n if (profile.kind === 'stringed') {\n let low = Number.POSITIVE_INFINITY;\n let high = Number.NEGATIVE_INFINITY;\n for (const open of profile.tuning) {\n low = Math.min(low, open);\n high = Math.max(high, open + profile.frets);\n }\n return { low, high };\n }\n let low = Number.POSITIVE_INFINITY;\n let high = Number.NEGATIVE_INFINITY;\n for (const key of Object.keys(profile.reach)) {\n const pitch = Number(key);\n // A voice only an absent limb reaches is not on this kit, so it does not\n // stretch the range either — otherwise the range would promise a pitch\n // {@link canSound} denies.\n if (reachOf(profile, pitch).length === 0) {\n continue;\n }\n low = Math.min(low, pitch);\n high = Math.max(high, pitch);\n }\n return { low, high };\n}\n\n/**\n * Whether the instrument has this pitch at all.\n *\n * This is the first of the three playability layers: no amount of skill or\n * rehearsal puts a note on an instrument that has no position for it.\n *\n * @param profile The instrument.\n * @param pitch MIDI pitch.\n * @returns True when some string and fret, or some limb the player has,\n * produces it.\n * @category Core\n */\nexport function canSound(profile: InstrumentProfile, pitch: number): boolean {\n assertProfile(profile);\n if (profile.kind === 'stringed') {\n return profile.tuning.some((open) => pitch >= open && pitch - open <= profile.frets);\n }\n return reachOf(profile, pitch).length > 0;\n}\n\n/**\n * Every position on the neck that sounds a pitch, lowest string first.\n *\n * @param profile The instrument.\n * @param pitch MIDI pitch.\n * @returns One entry per string that reaches the pitch; empty when none does.\n * @category Core\n */\nexport function fingeringsFor(profile: StringedProfile, pitch: number): StringFingering[] {\n assertProfile(profile);\n const out: StringFingering[] = [];\n for (let string = 0; string < profile.tuning.length; string += 1) {\n const open = profile.tuning[string];\n if (open === undefined) {\n continue;\n }\n const fret = pitch - open;\n if (fret >= 0 && fret <= profile.frets) {\n out.push({ string, fret });\n }\n }\n return out;\n}\n\n/**\n * Move a pitch by whole octaves until the instrument can sound it.\n *\n * This is what a player does with a line written below the instrument: the note\n * comes back an octave up, keeping its pitch class and its place in the phrase.\n * Substituting a different scale degree, or refusing the passage outright, is\n * the wrong answer for a bass line.\n *\n * @param pitch MIDI pitch to place.\n * @param profile The instrument.\n * @returns The nearest octave transposition the instrument sounds, searching\n * upward first; the pitch unchanged when no transposition of it is available.\n *\n * @example\n * ```ts\n * import { BASS_4_STRING, foldIntoRange } from '@libraz/libcantus';\n * foldIntoRange(27, BASS_4_STRING); // 39 — Eb1 is under the low E\n * ```\n * @category Core\n */\nexport function foldIntoRange(pitch: number, profile: InstrumentProfile): number {\n if (canSound(profile, pitch)) {\n return pitch;\n }\n const { low, high } = instrumentRange(profile);\n for (let candidate = pitch + 12; candidate <= high; candidate += 12) {\n if (canSound(profile, candidate)) {\n return candidate;\n }\n }\n for (let candidate = pitch - 12; candidate >= low; candidate -= 12) {\n if (canSound(profile, candidate)) {\n return candidate;\n }\n }\n return pitch;\n}\n","import type { NoteEvent } from '../types.js';\nimport { assertNoteEvents, assertRange } from '../validation/index.js';\nimport {\n canSound,\n fingeringsFor,\n type InstrumentProfile,\n type Limb,\n reachOf,\n type StringFingering,\n} from './profile.js';\n\n/**\n * What makes a passage hard or impossible on an instrument.\n *\n * `noteOutOfRange` and `articulationUnavailable` belong to the first layer: the\n * instrument has no position for what was written. `stringConflict`,\n * `stretchTooWide`, `limbConflict` and `polyphonyExceeded` belong to the second:\n * the notes exist but the arrangement cannot be held or struck. `tooFast`\n * belongs to the third, the only one that depends on tempo.\n *\n * @category Core\n */\nexport type PlayabilityIssueType =\n | 'noteOutOfRange'\n | 'articulationUnavailable'\n | 'stringConflict'\n | 'stretchTooWide'\n | 'limbConflict'\n | 'polyphonyExceeded'\n | 'tooFast';\n\n/**\n * Which playability layer an issue belongs to: 1 the note does not exist, 2 the\n * arrangement does not hold together, 3 there is not enough time.\n *\n * @category Core\n */\nexport type PlayabilityLayer = 1 | 2 | 3;\n\n/**\n * One thing standing between a passage and a performance of it.\n *\n * @category Core\n */\nexport type PlayabilityIssue = {\n type: PlayabilityIssueType;\n layer: PlayabilityLayer;\n /** Indices into the analysed note array, ascending. */\n notes: readonly number[];\n /** Where the problem occurs, in quarter-note beats. */\n startBeat: number;\n /**\n * True when practice cannot fix it. Layers 1 and 2 depend on the instrument\n * alone, so they are impossible however skilled the player; only a layer-3\n * issue is a matter of degree.\n */\n impossible: boolean;\n message: string;\n};\n\n/**\n * How one note is actually produced: the string and fret it is stopped at, or\n * the limb that strikes it.\n *\n * This is reported for every note that has a position at all, whether or not\n * the passage raises issues, so a caller can apply its own standard rather than\n * take this module's.\n *\n * @category Core\n */\nexport type NotePlacement = {\n /** Index into the analysed note array. */\n note: number;\n /** String instruments: where the note is stopped. */\n fingering?: StringFingering;\n /** Percussion: which limb plays the stroke. */\n limb?: Limb;\n};\n\n/**\n * The verdict on a passage: how hard it is, what stands in the way, and how\n * each note would be produced.\n *\n * @category Core\n */\nexport type PlayabilityReport = {\n /**\n * Movement per unit time, mapped onto 1 (trivial) to 5 (at the limit).\n * Without a tempo the passage is measured at 120 BPM, so the number stays\n * comparable between passages; with one it rises as the tempo does.\n */\n difficulty: number;\n issues: PlayabilityIssue[];\n placements: NotePlacement[];\n};\n\n/** Tempo the difficulty is measured at when the caller gives none. */\nconst REFERENCE_BPM = 120;\n/** Fastest the fretting hand shifts along the neck, in frets per second. */\nconst MAX_FRET_SHIFT_PER_SECOND = 100;\n/** Shortest gap between two strokes of one limb, in seconds. */\nconst MIN_LIMB_STROKE_SECONDS = 0.06;\n/** Movement rate, in units per second, that reads as a demanding passage. */\nconst DIFFICULTY_SCALE = 12;\nconst MIN_DIFFICULTY = 1;\nconst MAX_DIFFICULTY = 5;\n/** Cost of crossing one string, relative to moving one fret. */\nconst STRING_CROSSING_COST = 2;\n/** Onsets closer than this fraction of a beat count as simultaneous. */\nconst ONSET_GRID = 128;\n\nconst EPS = 1e-9;\n\n/** Working state shared by both instrument families. */\ntype Analysis = {\n notes: readonly NoteEvent[];\n /** Note indices in onset order, ties broken by pitch. */\n order: number[];\n placements: NotePlacement[];\n issues: PlayabilityIssue[];\n /** Accumulated movement, in frets or strokes. */\n movement: number;\n /** Seconds per quarter-note beat, or undefined when no tempo was given. */\n secondsPerBeat: number | undefined;\n};\n\n/**\n * Judge whether a passage can be played on an instrument, and how hard it is.\n *\n * Inspection is deliberately separate from generation: this answers \"can this\n * be played\" for a chart handed to a player or a practice tool, and never\n * rewrites or rejects anything. The three layers are reported together but stay\n * distinct — an out-of-range note or an impossible stretch is not the bottom of\n * the difficulty scale but a different axis, since no amount of skill puts a\n * note on an instrument that does not have it. Only the third layer consults\n * `bpm`, so omitting the tempo reports what the instrument alone decides.\n *\n * @param notes The passage, in any order. Drum hits qualify as note events.\n * @param profile The instrument to play it on.\n * @param bpm Tempo in quarter-note beats per minute; enables the third layer.\n * @returns Difficulty, the issues found, and each note's string/fret or limb.\n *\n * @example\n * ```ts\n * import { BASS_4_STRING, playability } from '@libraz/libcantus';\n * const report = playability([{ pitch: 27, startBeat: 0, durationBeat: 1 }], BASS_4_STRING);\n * report.issues[0]?.type; // 'noteOutOfRange'\n * ```\n * @category Core\n */\nexport function playability(\n notes: readonly NoteEvent[],\n profile: InstrumentProfile,\n bpm?: number,\n): PlayabilityReport {\n assertNoteEvents(notes, 'playability notes', { allowNonPositiveDuration: true });\n if (bpm !== undefined) {\n assertRange(bpm, Number.MIN_VALUE, 1000, 'playability bpm');\n }\n\n const order = notes\n .map((_, index) => index)\n .sort((a, b) => {\n const left = notes[a];\n const right = notes[b];\n if (!left || !right) {\n return a - b;\n }\n return left.startBeat - right.startBeat || left.pitch - right.pitch || a - b;\n });\n\n const state: Analysis = {\n notes,\n order,\n placements: notes.map((_, index) => ({ note: index })),\n issues: [],\n movement: 0,\n secondsPerBeat: bpm === undefined ? undefined : 60 / bpm,\n };\n\n checkExistence(state, profile);\n if (profile.kind === 'stringed') {\n placeOnNeck(state, profile);\n } else {\n placeOnKit(state, profile);\n }\n\n return {\n difficulty: difficultyOf(state, bpm),\n issues: state.issues,\n placements: state.placements,\n };\n}\n\n/** Layer 1: the instrument has no position for the note, or no such technique. */\nfunction checkExistence(state: Analysis, profile: InstrumentProfile): void {\n for (const index of state.order) {\n const note = state.notes[index];\n if (!note) {\n continue;\n }\n if (!canSound(profile, note.pitch)) {\n state.issues.push({\n type: 'noteOutOfRange',\n layer: 1,\n notes: [index],\n startBeat: note.startBeat,\n impossible: true,\n message: `${profile.name} cannot sound MIDI ${note.pitch}`,\n });\n }\n const articulation = note.articulation;\n if (articulation !== undefined && !profile.articulations.includes(articulation)) {\n state.issues.push({\n type: 'articulationUnavailable',\n layer: 1,\n notes: [index],\n startBeat: note.startBeat,\n impossible: true,\n message: `${profile.name} cannot play ${articulation}`,\n });\n }\n }\n}\n\n/** Note indices sounding at each distinct onset, in onset order. */\nfunction soundingGroups(state: Analysis): { startBeat: number; members: number[] }[] {\n const groups: { startBeat: number; members: number[] }[] = [];\n let active: number[] = [];\n let cursor = 0;\n let previousKey = Number.NaN;\n for (const index of state.order) {\n const note = state.notes[index];\n if (!note) {\n continue;\n }\n const key = Math.round(note.startBeat * ONSET_GRID);\n if (key === previousKey) {\n continue;\n }\n previousKey = key;\n const at = note.startBeat;\n active = active.filter((member) => {\n const held = state.notes[member];\n return held !== undefined && held.startBeat + held.durationBeat > at + EPS;\n });\n while (cursor < state.order.length) {\n const candidateIndex = state.order[cursor];\n const candidate = candidateIndex === undefined ? undefined : state.notes[candidateIndex];\n if (!candidate || candidate.startBeat > at + EPS) {\n break;\n }\n if (candidateIndex !== undefined && candidate.startBeat + candidate.durationBeat > at + EPS) {\n active.push(candidateIndex);\n }\n cursor += 1;\n }\n groups.push({ startBeat: at, members: [...active] });\n }\n return groups;\n}\n\n/** Layers 2 and 3 for a string instrument, and the fingering that goes with them. */\nfunction placeOnNeck(state: Analysis, profile: InstrumentProfile & { kind: 'stringed' }): void {\n /** Beat each string becomes free again. */\n const stringFreeAt = profile.tuning.map(() => Number.NEGATIVE_INFINITY);\n /** Note currently holding each string. */\n const stringHeldBy = profile.tuning.map<number | undefined>(() => undefined);\n let previous: StringFingering | undefined;\n let previousIndex: number | undefined;\n\n for (const index of state.order) {\n const note = state.notes[index];\n const placement = state.placements[index];\n if (!note || !placement) {\n continue;\n }\n const candidates = fingeringsFor(profile, note.pitch);\n if (candidates.length === 0) {\n continue;\n }\n const free = candidates.filter(\n (candidate) => (stringFreeAt[candidate.string] ?? 0) <= note.startBeat + EPS,\n );\n if (free.length === 0) {\n const blocked = candidates\n .map((candidate) => stringHeldBy[candidate.string])\n .filter((held): held is number => held !== undefined);\n state.issues.push({\n type: 'stringConflict',\n layer: 2,\n notes: [...new Set([...blocked, index])].sort((a, b) => a - b),\n startBeat: note.startBeat,\n impossible: true,\n message: `MIDI ${note.pitch} needs a string already sounding on ${profile.name}`,\n });\n }\n const pool = free.length > 0 ? free : candidates;\n const chosen = pool.reduce((best, candidate) =>\n fingeringCost(candidate, previous) < fingeringCost(best, previous) ? candidate : best,\n );\n placement.fingering = chosen;\n stringFreeAt[chosen.string] = note.startBeat + note.durationBeat;\n stringHeldBy[chosen.string] = index;\n\n state.movement += 1;\n if (previous) {\n state.movement +=\n Math.abs(chosen.fret - previous.fret) + Math.abs(chosen.string - previous.string);\n checkShiftSpeed(state, profile, previousIndex, index, previous, chosen);\n }\n previous = chosen;\n previousIndex = index;\n }\n\n for (const group of soundingGroups(state)) {\n checkStretch(state, profile, group);\n checkPolyphony(state, profile, group);\n }\n}\n\n/** Cost of taking a position, counting a string crossing as several frets. */\nfunction fingeringCost(candidate: StringFingering, previous: StringFingering | undefined): number {\n if (!previous) {\n // With nothing to move from, the low frets are the resting position.\n return candidate.fret + candidate.string / 100;\n }\n return (\n Math.abs(candidate.fret - previous.fret) +\n STRING_CROSSING_COST * Math.abs(candidate.string - previous.string) +\n candidate.string / 100\n );\n}\n\n/** Layer 3: the fretting hand cannot travel that far in the time available. */\nfunction checkShiftSpeed(\n state: Analysis,\n profile: InstrumentProfile,\n fromIndex: number | undefined,\n toIndex: number,\n from: StringFingering,\n to: StringFingering,\n): void {\n const secondsPerBeat = state.secondsPerBeat;\n if (secondsPerBeat === undefined || fromIndex === undefined) {\n return;\n }\n const previousNote = state.notes[fromIndex];\n const note = state.notes[toIndex];\n if (!previousNote || !note) {\n return;\n }\n const seconds = (note.startBeat - previousNote.startBeat) * secondsPerBeat;\n const shift = Math.abs(to.fret - from.fret);\n if (seconds <= EPS || shift === 0) {\n return;\n }\n if (shift / seconds > MAX_FRET_SHIFT_PER_SECOND) {\n state.issues.push({\n type: 'tooFast',\n layer: 3,\n notes: [fromIndex, toIndex].sort((a, b) => a - b),\n startBeat: note.startBeat,\n impossible: false,\n message: `a ${shift}-fret shift in ${seconds.toFixed(3)}s is beyond ${profile.name}`,\n });\n }\n}\n\n/** Layer 2: one hand cannot span that many frets at once. */\nfunction checkStretch(\n state: Analysis,\n profile: InstrumentProfile & { kind: 'stringed' },\n group: { startBeat: number; members: number[] },\n): void {\n const frets = group.members\n .map((member) => state.placements[member]?.fingering)\n .filter(\n (fingering): fingering is StringFingering => fingering !== undefined && fingering.fret > 0,\n )\n .map((fingering) => fingering.fret);\n if (frets.length < 2) {\n return;\n }\n const span = Math.max(...frets) - Math.min(...frets);\n if (span > profile.maxStretch) {\n state.issues.push({\n type: 'stretchTooWide',\n layer: 2,\n notes: [...group.members].sort((a, b) => a - b),\n startBeat: group.startBeat,\n impossible: true,\n message: `a ${span}-fret stretch exceeds the ${profile.maxStretch} frets one hand spans on ${profile.name}`,\n });\n }\n}\n\n/** Layer 2: more notes sounding at once than the instrument has voices. */\nfunction checkPolyphony(\n state: Analysis,\n profile: InstrumentProfile,\n group: { startBeat: number; members: number[] },\n): void {\n if (group.members.length <= profile.polyphony) {\n return;\n }\n state.issues.push({\n type: 'polyphonyExceeded',\n layer: 2,\n notes: [...group.members].sort((a, b) => a - b),\n startBeat: group.startBeat,\n impossible: true,\n message: `${group.members.length} notes sound at once; ${profile.name} has ${profile.polyphony}`,\n });\n}\n\n/** Layers 2 and 3 for a kit, and the limb assignment that goes with them. */\nfunction placeOnKit(state: Analysis, profile: InstrumentProfile & { kind: 'percussion' }): void {\n /** Limb that last struck each voice, so a voice keeps its stick. */\n const limbForVoice = new Map<number, Limb>();\n /** Last stroke of each limb, as a note index. */\n const lastStroke = new Map<Limb, number>();\n\n for (const group of onsetGroups(state)) {\n const taken = new Set<Limb>();\n let conflicted = false;\n // The most constrained voice picks first: a kick only the right foot\n // reaches must not lose it to a snare that either hand could have taken.\n const members = [...group.members].sort((a, b) => {\n const left = reachOf(profile, state.notes[a]?.pitch);\n const right = reachOf(profile, state.notes[b]?.pitch);\n return left.length - right.length || a - b;\n });\n for (const index of members) {\n const note = state.notes[index];\n const placement = state.placements[index];\n if (!note || !placement) {\n continue;\n }\n const candidates = reachOf(profile, note.pitch);\n if (candidates.length === 0) {\n continue;\n }\n const preferred = limbForVoice.get(note.pitch);\n const free = candidates.filter((limb) => !taken.has(limb));\n const chosen =\n free.length === 0\n ? candidates[0]\n : preferred !== undefined && free.includes(preferred)\n ? preferred\n : free[0];\n if (free.length === 0) {\n conflicted = true;\n }\n if (chosen === undefined) {\n continue;\n }\n taken.add(chosen);\n placement.limb = chosen;\n limbForVoice.set(note.pitch, chosen);\n\n state.movement += 1;\n const previousIndex = lastStroke.get(chosen);\n if (previousIndex !== undefined) {\n const previousNote = state.notes[previousIndex];\n if (previousNote && previousNote.pitch !== note.pitch) {\n // The limb had to travel across the kit rather than repeat a voice.\n state.movement += 1;\n }\n checkStrokeSpeed(state, profile, previousIndex, index, chosen);\n }\n lastStroke.set(chosen, index);\n }\n if (conflicted) {\n state.issues.push({\n type: 'limbConflict',\n layer: 2,\n notes: [...group.members].sort((a, b) => a - b),\n startBeat: group.startBeat,\n impossible: true,\n message: `${group.members.length} simultaneous strokes cannot be shared among the limbs of ${profile.name}`,\n });\n } else {\n checkPolyphony(state, profile, group);\n }\n }\n}\n\n/** Layer 3: one limb cannot strike twice that close together. */\nfunction checkStrokeSpeed(\n state: Analysis,\n profile: InstrumentProfile,\n fromIndex: number,\n toIndex: number,\n limb: Limb,\n): void {\n const secondsPerBeat = state.secondsPerBeat;\n if (secondsPerBeat === undefined) {\n return;\n }\n const previousNote = state.notes[fromIndex];\n const note = state.notes[toIndex];\n if (!previousNote || !note) {\n return;\n }\n const seconds = (note.startBeat - previousNote.startBeat) * secondsPerBeat;\n if (seconds <= EPS || seconds >= MIN_LIMB_STROKE_SECONDS) {\n return;\n }\n state.issues.push({\n type: 'tooFast',\n layer: 3,\n notes: [fromIndex, toIndex].sort((a, b) => a - b),\n startBeat: note.startBeat,\n impossible: false,\n message: `${limb} strikes twice ${seconds.toFixed(3)}s apart on ${profile.name}`,\n });\n}\n\n/** Note indices sharing each distinct onset, in onset order. */\nfunction onsetGroups(state: Analysis): { startBeat: number; members: number[] }[] {\n const groups: { startBeat: number; members: number[] }[] = [];\n let key = Number.NaN;\n let current: { startBeat: number; members: number[] } | undefined;\n for (const index of state.order) {\n const note = state.notes[index];\n if (!note) {\n continue;\n }\n const onsetKey = Math.round(note.startBeat * ONSET_GRID);\n if (!current || onsetKey !== key) {\n current = { startBeat: note.startBeat, members: [] };\n groups.push(current);\n key = onsetKey;\n }\n current.members.push(index);\n }\n return groups;\n}\n\n/**\n * Movement per unit time, on a 1..5 scale.\n *\n * The rate is what a passage demands of the player: strokes and shifts divided\n * by the seconds available for them. It is mapped through a saturating curve so\n * the scale has a fixed top a ceiling can be compared against — a faster tempo\n * over the same notes never reads easier, and 5 is reached only where the\n * passage has already left what the instrument allows.\n */\nfunction difficultyOf(state: Analysis, bpm: number | undefined): number {\n let first = Number.POSITIVE_INFINITY;\n let last = Number.NEGATIVE_INFINITY;\n for (const note of state.notes) {\n if (!note) {\n continue;\n }\n first = Math.min(first, note.startBeat);\n last = Math.max(last, note.startBeat + Math.max(0, note.durationBeat));\n }\n const spanBeats = last - first;\n if (!Number.isFinite(spanBeats) || spanBeats <= EPS) {\n return MIN_DIFFICULTY;\n }\n const seconds = (spanBeats * 60) / (bpm ?? REFERENCE_BPM);\n const rate = state.movement / seconds;\n const scaled =\n MIN_DIFFICULTY + (MAX_DIFFICULTY - MIN_DIFFICULTY) * (1 - Math.exp(-rate / DIFFICULTY_SCALE));\n return Math.round(scaled * 1e4) / 1e4;\n}\n","import type { Articulation } from './articulation.js';\nimport type { StringedProfile } from './profile.js';\n\n/** Techniques common to every fretted string instrument here. */\nconst STRINGED_ARTICULATIONS: readonly Articulation[] = Object.freeze([\n 'accent',\n 'ghost',\n 'staccato',\n 'legato',\n 'slide',\n 'hammer',\n 'mute',\n 'open',\n] as const);\n\n/**\n * Electric bass in standard tuning: E1 A1 D2 G2 over a 24-fret neck.\n *\n * @category Core\n */\nexport const BASS_4_STRING: StringedProfile = Object.freeze({\n kind: 'stringed',\n name: '4-string bass',\n tuning: Object.freeze([28, 33, 38, 43]),\n frets: 24,\n maxStretch: 4,\n articulations: STRINGED_ARTICULATIONS,\n polyphony: 4,\n});\n\n/**\n * Five-string bass with the low B: B0 E1 A1 D2 G2.\n *\n * @category Core\n */\nexport const BASS_5_STRING: StringedProfile = Object.freeze({\n kind: 'stringed',\n name: '5-string bass',\n tuning: Object.freeze([23, 28, 33, 38, 43]),\n frets: 24,\n maxStretch: 4,\n articulations: STRINGED_ARTICULATIONS,\n polyphony: 5,\n});\n\n/**\n * Six-string guitar in standard tuning: E2 A2 D3 G3 B3 E4.\n *\n * @category Core\n */\nexport const GUITAR_STANDARD: StringedProfile = Object.freeze({\n kind: 'stringed',\n name: 'guitar',\n tuning: Object.freeze([40, 45, 50, 55, 59, 64]),\n frets: 24,\n maxStretch: 5,\n articulations: STRINGED_ARTICULATIONS,\n polyphony: 6,\n});\n\n/**\n * Six-string guitar in drop D: the lowest string down a tone to D2, the rest\n * standard. Only the tuning differs, which is the whole point of deriving the\n * range from it.\n *\n * @category Core\n */\nexport const GUITAR_DROP_D: StringedProfile = Object.freeze({\n kind: 'stringed',\n name: 'guitar (drop D)',\n tuning: Object.freeze([38, 45, 50, 55, 59, 64]),\n frets: 24,\n maxStretch: 5,\n articulations: STRINGED_ARTICULATIONS,\n polyphony: 6,\n});\n","/**\n * The hash behind hierarchical seeds and position-addressed draws.\n *\n * Both public entry points reduce a root seed plus a path to one 32-bit word,\n * so the mixing lives here once. The construction is FNV-1a (32-bit) over a\n * type-tagged byte encoding of the path, finished with the MurmurHash3 fmix32\n * avalanche step: FNV-1a alone leaves neighbouring inputs — `'drums'` against\n * `'drum'`, bar 1 against bar 2 — differing in only a few bits, and fmix32\n * spreads a one-bit input difference across the whole word so sibling paths\n * behave as unrelated seeds.\n *\n * Nothing here reads the platform: only code units, IEEE 754 bytes and 32-bit\n * integer arithmetic, so the same path yields the same word in every runtime\n * and every locale.\n */\n\nimport { InvalidInputError } from '../errors/index.js';\nimport { assertFiniteNumber } from '../validation/index.js';\n\n/** Number of distinct values a 32-bit word takes; the divisor for a unit float. */\nexport const UINT32_RANGE = 4294967296;\n\n/** FNV-1a 32-bit offset basis. */\nconst FNV_OFFSET_BASIS = 0x811c9dc5;\n\n/** FNV-1a 32-bit prime. */\nconst FNV_PRIME = 0x01000193;\n\n/**\n * Keyspace a hash belongs to.\n *\n * Derived seeds and positional draws are asked for at the same paths, so they\n * are hashed under different tags; without that, `at(...path)` would be the\n * unit-scaled `deriveSeed(seed, ...path)` and a generator using both at one\n * position would draw the same number twice.\n */\nexport const DOMAIN_SEED = 0x01;\nexport const DOMAIN_POSITION = 0x02;\n\n/** Segment tags, so `'3'` and `3` never encode to the same bytes. */\nconst TAG_STRING = 0x01;\nconst TAG_NUMBER = 0x02;\n\n/**\n * Scratch view for the IEEE 754 encoding of a numeric segment.\n *\n * Written and read within a single synchronous call, so the reuse is not\n * observable; it keeps the hot path — one draw per bar, beat and voice — free\n * of per-call allocation.\n */\nconst numberBytes = new DataView(new ArrayBuffer(8));\n\n/** Absorb one byte into a running FNV-1a hash. */\nfunction absorb(hash: number, byte: number): number {\n return Math.imul(hash ^ (byte & 0xff), FNV_PRIME) >>> 0;\n}\n\n/** Absorb a 32-bit word, least significant byte first. */\nfunction absorbUint32(hash: number, value: number): number {\n let next = hash;\n for (let shift = 0; shift < 32; shift += 8) {\n next = absorb(next, (value >>> shift) & 0xff);\n }\n return next;\n}\n\n/**\n * MurmurHash3 32-bit finalizer.\n *\n * @param value The accumulated hash.\n * @returns The avalanched word.\n */\nfunction fmix32(value: number): number {\n let mixed = value >>> 0;\n mixed ^= mixed >>> 16;\n mixed = Math.imul(mixed, 0x85ebca6b);\n mixed ^= mixed >>> 13;\n mixed = Math.imul(mixed, 0xc2b2ae35);\n mixed ^= mixed >>> 16;\n return mixed >>> 0;\n}\n\n/**\n * Absorb one path segment, tagged and length-prefixed.\n *\n * The length prefix is what keeps `['ab', 'c']` apart from `['a', 'bc']`: a\n * plain separator byte could itself appear inside a segment.\n */\nfunction absorbSegment(hash: number, segment: string | number, index: number): number {\n if (typeof segment === 'string') {\n let next = absorb(hash, TAG_STRING);\n next = absorbUint32(next, segment.length);\n for (let i = 0; i < segment.length; i += 1) {\n // UTF-16 code units, low byte first: a fixed encoding of the string,\n // with no case folding or normalisation that a locale could steer.\n const unit = segment.charCodeAt(i);\n next = absorb(next, unit & 0xff);\n next = absorb(next, (unit >>> 8) & 0xff);\n }\n return next;\n }\n if (typeof segment === 'number') {\n // A path segment that is not a number at all would otherwise hash as one\n // fixed word (NaN) or two (the infinities), aliasing unrelated positions.\n assertFiniteNumber(segment, `seed path segment ${index}`);\n // Negative zero carries different bytes from zero while comparing equal,\n // which would make two paths a caller reads as identical derive apart.\n numberBytes.setFloat64(0, segment === 0 ? 0 : segment, true);\n let next = absorb(hash, TAG_NUMBER);\n for (let i = 0; i < 8; i += 1) {\n next = absorb(next, numberBytes.getUint8(i));\n }\n return next;\n }\n throw new InvalidInputError(\n `seed path segment ${index} must be a string or a number; received ${typeof segment}`,\n );\n}\n\n/**\n * Hash a root seed and a path to a 32-bit word within one keyspace.\n *\n * @param domain The keyspace tag.\n * @param root The validated root seed.\n * @param path The path segments.\n * @returns The mixed word in `[0, 0xffffffff]`.\n */\nexport function hashPath(domain: number, root: number, path: readonly (string | number)[]): number {\n let hash = absorb(FNV_OFFSET_BASIS, domain);\n hash = absorbUint32(hash, root);\n for (let index = 0; index < path.length; index += 1) {\n const segment = path[index];\n if (segment === undefined) {\n throw new InvalidInputError(\n `seed path segment ${index} must be a string or a number; received undefined`,\n );\n }\n hash = absorbSegment(hash, segment, index);\n }\n return fmix32(hash);\n}\n","/**\n * Position-addressed randomness and threshold sampling.\n *\n * Drawing in call order makes a generator's output depend on how many draws\n * preceded it, so editing a parameter in the middle of a piece redraws\n * everything after it. Addressing each draw by where it happens — bar, beat,\n * voice — removes that coupling: the value at a position depends on the\n * position alone, so a change anywhere leaves every other position as it was.\n *\n * Deciding inclusion by comparing a fixed positional draw against a complexity\n * dial gives the property a slider needs: raising the dial only adds events,\n * and everything already sounding stays where it was.\n */\n\nimport { InvalidInputError } from '../errors/index.js';\nimport { assertInteger, assertRange } from '../validation/index.js';\nimport { DOMAIN_POSITION, hashPath, UINT32_RANGE } from './internal.js';\nimport type { SeedPath } from './seed.js';\n\n/**\n * A random source addressed by position rather than by call order.\n *\n * @category Utilities\n */\nexport type PositionalRng = {\n /**\n * The draw belonging to a position: a float in [0, 1) that depends only on\n * the seed and the path, so it is the same however often and in whatever\n * order it is asked for.\n */\n at: (...path: SeedPath) => number;\n};\n\n/**\n * Create a position-addressed random source.\n *\n * `at` holds no cursor: it is a pure function of the seed and the path, safe to\n * call in any order, any number of times, from anywhere in a generator. Its\n * draws are independent of {@link deriveSeed} at the same path, so a generator\n * may seed a sub-part and take a positional draw at one position without\n * getting the same number twice.\n *\n * @param seed The seed; an integer in 0..0xffffffff, typically from\n * {@link deriveSeed}.\n * @returns The positional source.\n * @throws If the seed is not a 32-bit integer.\n * @example\n * ```ts\n * import { createPositionalRng, deriveSeed } from '@libraz/libcantus';\n * const rng = createPositionalRng(deriveSeed(42, 'drums'));\n * rng.at('ghost', 4, 2.5); // the same value however often it is asked for\n * ```\n * @category Utilities\n */\nexport function createPositionalRng(seed: number): PositionalRng {\n assertInteger(seed, 'seed', 0, 0xffffffff);\n return {\n at: (...path: SeedPath) => hashPath(DOMAIN_POSITION, seed, path) / UINT32_RANGE,\n };\n}\n\n/**\n * Whether the event at a position belongs in the output at a given complexity.\n *\n * The test is `at(...path) < complexity`, and because the draw is fixed by the\n * position, the selected set grows monotonically with complexity: for any\n * `c1 < c2` the events at `c1` are a subset of the events at `c2` over the same\n * seed and the same path space. Complexity 0 selects nothing and complexity 1\n * selects everything.\n *\n * @param rng The positional source; may be a caller-supplied one.\n * @param complexity The dial, in [0, 1].\n * @param path The position to test.\n * @returns True when the position is included.\n * @throws If the complexity is outside [0, 1], or the source returns a value\n * outside [0, 1).\n * @example\n * ```ts\n * import { createPositionalRng, includeAt } from '@libraz/libcantus';\n * const rng = createPositionalRng(7);\n * includeAt(rng, 0.3, 'ghost', 4, 2.5); // raising 0.3 only adds hits\n * ```\n * @category Utilities\n */\nexport function includeAt(rng: PositionalRng, complexity: number, ...path: SeedPath): boolean {\n assertRange(complexity, 0, 1, 'complexity');\n const draw = rng.at(...path);\n // A substituted source is part of the contract, and one that returns 1, a\n // negative value or NaN would quietly break monotonicity rather than fail.\n if (!(draw >= 0 && draw < 1)) {\n throw new InvalidInputError(`positional draw must be in [0, 1); received ${draw}`);\n }\n return draw < complexity;\n}\n","/**\n * Hierarchical seeds: one project number that addresses every part.\n *\n * A flat seed per generator makes the caller keep a table of numbers and,\n * worse, makes \"reroll the drums\" mean \"pick a number nobody else is using\".\n * Deriving instead — `deriveSeed(project, 'drums', 'chorus', 2)` — turns a\n * reroll into an extra path segment, and every other part keeps the seed it\n * had because its path did not change.\n */\n\nimport { assertInteger } from '../validation/index.js';\nimport { DOMAIN_SEED, hashPath } from './internal.js';\n\n/**\n * The address of one derivation: a namespace, and whatever names or indices\n * locate the thing being seeded.\n *\n * Strings and numbers are kept apart by the hash, so `'3'` and `3` address\n * different things, as do `['ab', 'c']` and `['a', 'bc']`.\n *\n * @category Utilities\n */\nexport type SeedPath = readonly (string | number)[];\n\n/**\n * Derive a child seed from a root seed and a path.\n *\n * The result is a 32-bit integer, so it can be passed straight to\n * {@link createRng}. Derivation is a pure function of its arguments — the same\n * root and path give the same seed in every runtime — and near-identical paths\n * (`'drums'` against `'drum'`, bar 1 against bar 2) give unrelated seeds: the\n * path is hashed with FNV-1a and finished with the MurmurHash3 fmix32\n * avalanche step.\n *\n * @param root The project seed; an integer in 0..0xffffffff.\n * @param path The address to derive at; string or finite number segments.\n * @returns The derived seed, an integer in 0..0xffffffff.\n * @throws If the root is not a 32-bit integer, or a segment is neither a\n * string nor a finite number.\n * @example\n * ```ts\n * import { createRng, deriveSeed } from '@libraz/libcantus';\n * const drums = deriveSeed(42, 'drums', 'chorus', 3);\n * const rng = createRng(drums); // rerolling only the drums: bump the 3\n * ```\n * @category Utilities\n */\nexport function deriveSeed(root: number, ...path: SeedPath): number {\n // The root is held to the seed range createRng accepts, so a derived seed is\n // always a legal root for a further derivation.\n assertInteger(root, 'root seed', 0, 0xffffffff);\n return hashPath(DOMAIN_SEED, root, path);\n}\n","/**\n * The reproducibility contract for generated output.\n *\n * A saved project is a seed plus parameters, and it only reopens as the same\n * piece if the library still turns them into the same notes. Package versions\n * cannot carry that promise: a bug fix inside a generator is a patch release\n * and still moves every note. The algorithm version is the separate number\n * that does carry it, declared here and recorded by the caller alongside the\n * seed.\n */\n\nimport { InvalidInputError } from '../errors/index.js';\nimport { assertInteger } from '../validation/index.js';\n\n/**\n * The generator algorithm version this build produces by default.\n *\n * The guarantee it carries: for a fixed algorithm version, the same inputs —\n * seed, version and every documented parameter — produce the same generated\n * output from any build of this library that accepts that version. It covers\n * what the generators return, and nothing else: analysis results, error\n * messages and output taken under a different version are outside it.\n *\n * Changing what a generator returns for a version it already accepts is a\n * defect. New behaviour raises this constant instead, and the older version\n * keeps producing what it produced. Withdrawing an accepted version is a\n * breaking change, and a project pinned to a withdrawn or not-yet-known\n * version is rejected rather than reinterpreted.\n *\n * @category Utilities\n */\nexport const ALGORITHM_VERSION = 1;\n\n/**\n * The oldest algorithm version this build still produces.\n *\n * @category Utilities\n */\nexport const MIN_ALGORITHM_VERSION = 1;\n\n/**\n * Resolve the algorithm version a request runs under.\n *\n * An unpinned request runs at {@link ALGORITHM_VERSION}, which is what a\n * caller who wants the current behaviour means; a caller who wants the piece\n * back records the resolved number and passes it next time. A version this\n * build does not produce is rejected — a project saved by a newer build cannot\n * be reproduced by an older one, and quietly rendering it at another version\n * would return a different piece under the same name.\n *\n * @param requested The pinned version, or undefined for the current one.\n * @returns The version to generate under.\n * @throws If the version is not an integer this build produces.\n * @example\n * ```ts\n * import { ALGORITHM_VERSION, resolveAlgorithmVersion } from '@libraz/libcantus';\n * resolveAlgorithmVersion(undefined); // ALGORITHM_VERSION\n * resolveAlgorithmVersion(ALGORITHM_VERSION); // the pinned value, unchanged\n * ```\n * @category Utilities\n */\nexport function resolveAlgorithmVersion(requested?: number): number {\n if (requested === undefined) {\n return ALGORITHM_VERSION;\n }\n assertInteger(requested, 'algorithm version');\n if (requested < MIN_ALGORITHM_VERSION || requested > ALGORITHM_VERSION) {\n throw new InvalidInputError(\n `algorithm version ${requested} is not produced by this build; supported versions are ` +\n `${MIN_ALGORITHM_VERSION}..${ALGORITHM_VERSION}`,\n );\n }\n return requested;\n}\n","/**\n * Deterministic seeded pseudo-random number generation shared across the\n * generative modules (rhythm, drums, bass, groove, counter-melody). A single\n * mulberry32 core exposes the small set of samplers those modules need; given a\n * seed the stream is fully reproducible.\n *\n * Around that core sit the pieces that make a whole project reproducible: seeds\n * derived hierarchically from one project number, draws addressed by position\n * rather than by call order, and the algorithm version the output is pinned to.\n */\n\nimport { InvalidInputError } from '../errors/index.js';\nimport { assertFiniteNumber, assertInteger, assertRange } from '../validation/index.js';\n\nexport type { PositionalRng } from './positional.js';\nexport { createPositionalRng, includeAt } from './positional.js';\nexport type { SeedPath } from './seed.js';\nexport { deriveSeed } from './seed.js';\nexport { ALGORITHM_VERSION, MIN_ALGORITHM_VERSION, resolveAlgorithmVersion } from './version.js';\n\n/**\n * A deterministic PRNG with the sampling helpers the generators need.\n *\n * @category Utilities\n */\nexport type Rng = {\n /** Next float in [0, 1). */\n next: () => number;\n /** True with probability `p`. */\n prob: (p: number) => boolean;\n /** Integer in the inclusive range [lo, hi]. */\n range: (lo: number, hi: number) => number;\n /** Float in [lo, hi). */\n float: (lo: number, hi: number) => number;\n};\n\n/**\n * Create a seeded PRNG (mulberry32) exposing the shared samplers.\n *\n * @param seed The 32-bit seed; the same seed always yields the same stream.\n * @returns The seeded generator.\n * @example\n * ```ts\n * import { createRng } from '@libraz/libcantus';\n * const rng = createRng(42);\n * rng.next(); // deterministic float in [0, 1)\n * rng.range(1, 6); // deterministic integer in [1, 6]\n * ```\n * @category Utilities\n */\nexport function createRng(seed: number): Rng {\n // The state is 32 bits, so a wider or fractional seed would be truncated —\n // silently mapping distinct seeds onto one sequence.\n assertInteger(seed, 'seed', 0, 0xffffffff);\n let state = seed >>> 0;\n const next = () => {\n state = (state + 0x6d2b79f5) >>> 0;\n let t = state;\n t = Math.imul(t ^ (t >>> 15), t | 1);\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61);\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296;\n };\n return {\n next,\n prob: (p) => {\n // Validate first: a draw consumed by a rejected call shifts the stream,\n // so a caller who catches the error gets a different sequence than one\n // who never made the call.\n assertRange(p, 0, 1, 'probability');\n return next() < p;\n },\n range: (lo, hi) => {\n assertInteger(lo, 'range lower bound');\n assertInteger(hi, 'range upper bound');\n if (lo > hi) {\n throw new InvalidInputError(\n `range lower bound must not exceed upper bound; received ${lo} > ${hi}`,\n );\n }\n assertInteger(hi - lo, 'range span', 0);\n return lo + Math.floor(next() * (hi - lo + 1));\n },\n float: (lo, hi) => {\n assertFiniteNumber(lo, 'float lower bound');\n assertFiniteNumber(hi, 'float upper bound');\n if (lo > hi) {\n throw new InvalidInputError(\n `float lower bound must not exceed upper bound; received ${lo} > ${hi}`,\n );\n }\n assertFiniteNumber(hi - lo, 'float span');\n return lo + next() * (hi - lo);\n },\n };\n}\n"],"mappings":"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","names":["low","high"]}
|