@libraz/libcantus 1.2.0 → 1.3.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.
Files changed (56) hide show
  1. package/README.md +4 -0
  2. package/README_ja.md +4 -0
  3. package/dist/analyze/index.cjs +28 -4
  4. package/dist/analyze/index.cjs.map +1 -1
  5. package/dist/analyze/index.d.cts +151 -5
  6. package/dist/analyze/index.d.ts +151 -5
  7. package/dist/analyze/index.js +34 -10
  8. package/dist/{chunk-DSMJRXCZ.cjs → chunk-4R2LWV7E.cjs} +384 -107
  9. package/dist/chunk-4R2LWV7E.cjs.map +1 -0
  10. package/dist/{chunk-J7FHVZII.cjs → chunk-B2PW6EHN.cjs} +2000 -30
  11. package/dist/chunk-B2PW6EHN.cjs.map +1 -0
  12. package/dist/{chunk-Q6LFSAKV.js → chunk-CPMB7P75.js} +1973 -3
  13. package/dist/chunk-CPMB7P75.js.map +1 -0
  14. package/dist/{chunk-H2KJHBW4.js → chunk-GNVN4PPJ.js} +1871 -5
  15. package/dist/chunk-GNVN4PPJ.js.map +1 -0
  16. package/dist/{chunk-SGLEQVJH.cjs → chunk-IFZDWIKM.cjs} +1868 -2
  17. package/dist/chunk-IFZDWIKM.cjs.map +1 -0
  18. package/dist/{chunk-FBH75DAY.js → chunk-IPEF6NWB.js} +287 -10
  19. package/dist/chunk-IPEF6NWB.js.map +1 -0
  20. package/dist/{chunk-HYJJ2V2I.js → chunk-MLBPXA4Z.js} +300 -720
  21. package/dist/chunk-MLBPXA4Z.js.map +1 -0
  22. package/dist/{chunk-HWHGOVO6.cjs → chunk-PWC4CRWT.cjs} +1 -1
  23. package/dist/{chunk-HWHGOVO6.cjs.map → chunk-PWC4CRWT.cjs.map} +1 -1
  24. package/dist/{chunk-HPVSH7SF.cjs → chunk-YFA7X2F6.cjs} +350 -770
  25. package/dist/chunk-YFA7X2F6.cjs.map +1 -0
  26. package/dist/{chunk-NFJ4P6ZZ.js → chunk-ZBUROTJQ.js} +1 -1
  27. package/dist/generate/index.cjs +15 -3
  28. package/dist/generate/index.cjs.map +1 -1
  29. package/dist/generate/index.d.cts +84 -6
  30. package/dist/generate/index.d.ts +84 -6
  31. package/dist/generate/index.js +14 -2
  32. package/dist/{index-BNSM-DSk.d.cts → index-CY-3NVfy.d.cts} +484 -3
  33. package/dist/{index-BJgP97N1.d.ts → index-zaZdmnU4.d.ts} +484 -3
  34. package/dist/index.cjs +43 -7
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +5 -5
  37. package/dist/index.d.ts +5 -5
  38. package/dist/index.js +48 -12
  39. package/dist/model/index.cjs +5 -5
  40. package/dist/model/index.d.cts +203 -7
  41. package/dist/model/index.d.ts +203 -7
  42. package/dist/model/index.js +4 -4
  43. package/dist/{index-DGCNKAUr.d.cts → profile-CQMEqdH9.d.cts} +65 -598
  44. package/dist/{index-BRMbPkHE.d.ts → profile-DLdlIpS9.d.ts} +65 -598
  45. package/dist/{index-BBWNZS0f.d.cts → types-D-XgxGvp.d.cts} +1169 -13
  46. package/dist/{index-CJBBiAmU.d.ts → types-Dh7C064C.d.ts} +1169 -13
  47. package/package.json +5 -5
  48. package/dist/chunk-DSMJRXCZ.cjs.map +0 -1
  49. package/dist/chunk-FBH75DAY.js.map +0 -1
  50. package/dist/chunk-H2KJHBW4.js.map +0 -1
  51. package/dist/chunk-HPVSH7SF.cjs.map +0 -1
  52. package/dist/chunk-HYJJ2V2I.js.map +0 -1
  53. package/dist/chunk-J7FHVZII.cjs.map +0 -1
  54. package/dist/chunk-Q6LFSAKV.js.map +0 -1
  55. package/dist/chunk-SGLEQVJH.cjs.map +0 -1
  56. /package/dist/{chunk-NFJ4P6ZZ.js.map → chunk-ZBUROTJQ.js.map} +0 -0
@@ -1,8 +1,8 @@
1
- import { T as TimeSignature, b as MeterLike } from './index-CL4b4gUQ.js';
1
+ import { T as TimeSignature, b as MeterLike, c as MeterMap } from './index-CL4b4gUQ.js';
2
2
  import { a as Articulation, h as NoteEvent } from './index-Cy15oTvh.js';
3
3
  import { S as SeedPath, d as PositionalRng, I as InstrumentProfile, f as StringedProfile } from './positional-DJgs6ltD.js';
4
4
  import { d as ChordQuality, e as ChordSegment, c as ChordLike, K as KeyLike, b as Chord, m as ProfileWeights, g as ChordSpan, n as ResolvedKey, r as SpelledKeyLike } from './coerce-DP0vYu-x.js';
5
- import { e as ChordTimeline, B as BorrowedSource, H as HarmonicFunction } from './index-CJBBiAmU.js';
5
+ import { h as ChordTimeline, e as CadenceType, v as MelodicContourShape, P as MotifRelationSummary, a5 as ReferenceProfile, b as BorrowedSource, o as HarmonicFunction } from './types-Dh7C064C.js';
6
6
  import { H as HarmonyRole } from './index-Dw2DP7vm.js';
7
7
 
8
8
  /**
@@ -1805,6 +1805,266 @@ type HarmonizeResult = {
1805
1805
  }[];
1806
1806
  };
1807
1807
 
1808
+ /**
1809
+ * A composition plan: what a piece is going to be, before any note is chosen.
1810
+ *
1811
+ * Where a {@link ReferenceProfile} records what an existing piece did, a plan
1812
+ * records what a new one will do — the same structural dimensions (form,
1813
+ * harmony, motif derivation, rhythm), stated as targets a generator can hit
1814
+ * exactly rather than as a reading to approximate. A plan is plain data,
1815
+ * JSON-serializable end to end, and it carries the seed and algorithm version
1816
+ * a generator needs to reproduce the piece it describes: the plan is the
1817
+ * recipe, not a cache of one run of it.
1818
+ */
1819
+
1820
+ /**
1821
+ * Schema version {@link CompositionPlan} is written at.
1822
+ *
1823
+ * {@link assertCompositionPlan} rejects any other value outright rather than
1824
+ * guess at an older shape a caller's stored JSON might be in.
1825
+ *
1826
+ * @category Composition
1827
+ */
1828
+ declare const COMPOSITION_PLAN_VERSION = 1;
1829
+ /**
1830
+ * What a new piece is going to be: its keys, meter and span, the sections and
1831
+ * phrases it is built from, the harmony and motif derivations a generator
1832
+ * fills those phrases with, and the rhythmic target it aims for.
1833
+ *
1834
+ * @category Composition
1835
+ */
1836
+ type CompositionPlan = {
1837
+ /** Schema version this record is written at; see {@link COMPOSITION_PLAN_VERSION}. */
1838
+ planVersion: number;
1839
+ /** The project seed a generator following this plan draws from. */
1840
+ seed: number;
1841
+ /** The algorithm version a generator following this plan runs under. */
1842
+ algorithmVersion: number;
1843
+ /** Keys the piece passes through; index 0 is the home key harmony is read against by default. */
1844
+ keys: ResolvedKey[];
1845
+ /** The piece's meter across the planned span. */
1846
+ meters: MeterMap;
1847
+ /** The planned span. */
1848
+ span: {
1849
+ startBeat: number;
1850
+ endBeat: number;
1851
+ bars: number;
1852
+ };
1853
+ /** Sections, in time order. */
1854
+ sections: PlannedSection[];
1855
+ /** Phrases, in time order. */
1856
+ phrases: PlannedPhrase[];
1857
+ /** The harmonic progression, as a partition of the span. */
1858
+ harmony: PlannedChord[];
1859
+ /** Motifs and how they derive from one another. */
1860
+ motifs: PlannedMotif[];
1861
+ /** The rhythmic target a generator's onset placement aims for. */
1862
+ rhythm: PlannedRhythm;
1863
+ };
1864
+ /**
1865
+ * One planned section: a labelled span of the piece.
1866
+ *
1867
+ * @category Composition
1868
+ */
1869
+ type PlannedSection = {
1870
+ /** The section's label. */
1871
+ label: string;
1872
+ /** First beat of the section. */
1873
+ startBeat: number;
1874
+ /** End of the section, exclusive. */
1875
+ endBeat: number;
1876
+ };
1877
+ /**
1878
+ * One planned phrase: its span, the section it opens in, the cadence and
1879
+ * melodic shape it targets, and the motifs it carries.
1880
+ *
1881
+ * @category Composition
1882
+ */
1883
+ type PlannedPhrase = {
1884
+ /** First beat of the phrase. */
1885
+ startBeat: number;
1886
+ /** End of the phrase, exclusive. */
1887
+ endBeat: number;
1888
+ /** Index into `plan.sections` of the section this phrase's start beat falls in. */
1889
+ section: number | null;
1890
+ /** The cadence the phrase is to end on, or null for none. */
1891
+ cadence: CadenceType | null;
1892
+ /** The phrase's targeted melodic shape. */
1893
+ shape: MelodicContourShape;
1894
+ /** Onset of the phrase's targeted peak, as a fraction of the phrase's length, in [0, 1]. */
1895
+ peakPosition: number;
1896
+ /** The phrase's targeted pitch range and mean, as absolute MIDI pitches. */
1897
+ register: {
1898
+ low: number;
1899
+ high: number;
1900
+ mean: number;
1901
+ };
1902
+ /** Targeted onsets per bar within the phrase; see {@link RhythmAnalysis.onsetDensity}. */
1903
+ onsetDensity: number;
1904
+ /** Indices into `plan.motifs` of every motif statement placed in this phrase. */
1905
+ motifs: number[];
1906
+ };
1907
+ /**
1908
+ * One planned chord, placed against one of the plan's keys.
1909
+ *
1910
+ * @category Composition
1911
+ */
1912
+ type PlannedChord = {
1913
+ /** First beat of the chord. */
1914
+ startBeat: number;
1915
+ /** End of the chord, exclusive. */
1916
+ endBeat: number;
1917
+ /** Index into `plan.keys` of the key this chord is named against. */
1918
+ key: number;
1919
+ /** The chord, as a Roman numeral in that key. */
1920
+ roman: string;
1921
+ };
1922
+ /**
1923
+ * One planned motif statement: a root the generator writes new notes for, or a
1924
+ * derivation the generator obtains from an earlier statement by a named
1925
+ * transformation.
1926
+ *
1927
+ * @category Composition
1928
+ */
1929
+ type PlannedMotif = {
1930
+ /** Index into `plan.phrases` of the phrase this statement is placed in. */
1931
+ phrase: number;
1932
+ /** First beat of the statement. */
1933
+ startBeat: number;
1934
+ /** End of the statement, exclusive. */
1935
+ endBeat: number;
1936
+ /** How many notes the statement carries; a root's generated length, a derivation's inherited from its source. */
1937
+ notes: number;
1938
+ /**
1939
+ * A root's onset-to-onset gaps in beats, `notes - 1` of them, summing to
1940
+ * less than the statement's length: its reference motif's ratios times
1941
+ * {@link ReferenceMotif.unitBeats}. The first note sounds at `startBeat`
1942
+ * and the last lasts to `endBeat`. Null for a root whose onsets a generator
1943
+ * draws from `plan.rhythm`; always null for a derivation, whose rhythm is
1944
+ * its source's under the named transformation.
1945
+ */
1946
+ rhythm: number[] | null;
1947
+ /** Index into `plan.motifs` of the statement this one derives from; null for a root. Always less than this statement's own index. */
1948
+ from: number | null;
1949
+ /**
1950
+ * The named transformation from `from`, or null for a root, or for a
1951
+ * derivation with no named transformation (a variation). Its `semitones`
1952
+ * and `degrees` are the preferred pitch level: a generator may place the
1953
+ * derivation at another level to keep it inside the register and the harmony.
1954
+ */
1955
+ relation: MotifRelationSummary | null;
1956
+ };
1957
+ /**
1958
+ * The rhythmic target a generator's onset placement aims for.
1959
+ *
1960
+ * @category Composition
1961
+ */
1962
+ type PlannedRhythm = {
1963
+ /** Targeted distribution of onsets across metric levels; see {@link RhythmAnalysis.onsetLevels}. */
1964
+ onsetLevels: number[];
1965
+ /** Targeted distribution of inter-onset intervals; see {@link RhythmAnalysis.interOnsetShares}. */
1966
+ interOnsetShares: number[];
1967
+ /** Targeted syncopation, in [0, 1]; see {@link RhythmAnalysis.syncopation}. */
1968
+ syncopation: number;
1969
+ };
1970
+
1971
+ /**
1972
+ * Melody generation from a {@link CompositionPlan}.
1973
+ *
1974
+ * The plan is a hard constraint where the generator is free: every root, every
1975
+ * span no motif covers and every closing note keeps its phrase's register and
1976
+ * offers only the pitches its position class admits — a chord tone on a strong
1977
+ * pulse, a scale tone on a weak one, anything in the register off the pulses.
1978
+ * A derived statement is its source replayed through the named relation at the
1979
+ * pitch level that suits its phrase best; there the transformation outranks
1980
+ * the harmony, and the register outranks both. A root takes its plan's onset
1981
+ * gaps, or onsets drawn toward the plan's onset levels and gap shares, and pitches from a
1982
+ * chain search toward the phrase's target curve; each phrase closes on a note
1983
+ * held from its final bar to its end, bent onto its cadence.
1984
+ *
1985
+ * Every draw is addressed by phrase index and absolute beat, and a phrase
1986
+ * reads only its own plan entries and the statements its derivations lead
1987
+ * back to, so changing one phrase's plan leaves every phrase that does not
1988
+ * derive from it sounding as it did.
1989
+ */
1990
+
1991
+ /**
1992
+ * Options controlling {@link generateMelody}.
1993
+ *
1994
+ * @category Composition
1995
+ */
1996
+ type MelodyOptions = {
1997
+ /**
1998
+ * The generation context. Its `seed` and `algorithmVersion` take precedence
1999
+ * over the plan's; whatever it leaves out is read from the plan. A supplied
2000
+ * `rng` is rejected: every onset and tie-break is drawn at an address of
2001
+ * phrase and beat under a seed, which a caller-held source cannot provide.
2002
+ *
2003
+ * @defaultValue the plan's seed and algorithm version
2004
+ */
2005
+ ctx?: GenerationContextInput;
2006
+ /** Upper bound on the onset grid and on the pitch search. */
2007
+ budget?: number;
2008
+ };
2009
+ /**
2010
+ * Write a melody that follows a composition plan.
2011
+ *
2012
+ * Each phrase is written inside its planned register, and every note the
2013
+ * generator chooses freely takes a pitch its position class admits: a chord
2014
+ * tone on a strong pulse, a scale tone on a weak one, any pitch off the pulses.
2015
+ * A root motif takes the onset gaps its plan carries from its start, its last
2016
+ * note lasting to its end, or onsets drawn one after another on the sixteenth
2017
+ * grid toward the plan's onset levels and gap shares; its pitches come from a chain
2018
+ * search that moves by step, recovers from leaps and follows the phrase's
2019
+ * target shape. A derived motif replays its planned relation on the statement
2020
+ * it derives from, starting on its planned beat, at the pitch level inside the
2021
+ * register that best fits the harmony, the target curve and the planned level;
2022
+ * a harmony misfit it keeps is left, and only notes no level brings inside the
2023
+ * register are replaced. A variation keeps its source's rhythm and outer
2024
+ * pitches. The spans no motif covers get drawn onsets at the phrase's density
2025
+ * and searched pitches. In each phrase's final bar, the note sounding at the
2026
+ * downbeat (or the first onset after it) is held to the phrase's end, later
2027
+ * onsets are dropped, and that note is bent onto the tones the phrase's
2028
+ * cadence asks for; a note sounding across the downbeat is cut there, and the
2029
+ * downbeat becomes the held onset, re-chosen from the pitches the downbeat
2030
+ * admits unless a cadence or a derived statement decides it. Onsets fall on the sixteenth grid only, so
2031
+ * triplets never appear.
2032
+ *
2033
+ * Harmony is read from the plan alone, through {@link planTimeline}.
2034
+ *
2035
+ * @param plan The plan to follow; validated first.
2036
+ * @param opts Context and budget.
2037
+ * @returns The melody, in time order.
2038
+ * @throws {InvalidInputError} If the plan, the options or the context are
2039
+ * malformed, or the context supplies an `rng`.
2040
+ * @throws {NoSolutionError} If a phrase's register admits no line, no pitch a
2041
+ * position asks for, no pitch that ends its cadence, or a span more onsets
2042
+ * than its grid holds; `at` is the phrase's start beat.
2043
+ * @throws {BudgetExceededError} If the onset grid or the pitch search would
2044
+ * exceed the budget.
2045
+ * @example
2046
+ * ```ts
2047
+ * import { generateMelody, resolveKey } from '@libraz/libcantus';
2048
+ * import type { CompositionPlan } from '@libraz/libcantus';
2049
+ * const plan: CompositionPlan = {
2050
+ * planVersion: 1, seed: 7, algorithmVersion: 1,
2051
+ * keys: [resolveKey('C major')], meters: [{ startBeat: 0, ts: { numerator: 4, denominator: 4 } }],
2052
+ * span: { startBeat: 0, endBeat: 8, bars: 2 },
2053
+ * sections: [{ label: 'A', startBeat: 0, endBeat: 8 }],
2054
+ * phrases: [{
2055
+ * startBeat: 0, endBeat: 8, section: 0, cadence: 'authentic', shape: 'arch', peakPosition: 0.6,
2056
+ * register: { low: 60, high: 72, mean: 66 }, onsetDensity: 4, motifs: [0],
2057
+ * }],
2058
+ * harmony: [{ startBeat: 0, endBeat: 4, key: 0, roman: 'V' }, { startBeat: 4, endBeat: 8, key: 0, roman: 'I' }],
2059
+ * motifs: [{ phrase: 0, startBeat: 0, endBeat: 4, notes: 4, rhythm: [1, 1, 1], from: null, relation: null }],
2060
+ * rhythm: { onsetLevels: [0, 0, 0, 0, 0, 1], interOnsetShares: new Array(17).fill(0).fill(1, 8, 9), syncopation: 0 },
2061
+ * };
2062
+ * generateMelody(plan); // the same notes on every call
2063
+ * ```
2064
+ * @category Composition
2065
+ */
2066
+ declare function generateMelody(plan: CompositionPlan, opts?: MelodyOptions): NoteEvent[];
2067
+
1808
2068
  /**
1809
2069
  * A transformation applicable to a motif cell.
1810
2070
  *
@@ -2109,6 +2369,227 @@ type OrnamentOptions = {
2109
2369
  */
2110
2370
  declare function ornament(notes: readonly NoteEvent[], opts?: OrnamentOptions): NoteEvent[];
2111
2371
 
2372
+ /**
2373
+ * Deriving a {@link CompositionPlan} from a {@link ReferenceProfile}.
2374
+ *
2375
+ * Each structural dimension of the reference is either carried into the plan
2376
+ * or replaced, weighted per dimension by {@link PreserveWeights}. A discrete
2377
+ * item (a section label, a phrase length, a structural chord, a motif
2378
+ * derivation) is kept or replaced whole by a positional draw addressed by that
2379
+ * item's index, so changing one weight or one item never redraws another; a
2380
+ * replacement follows a fixed rule. A continuous item (harmonic rhythm,
2381
+ * register and contour, onset placement) is interpolated between the
2382
+ * reference's value and a neutral one.
2383
+ *
2384
+ * The reference's melodic surface — its motif intervals and phrase outlines —
2385
+ * never reaches the plan: a plan says how motifs derive from one another and
2386
+ * carries a root's onset gaps, not the pitches it sounds.
2387
+ */
2388
+
2389
+ /**
2390
+ * How much of each structural dimension of a reference a derived plan keeps,
2391
+ * each in [0, 1]; an omitted weight is 1.
2392
+ *
2393
+ * @category Composition
2394
+ */
2395
+ type PreserveWeights = {
2396
+ /** Section labels; a replaced section takes its neighbour's label. */
2397
+ form?: number;
2398
+ /** Phrase lengths; a replaced phrase is rounded to a power-of-two bar count. */
2399
+ phraseLengths?: number;
2400
+ /** Structural chords; a replaced chord is another numeral of the same function. */
2401
+ harmonicFunction?: number;
2402
+ /** Where the chords change, interpolated toward one change per bar line. */
2403
+ harmonicRhythm?: number;
2404
+ /** Motif derivations; a replaced derivation becomes a variation of the same source. */
2405
+ motifRelations?: number;
2406
+ /** Phrase contour, peak and register, interpolated toward a neutral arch. */
2407
+ registerShape?: number;
2408
+ /**
2409
+ * Each root's onset gaps, kept per root by a positional draw, and the
2410
+ * onset placement, interpolated toward a level-proportional distribution
2411
+ * without syncopation.
2412
+ */
2413
+ rhythm?: number;
2414
+ };
2415
+ /**
2416
+ * Options for {@link deriveCompositionPlan}.
2417
+ *
2418
+ * @category Composition
2419
+ */
2420
+ type CompositionPlanOptions = {
2421
+ /**
2422
+ * The home key of the plan. Every reference key moves by the interval from
2423
+ * the reference's home tonic to this one, keeping its own variant. Defaults
2424
+ * to the reference's home key; a key of another variant is rejected.
2425
+ */
2426
+ key?: KeyLike;
2427
+ /**
2428
+ * The melody range the plan's phrase registers are mapped onto. Defaults to
2429
+ * the reference's melody range, moved by the nearest shift to the new key.
2430
+ */
2431
+ register?: {
2432
+ low: number;
2433
+ high: number;
2434
+ };
2435
+ /** How much of each dimension of the reference to keep; see {@link PreserveWeights}. */
2436
+ preserve?: PreserveWeights;
2437
+ /**
2438
+ * The seed and algorithm version the plan records. A caller-supplied `rng`
2439
+ * is rejected: a plan has to carry its source as a seed to be reproducible.
2440
+ */
2441
+ ctx?: GenerationContextInput;
2442
+ /** Upper bound on the motif selection. */
2443
+ budget?: number;
2444
+ };
2445
+ /**
2446
+ * Derive a composition plan from a reference profile.
2447
+ *
2448
+ * The plan keeps the reference's meter and span. Its keys are the reference's,
2449
+ * moved to `opts.key` with each key's variant kept, and its phrase registers
2450
+ * are the reference's mapped onto `opts.register`. Each dimension named in
2451
+ * `opts.preserve` is kept, replaced or interpolated by its weight; a draw for
2452
+ * a discrete item is addressed by the dimension and the item's index, so the
2453
+ * same reference, options and seed always give the same plan. The resolved
2454
+ * seed and algorithm version are recorded in the plan.
2455
+ *
2456
+ * @param reference The reference profile to derive from.
2457
+ * @param opts The key, register, preserve weights, generation context and budget.
2458
+ * @returns A plan that passes {@link assertCompositionPlan}.
2459
+ * @throws {InvalidInputError} If the reference is not a valid profile or has
2460
+ * no melody onsets, an option is out of range, `opts.key` is of another
2461
+ * variant than the reference's home key, `opts.ctx` carries an `rng`, or the
2462
+ * default register moved to the new key leaves the MIDI range.
2463
+ * @throws {BudgetExceededError} If the motif selection would exceed the budget.
2464
+ * @category Composition
2465
+ */
2466
+ declare function deriveCompositionPlan(reference: ReferenceProfile, opts?: CompositionPlanOptions): CompositionPlan;
2467
+
2468
+ /**
2469
+ * Checking a candidate melody against the plan it was written for.
2470
+ *
2471
+ * The candidate is read against the plan itself, never re-analyzed: its
2472
+ * phrases are the plan's phrases, its harmony is the plan's through
2473
+ * `planTimeline`, and its derivations are replayed from the candidate's own
2474
+ * source statements. What comes back is never one score: a list of concrete
2475
+ * violations (which are errors and which are only warnings), and a fit reading
2476
+ * per dimension, each measured the way {@link compareReferences} measures two
2477
+ * profiles against each other. Whether the piece is any good is left to the ear.
2478
+ */
2479
+
2480
+ /**
2481
+ * One way a candidate melody departs from its plan.
2482
+ *
2483
+ * @category Composition
2484
+ */
2485
+ type CompositionViolation = {
2486
+ /** What kind of departure this is. */
2487
+ kind: 'span' | 'phraseBoundary' | 'cadence' | 'harmony' | 'motifDerivation' | 'motifDisplaced' | 'register' | 'peakPosition';
2488
+ /** Whether this rules the candidate out, or only flags it. */
2489
+ severity: 'error' | 'warning';
2490
+ /** Where in the candidate this was found. */
2491
+ atBeat: number;
2492
+ /** What the plan called for. */
2493
+ expected: string;
2494
+ /** What the candidate did instead. */
2495
+ actual: string;
2496
+ /** Why this counts as a departure. */
2497
+ rationale: string;
2498
+ };
2499
+ /**
2500
+ * How a candidate melody measures up against its plan: violations that judge
2501
+ * it, and a fit reading per structural dimension.
2502
+ *
2503
+ * There is no aggregate: the fields never fold into one score, the same way
2504
+ * {@link ReferenceComparison} does not.
2505
+ *
2506
+ * @category Composition
2507
+ */
2508
+ type CompositionEvaluation = {
2509
+ /** Concrete departures from the plan, error and warning alike. */
2510
+ violations: CompositionViolation[];
2511
+ /** Likeness of the candidate to the plan, one field per dimension, each phrase read over its planned span. */
2512
+ fit: {
2513
+ /** Likeness of the phrases' melodic outlines to their planned shapes. */
2514
+ contour: number | null;
2515
+ /** Likeness of the phrases' registers to the planned ones. */
2516
+ register: number | null;
2517
+ /** Likeness of where the melody's onsets fall. */
2518
+ onset: number | null;
2519
+ /** Likeness of the melody's inter-onset-interval distribution. */
2520
+ duration: number | null;
2521
+ /** Likeness of the melody's syncopation. */
2522
+ syncopation: number | null;
2523
+ /** Likeness of the melody's onsets per bar to the phrases' planned densities. */
2524
+ density: number | null;
2525
+ };
2526
+ };
2527
+ /** Options for {@link evaluateComposition}. */
2528
+ type EvaluateCompositionOptions = {
2529
+ /**
2530
+ * Upper bound on the work each analysis may do.
2531
+ *
2532
+ * @defaultValue {@link DEFAULT_GENERATION_BUDGET}
2533
+ */
2534
+ budget?: number;
2535
+ };
2536
+ /**
2537
+ * Check a candidate melody against the plan it was written for.
2538
+ *
2539
+ * The candidate is read against the plan itself — its phrases, its harmony
2540
+ * through {@link planTimeline}, its derivations — rather than re-analyzed into
2541
+ * a profile, so every violation and fit reading judges the melody against what
2542
+ * the plan actually calls for.
2543
+ *
2544
+ * Violations name concrete departures at a beat. `span`, `phraseBoundary`,
2545
+ * `cadence`, `register` and `motifDerivation` are errors. `harmony` is an
2546
+ * error, or a warning for a note inside a derived statement, where the
2547
+ * transformation takes precedence. A derived statement passes at any pitch
2548
+ * level its relation admits, with its durations free and its closing and
2549
+ * out-of-register notes excused; `motifDisplaced` warns when that level is not
2550
+ * the planned one or a note was excused. `peakPosition` is a warning. The fit
2551
+ * fields, one per dimension, read each planned phrase of the candidate the way
2552
+ * a reference profile reads its phrases and measure it as
2553
+ * {@link compareReferences} would — never folded into one score.
2554
+ *
2555
+ * @param melody The candidate melody.
2556
+ * @param plan The plan it was written for.
2557
+ * @param opts The work budget.
2558
+ * @returns The violations found and the fit reading.
2559
+ * @throws {InvalidInputError} If the plan or the melody cannot be read.
2560
+ * @throws {BudgetExceededError} If an analysis would exceed the budget.
2561
+ * @example
2562
+ * ```ts
2563
+ * import { evaluateComposition, planTimeline, resolveKey } from '@libraz/libcantus';
2564
+ * import type { CompositionPlan } from '@libraz/libcantus';
2565
+ * const plan: CompositionPlan = {
2566
+ * planVersion: 1, seed: 0, algorithmVersion: 1,
2567
+ * keys: [resolveKey('C major')],
2568
+ * meters: [{ startBeat: 0, ts: { numerator: 4, denominator: 4 } }],
2569
+ * span: { startBeat: 0, endBeat: 4, bars: 1 },
2570
+ * sections: [{ label: 'A', startBeat: 0, endBeat: 4 }],
2571
+ * phrases: [
2572
+ * {
2573
+ * startBeat: 0, endBeat: 4, section: 0, cadence: null, shape: 'ascending',
2574
+ * peakPosition: 0.5, register: { low: 60, high: 67, mean: 64 }, onsetDensity: 4, motifs: [],
2575
+ * },
2576
+ * ],
2577
+ * harmony: [{ startBeat: 0, endBeat: 4, key: 0, roman: 'I' }],
2578
+ * motifs: [],
2579
+ * rhythm: { onsetLevels: [0, 0, 0, 1, 0, 0], interOnsetShares: new Array(17).fill(0).fill(1, 8, 9), syncopation: 0 },
2580
+ * };
2581
+ * const melody = [
2582
+ * { pitch: 60, startBeat: 0, durationBeat: 1 },
2583
+ * { pitch: 64, startBeat: 1, durationBeat: 1 },
2584
+ * { pitch: 67, startBeat: 2, durationBeat: 1 },
2585
+ * { pitch: 60, startBeat: 3, durationBeat: 1 },
2586
+ * ];
2587
+ * evaluateComposition(melody, plan).violations; // []
2588
+ * ```
2589
+ * @category Composition
2590
+ */
2591
+ declare function evaluateComposition(melody: readonly NoteEvent[], plan: CompositionPlan, opts?: EvaluateCompositionOptions): CompositionEvaluation;
2592
+
2112
2593
  /**
2113
2594
  * Reharmonization: chord substitution, modal-interchange palettes, and negative
2114
2595
  * harmony. These transforms take an existing chord (or a key) and propose
@@ -2386,4 +2867,4 @@ declare function generateRhythm(ts: MeterLike, opts?: RhythmOptions): RhythmEven
2386
2867
  */
2387
2868
  declare function rhythmDensity(events: RhythmEvent[], ts: MeterLike): number;
2388
2869
 
2389
- export { type Section as $, type MotifCell as A, BAR_STEPS as B, type Complexity as C, DRUM_NOTES as D, type EuclideanKick as E, type MotifContour as F, GENRES as G, type HarmonizeOptions as H, type MotifNote as I, type MotifOptions as J, type MotifTransform as K, type OrnamentOptions as L, type MelodyNote as M, type OrnamentStyle as N, ORNAMENT_STYLES as O, PROVENANCE_BASES as P, type ProgFunction as Q, type ProgStyle as R, type ProgressionDegree as S, type ProgressionOptions as T, type ProgressionPreset as U, type Provenance as V, type ProvenanceBasis as W, type ResolvedContext as X, type RhythmEvent as Y, type RhythmOptions as Z, STEP_BEATS as _, BASS_STYLES as a, type SubstituteOptions as a0, type Substitution as a1, type SubstitutionType as a2, type SyncopateOptions as a3, type Vocabulary as a4, type VocabularyQuery as a5, applyGrooveTemplate as a6, assertVocabulary as a7, deform as a8, developMotif as a9, rhythmDensity as aA, rhythmToNoteEvents as aB, selectVocabulary as aC, substituteChord as aD, syncopate as aE, thin as aF, transformMotif as aG, vocabularyOfKind as aH, withinCeiling as aI, double as aa, doubleTime as ab, drumVoiceOf as ac, extractGrooveTemplate as ad, fitsQuery as ae, generateBassLine as af, generateCounterMelody as ag, generateDrums as ah, generateMotif as ai, generateProgression as aj, generateRhythm as ak, gridMetricWeight as al, halfTime as am, humanize as an, mergeVocabulary as ao, modalInterchangePalette as ap, motifToNoteEvents as aq, negativeHarmonyMirror as ar, onsetWeightCurve as as, ornament as at, ornamentBy as au, pickProgressionPreset as av, pickVocabulary as aw, progressions as ax, progressionsByStyle as ay, resolveContext as az, BEAT_STEPS as b, BORROWED_DEGREES as c, type BassLineOptions as d, type BassSegment as e, type BassStyle as f, type BorrowedChord as g, type CounterMelodyOptions as h, type DeformOptions as i, type Draw as j, type DrumHit as k, type DrumRole as l, type DrumStyle as m, type DrumVoice as n, type DrumsOptions as o, type GenerationContext as p, type GenerationContextInput as q, type Genre as r, type GridEvent as s, type GrooveFeel as t, type GrooveSlot as u, type GrooveStyle as v, type GrooveTemplate as w, type HarmonizePlacement as x, type HarmonizeResult as y, type HumanizeOptions as z };
2870
+ export { type PlannedSection as $, type GrooveSlot as A, BAR_STEPS as B, COMPOSITION_PLAN_VERSION as C, DRUM_NOTES as D, type EuclideanKick as E, type GrooveStyle as F, GENRES as G, type GrooveTemplate as H, type HarmonizeOptions as I, type HarmonizePlacement as J, type HarmonizeResult as K, type HumanizeOptions as L, type MelodyNote as M, type MelodyOptions as N, type MotifCell as O, type MotifContour as P, type MotifNote as Q, type MotifOptions as R, type MotifTransform as S, ORNAMENT_STYLES as T, type OrnamentOptions as U, type OrnamentStyle as V, PROVENANCE_BASES as W, type PlannedChord as X, type PlannedMotif as Y, type PlannedPhrase as Z, type PlannedRhythm as _, BASS_STYLES as a, type PreserveWeights as a0, type ProgFunction as a1, type ProgStyle as a2, type ProgressionDegree as a3, type ProgressionOptions as a4, type ProgressionPreset as a5, type Provenance as a6, type ProvenanceBasis as a7, type ResolvedContext as a8, type RhythmEvent as a9, generateRhythm as aA, gridMetricWeight as aB, halfTime as aC, humanize as aD, mergeVocabulary as aE, modalInterchangePalette as aF, motifToNoteEvents as aG, negativeHarmonyMirror as aH, onsetWeightCurve as aI, ornament as aJ, ornamentBy as aK, pickProgressionPreset as aL, pickVocabulary as aM, progressions as aN, progressionsByStyle as aO, resolveContext as aP, rhythmDensity as aQ, rhythmToNoteEvents as aR, selectVocabulary as aS, substituteChord as aT, syncopate as aU, thin as aV, transformMotif as aW, vocabularyOfKind as aX, withinCeiling as aY, type RhythmOptions as aa, STEP_BEATS as ab, type Section as ac, type SubstituteOptions as ad, type Substitution as ae, type SubstitutionType as af, type SyncopateOptions as ag, type Vocabulary as ah, type VocabularyQuery as ai, applyGrooveTemplate as aj, assertVocabulary as ak, deform as al, deriveCompositionPlan as am, developMotif as an, double as ao, doubleTime as ap, drumVoiceOf as aq, evaluateComposition as ar, extractGrooveTemplate as as, fitsQuery as at, generateBassLine as au, generateCounterMelody as av, generateDrums as aw, generateMelody as ax, generateMotif as ay, generateProgression as az, BEAT_STEPS as b, BORROWED_DEGREES as c, type BassLineOptions as d, type BassSegment as e, type BassStyle as f, type BorrowedChord as g, type Complexity as h, type CompositionEvaluation as i, type CompositionPlan as j, type CompositionPlanOptions as k, type CompositionViolation as l, type CounterMelodyOptions as m, type DeformOptions as n, type Draw as o, type DrumHit as p, type DrumRole as q, type DrumStyle as r, type DrumVoice as s, type DrumsOptions as t, type EvaluateCompositionOptions as u, type GenerationContext as v, type GenerationContextInput as w, type Genre as x, type GridEvent as y, type GrooveFeel as z };