@libraz/libcantus 0.9.4 → 1.0.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 +61 -261
- package/README_ja.md +115 -0
- package/dist/analyze/index.cjs +54 -7
- package/dist/analyze/index.cjs.map +1 -1
- package/dist/analyze/index.d.cts +213 -456
- package/dist/analyze/index.d.ts +213 -456
- package/dist/analyze/index.js +56 -9
- package/dist/chunk-3JXCW6DX.cjs +3220 -0
- package/dist/chunk-3JXCW6DX.cjs.map +1 -0
- package/dist/chunk-4A2RMUCC.js +7647 -0
- package/dist/chunk-4A2RMUCC.js.map +1 -0
- package/dist/chunk-6XPTBE7Y.cjs +49 -0
- package/dist/chunk-6XPTBE7Y.cjs.map +1 -0
- package/dist/chunk-AFM2ULJH.cjs +5121 -0
- package/dist/chunk-AFM2ULJH.cjs.map +1 -0
- package/dist/chunk-E3F4AHAH.cjs +690 -0
- package/dist/chunk-E3F4AHAH.cjs.map +1 -0
- package/dist/chunk-ELCTZUJJ.js +379 -0
- package/dist/chunk-ELCTZUJJ.js.map +1 -0
- package/dist/chunk-FVSI36UV.js +690 -0
- package/dist/chunk-FVSI36UV.js.map +1 -0
- package/dist/chunk-GEDTJHAC.cjs +1 -0
- package/dist/chunk-GEDTJHAC.cjs.map +1 -0
- package/dist/chunk-GOZG57ER.js +3220 -0
- package/dist/chunk-GOZG57ER.js.map +1 -0
- package/dist/chunk-H3VORETI.js +2243 -0
- package/dist/chunk-H3VORETI.js.map +1 -0
- package/dist/chunk-H42UHPYW.js +1577 -0
- package/dist/chunk-H42UHPYW.js.map +1 -0
- package/dist/chunk-HFPS7JFD.js +1 -0
- package/dist/chunk-INHVXP6Z.js +3053 -0
- package/dist/chunk-INHVXP6Z.js.map +1 -0
- package/dist/chunk-K4ZJUGF4.cjs +379 -0
- package/dist/chunk-K4ZJUGF4.cjs.map +1 -0
- package/dist/chunk-KNJEKLKV.js +49 -0
- package/dist/chunk-KNJEKLKV.js.map +1 -0
- package/dist/chunk-M4CWTJWJ.cjs +7647 -0
- package/dist/chunk-M4CWTJWJ.cjs.map +1 -0
- package/dist/chunk-MXO72PLY.cjs +211 -0
- package/dist/chunk-MXO72PLY.cjs.map +1 -0
- package/dist/chunk-NLEU4QSE.cjs +360 -0
- package/dist/chunk-NLEU4QSE.cjs.map +1 -0
- package/dist/chunk-OE6WK2Y2.cjs +1577 -0
- package/dist/chunk-OE6WK2Y2.cjs.map +1 -0
- package/dist/chunk-Q2WGKJNN.cjs +1064 -0
- package/dist/chunk-Q2WGKJNN.cjs.map +1 -0
- package/dist/chunk-QYQQ5J6E.js +1 -0
- package/dist/chunk-QYQQ5J6E.js.map +1 -0
- package/dist/chunk-RHIJ3S4I.cjs +1 -0
- package/dist/chunk-RHIJ3S4I.cjs.map +1 -0
- package/dist/chunk-TTCLJX6D.js +211 -0
- package/dist/chunk-TTCLJX6D.js.map +1 -0
- package/dist/chunk-UADYAYAT.cjs +3053 -0
- package/dist/chunk-UADYAYAT.cjs.map +1 -0
- package/dist/chunk-VOHZB45S.js +5121 -0
- package/dist/chunk-VOHZB45S.js.map +1 -0
- package/dist/chunk-VTI6FOLB.js +1064 -0
- package/dist/chunk-VTI6FOLB.js.map +1 -0
- package/dist/chunk-WWURE4XT.cjs +2243 -0
- package/dist/chunk-WWURE4XT.cjs.map +1 -0
- package/dist/chunk-YH3BIKHH.js +360 -0
- package/dist/chunk-YH3BIKHH.js.map +1 -0
- package/dist/core/index.cjs +103 -6
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +96 -345
- package/dist/core/index.d.ts +96 -345
- package/dist/core/index.js +104 -7
- package/dist/generate/index.cjs +89 -10
- package/dist/generate/index.cjs.map +1 -1
- package/dist/generate/index.d.cts +437 -1095
- package/dist/generate/index.d.ts +437 -1095
- package/dist/generate/index.js +94 -15
- package/dist/index-9wcMhG73.d.ts +1095 -0
- package/dist/{index-D6b6na69.d.cts → index-B4o55NDr.d.cts} +13 -9
- package/dist/index-B9MNZboQ.d.cts +770 -0
- package/dist/index-B9MNZboQ.d.ts +770 -0
- package/dist/index-BGFW-8P2.d.cts +2307 -0
- package/dist/index-BO6xE9YU.d.cts +1804 -0
- package/dist/index-BkGmlyI3.d.cts +470 -0
- package/dist/index-CCNJRG-P.d.cts +1095 -0
- package/dist/index-CGpRv5sl.d.cts +643 -0
- package/dist/index-C_8o--VE.d.cts +1242 -0
- package/dist/index-CgHqxdps.d.ts +2307 -0
- package/dist/index-Chbst3Jx.d.ts +1343 -0
- package/dist/index-Cj49F892.d.ts +470 -0
- package/dist/{index-tSQh-xwY.d.ts → index-CunYKLiu.d.ts} +13 -9
- package/dist/index-D57QRbVn.d.ts +1804 -0
- package/dist/index-DInQqT8Q.d.cts +66 -0
- package/dist/index-DInQqT8Q.d.ts +66 -0
- package/dist/index-Dzr13q3d.d.cts +1343 -0
- package/dist/index-G1uwGGWQ.d.ts +643 -0
- package/dist/index-szBv3496.d.ts +1242 -0
- package/dist/index.cjs +358 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -15
- package/dist/index.d.ts +17 -15
- package/dist/index.js +407 -64
- package/dist/masks-DYbE485J.d.cts +445 -0
- package/dist/masks-DYbE485J.d.ts +445 -0
- package/dist/model/index.cjs +39 -7
- package/dist/model/index.cjs.map +1 -1
- package/dist/model/index.d.cts +4685 -243
- package/dist/model/index.d.ts +4685 -243
- package/dist/model/index.js +40 -8
- package/dist/positional-Cptlj-xs.d.ts +287 -0
- package/dist/positional-DvH_sd8u.d.cts +287 -0
- package/dist/theory/index.cjs +101 -7
- package/dist/theory/index.cjs.map +1 -1
- package/dist/theory/index.d.cts +513 -173
- package/dist/theory/index.d.ts +513 -173
- package/dist/theory/index.js +118 -24
- package/package.json +16 -11
- package/dist/chunk-36N73KZ3.cjs +0 -246
- package/dist/chunk-36N73KZ3.cjs.map +0 -1
- package/dist/chunk-4HLLZPXT.cjs +0 -3077
- package/dist/chunk-4HLLZPXT.cjs.map +0 -1
- package/dist/chunk-7MGZ7D62.cjs +0 -498
- package/dist/chunk-7MGZ7D62.cjs.map +0 -1
- package/dist/chunk-7NKDPF74.js +0 -3077
- package/dist/chunk-7NKDPF74.js.map +0 -1
- package/dist/chunk-CAN76NCU.cjs +0 -679
- package/dist/chunk-CAN76NCU.cjs.map +0 -1
- package/dist/chunk-D2FAJBYL.js +0 -479
- package/dist/chunk-D2FAJBYL.js.map +0 -1
- package/dist/chunk-EHVEBWM5.js +0 -1449
- package/dist/chunk-EHVEBWM5.js.map +0 -1
- package/dist/chunk-FCANXBAT.cjs +0 -30
- package/dist/chunk-FCANXBAT.cjs.map +0 -1
- package/dist/chunk-FNDWXRRI.cjs +0 -479
- package/dist/chunk-FNDWXRRI.cjs.map +0 -1
- package/dist/chunk-GQSGS2N4.js +0 -173
- package/dist/chunk-GQSGS2N4.js.map +0 -1
- package/dist/chunk-HKGYCQ34.js +0 -53
- package/dist/chunk-HKGYCQ34.js.map +0 -1
- package/dist/chunk-I24MV65U.js +0 -106
- package/dist/chunk-I24MV65U.js.map +0 -1
- package/dist/chunk-KMHOICIY.js +0 -679
- package/dist/chunk-KMHOICIY.js.map +0 -1
- package/dist/chunk-LW53AHQU.cjs +0 -1117
- package/dist/chunk-LW53AHQU.cjs.map +0 -1
- package/dist/chunk-N6IHQUOX.js +0 -1
- package/dist/chunk-NKIBQ6PT.js +0 -30
- package/dist/chunk-NKIBQ6PT.js.map +0 -1
- package/dist/chunk-NP4NA2FB.cjs +0 -106
- package/dist/chunk-NP4NA2FB.cjs.map +0 -1
- package/dist/chunk-NVE34YGZ.cjs +0 -1449
- package/dist/chunk-NVE34YGZ.cjs.map +0 -1
- package/dist/chunk-NYYWMV2C.js +0 -327
- package/dist/chunk-NYYWMV2C.js.map +0 -1
- package/dist/chunk-Q2XUP227.js +0 -845
- package/dist/chunk-Q2XUP227.js.map +0 -1
- package/dist/chunk-Q6OP4U43.cjs +0 -327
- package/dist/chunk-Q6OP4U43.cjs.map +0 -1
- package/dist/chunk-RC6GBMCD.cjs +0 -845
- package/dist/chunk-RC6GBMCD.cjs.map +0 -1
- package/dist/chunk-RK35RIN7.js +0 -1117
- package/dist/chunk-RK35RIN7.js.map +0 -1
- package/dist/chunk-RUMU3UA6.cjs +0 -53
- package/dist/chunk-RUMU3UA6.cjs.map +0 -1
- package/dist/chunk-T64C7ZD7.cjs +0 -1
- package/dist/chunk-T64C7ZD7.cjs.map +0 -1
- package/dist/chunk-XEBG3G53.js +0 -498
- package/dist/chunk-XEBG3G53.js.map +0 -1
- package/dist/chunk-YLEMGEFH.js +0 -246
- package/dist/chunk-YLEMGEFH.js.map +0 -1
- package/dist/chunk-ZEJR4TKL.cjs +0 -173
- package/dist/chunk-ZEJR4TKL.cjs.map +0 -1
- package/dist/function-BtiZb9i9.d.ts +0 -208
- package/dist/function-DNugAOrt.d.cts +0 -208
- package/dist/index-6VR8-kit.d.ts +0 -143
- package/dist/index-70p168VM.d.ts +0 -192
- package/dist/index-Be74mcon.d.cts +0 -152
- package/dist/index-BtXT6UQ0.d.cts +0 -192
- package/dist/index-C5UBbQ4G.d.ts +0 -152
- package/dist/index-C83mZI1P.d.ts +0 -242
- package/dist/index-DXd9JikN.d.cts +0 -246
- package/dist/index-DXd9JikN.d.ts +0 -246
- package/dist/index-OG65ocOr.d.cts +0 -272
- package/dist/index-OG65ocOr.d.ts +0 -272
- package/dist/index-tJhkmQgE.d.cts +0 -143
- package/dist/index-y_ak9_Hi.d.cts +0 -242
- package/dist/styled-DIRkk9Uz.d.ts +0 -506
- package/dist/styled-DJbkJQfj.d.cts +0 -506
- package/dist/types-BN6_Q9Kl.d.cts +0 -39
- package/dist/types-BN6_Q9Kl.d.ts +0 -39
- /package/dist/{chunk-N6IHQUOX.js.map → chunk-HFPS7JFD.js.map} +0 -0
package/dist/model/index.js
CHANGED
|
@@ -1,20 +1,52 @@
|
|
|
1
1
|
import {
|
|
2
|
+
Arrangement,
|
|
2
3
|
Chord,
|
|
4
|
+
Composer,
|
|
5
|
+
Duration,
|
|
6
|
+
Instrument,
|
|
3
7
|
Interval,
|
|
4
8
|
Key,
|
|
9
|
+
Meter,
|
|
10
|
+
Motif,
|
|
5
11
|
Note,
|
|
6
|
-
Progression
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
12
|
+
Progression,
|
|
13
|
+
Rhythm,
|
|
14
|
+
Score,
|
|
15
|
+
Tempo,
|
|
16
|
+
Timeline,
|
|
17
|
+
Tuning,
|
|
18
|
+
Voicing
|
|
19
|
+
} from "../chunk-4A2RMUCC.js";
|
|
20
|
+
import "../chunk-GOZG57ER.js";
|
|
21
|
+
import "../chunk-YH3BIKHH.js";
|
|
22
|
+
import "../chunk-VOHZB45S.js";
|
|
23
|
+
import "../chunk-H42UHPYW.js";
|
|
24
|
+
import "../chunk-FVSI36UV.js";
|
|
25
|
+
import "../chunk-ELCTZUJJ.js";
|
|
26
|
+
import "../chunk-H3VORETI.js";
|
|
27
|
+
import "../chunk-KNJEKLKV.js";
|
|
28
|
+
import {
|
|
29
|
+
NoteSafety
|
|
30
|
+
} from "../chunk-INHVXP6Z.js";
|
|
31
|
+
import "../chunk-VTI6FOLB.js";
|
|
13
32
|
export {
|
|
33
|
+
Arrangement,
|
|
14
34
|
Chord,
|
|
35
|
+
Composer,
|
|
36
|
+
Duration,
|
|
37
|
+
Instrument,
|
|
15
38
|
Interval,
|
|
16
39
|
Key,
|
|
40
|
+
Meter,
|
|
41
|
+
Motif,
|
|
17
42
|
Note,
|
|
18
|
-
|
|
43
|
+
NoteSafety,
|
|
44
|
+
Progression,
|
|
45
|
+
Rhythm,
|
|
46
|
+
Score,
|
|
47
|
+
Tempo,
|
|
48
|
+
Timeline,
|
|
49
|
+
Tuning,
|
|
50
|
+
Voicing
|
|
19
51
|
};
|
|
20
52
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { a as Articulation } from './index-B9MNZboQ.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Every limb a percussionist strikes with, in declaration order.
|
|
5
|
+
*
|
|
6
|
+
* @category Core
|
|
7
|
+
*/
|
|
8
|
+
declare const LIMBS: readonly ["rightHand", "leftHand", "rightFoot", "leftFoot"];
|
|
9
|
+
/**
|
|
10
|
+
* One limb of a percussionist.
|
|
11
|
+
*
|
|
12
|
+
* @category Core
|
|
13
|
+
*/
|
|
14
|
+
type Limb = (typeof LIMBS)[number];
|
|
15
|
+
/**
|
|
16
|
+
* Fields every instrument profile carries, whatever its family.
|
|
17
|
+
*
|
|
18
|
+
* @category Core
|
|
19
|
+
*/
|
|
20
|
+
type InstrumentProfileCommon = {
|
|
21
|
+
/** Instrument name, for issue messages and for the caller's own display. */
|
|
22
|
+
name: string;
|
|
23
|
+
/** Techniques this instrument can produce; anything else it cannot play. */
|
|
24
|
+
articulations: readonly Articulation[];
|
|
25
|
+
/** Greatest number of notes that can sound at once. */
|
|
26
|
+
polyphony: number;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* A fretted or fretless string instrument, described by what it is rather than
|
|
30
|
+
* by the range it happens to have.
|
|
31
|
+
*
|
|
32
|
+
* The sounding range follows from `tuning` and `frets`: string `s` sounds
|
|
33
|
+
* `tuning[s] .. tuning[s] + frets`. Storing `[min, max]` instead would make a
|
|
34
|
+
* five-string bass, a drop tuning, a seven-string guitar and an extended-range
|
|
35
|
+
* neck four unrelated special cases, and would leave "the neck is not long
|
|
36
|
+
* enough" indistinguishable from "the note is below the instrument".
|
|
37
|
+
*
|
|
38
|
+
* @category Core
|
|
39
|
+
*/
|
|
40
|
+
type StringedProfile = InstrumentProfileCommon & {
|
|
41
|
+
kind: 'stringed';
|
|
42
|
+
/** Open-string MIDI pitch per string, lowest-sounding string first. */
|
|
43
|
+
tuning: readonly number[];
|
|
44
|
+
/** Highest fret number; fret 0 is the open string. */
|
|
45
|
+
frets: number;
|
|
46
|
+
/** Greatest fret span one hand can hold down at once. */
|
|
47
|
+
maxStretch: number;
|
|
48
|
+
/**
|
|
49
|
+
* The neck has no frets. Positions are continuous, so intonation is the
|
|
50
|
+
* player's and a slide is unbroken; in twelve-tone equal temperament the
|
|
51
|
+
* positions coincide with fret numbers, so range and stretch are read the
|
|
52
|
+
* same way.
|
|
53
|
+
*/
|
|
54
|
+
fretless?: boolean;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* A percussion setup, described by the voices within reach of each limb.
|
|
58
|
+
*
|
|
59
|
+
* A kit's constraint is not range but limbs: a voice no limb can reach is not
|
|
60
|
+
* on the instrument, and two voices needing the same limb at the same instant
|
|
61
|
+
* cannot both sound.
|
|
62
|
+
*
|
|
63
|
+
* @category Core
|
|
64
|
+
*/
|
|
65
|
+
type PercussionProfile = InstrumentProfileCommon & {
|
|
66
|
+
kind: 'percussion';
|
|
67
|
+
/** The limbs the player has available. */
|
|
68
|
+
limbs: readonly Limb[];
|
|
69
|
+
/** Limbs that can strike each voice, in preference order, keyed by MIDI note. */
|
|
70
|
+
reach: Readonly<Record<number, readonly Limb[]>>;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* An instrument the library can check a passage against.
|
|
74
|
+
*
|
|
75
|
+
* @category Core
|
|
76
|
+
*/
|
|
77
|
+
type InstrumentProfile = StringedProfile | PercussionProfile;
|
|
78
|
+
/**
|
|
79
|
+
* Anything that names an instrument: a plain {@link InstrumentProfile}, or a
|
|
80
|
+
* value that serializes to one such as the `Instrument` class.
|
|
81
|
+
*
|
|
82
|
+
* Every entry point that takes an instrument takes this, so a caller holding
|
|
83
|
+
* the class hands it over as it is instead of unwrapping it for one call and
|
|
84
|
+
* wrapping it again for the next.
|
|
85
|
+
*
|
|
86
|
+
* @category Core
|
|
87
|
+
*/
|
|
88
|
+
type InstrumentProfileLike = InstrumentProfile | {
|
|
89
|
+
/** The profile data this value stands for. */
|
|
90
|
+
toJSON(): InstrumentProfile;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* A fretting-hand position: which string, and how far up the neck.
|
|
94
|
+
*
|
|
95
|
+
* @category Core
|
|
96
|
+
*/
|
|
97
|
+
type StringFingering = {
|
|
98
|
+
/** Index into the `tuning` of a {@link StringedProfile}. */
|
|
99
|
+
string: number;
|
|
100
|
+
/** Fret number; 0 is the open string. */
|
|
101
|
+
fret: number;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* The lowest and highest pitch an instrument sounds.
|
|
105
|
+
*
|
|
106
|
+
* For a string instrument this is derived from the tuning and the fret count,
|
|
107
|
+
* never stored; for a kit it is the extent of the voices the player's limbs
|
|
108
|
+
* reach. The range is not necessarily gapless — use {@link canSound} to ask
|
|
109
|
+
* about one pitch.
|
|
110
|
+
*
|
|
111
|
+
* @param profile The instrument.
|
|
112
|
+
* @returns The extreme sounding pitches, inclusive.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```ts
|
|
116
|
+
* import { BASS_5_STRING, instrumentRange } from '@libraz/libcantus';
|
|
117
|
+
* instrumentRange(BASS_5_STRING).low; // 23, low B
|
|
118
|
+
* ```
|
|
119
|
+
* @category Core
|
|
120
|
+
*/
|
|
121
|
+
declare function instrumentRange(profile: InstrumentProfile): {
|
|
122
|
+
low: number;
|
|
123
|
+
high: number;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Whether the instrument has this pitch at all.
|
|
127
|
+
*
|
|
128
|
+
* This is the first of the three playability layers: no amount of skill or
|
|
129
|
+
* rehearsal puts a note on an instrument that has no position for it.
|
|
130
|
+
*
|
|
131
|
+
* @param profile The instrument.
|
|
132
|
+
* @param pitch MIDI pitch.
|
|
133
|
+
* @returns True when some string and fret, or some limb the player has,
|
|
134
|
+
* produces it.
|
|
135
|
+
* @category Core
|
|
136
|
+
*/
|
|
137
|
+
declare function canSound(profile: InstrumentProfile, pitch: number): boolean;
|
|
138
|
+
/**
|
|
139
|
+
* Every position on the neck that sounds a pitch, lowest string first.
|
|
140
|
+
*
|
|
141
|
+
* @param profile The instrument.
|
|
142
|
+
* @param pitch MIDI pitch.
|
|
143
|
+
* @returns One entry per string that reaches the pitch; empty when none does.
|
|
144
|
+
* @category Core
|
|
145
|
+
*/
|
|
146
|
+
declare function fingeringsFor(profile: StringedProfile, pitch: number): StringFingering[];
|
|
147
|
+
/**
|
|
148
|
+
* Move a pitch by whole octaves until the instrument can sound it.
|
|
149
|
+
*
|
|
150
|
+
* This is what a player does with a line written below the instrument: the note
|
|
151
|
+
* comes back an octave up, keeping its pitch class and its place in the phrase.
|
|
152
|
+
* Substituting a different scale degree, or refusing the passage outright, is
|
|
153
|
+
* the wrong answer for a bass line.
|
|
154
|
+
*
|
|
155
|
+
* @param pitch MIDI pitch to place.
|
|
156
|
+
* @param profile The instrument.
|
|
157
|
+
* @returns The nearest octave transposition the instrument sounds, searching
|
|
158
|
+
* upward first; the pitch unchanged when no transposition of it is available.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* import { BASS_4_STRING, foldIntoRange } from '@libraz/libcantus';
|
|
163
|
+
* foldIntoRange(27, BASS_4_STRING); // 39 — Eb1 is under the low E
|
|
164
|
+
* ```
|
|
165
|
+
* @category Core
|
|
166
|
+
*/
|
|
167
|
+
declare function foldIntoRange(pitch: number, profile: InstrumentProfile): number;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Hierarchical seeds: one project number that addresses every part.
|
|
171
|
+
*
|
|
172
|
+
* A flat seed per generator makes the caller keep a table of numbers and,
|
|
173
|
+
* worse, makes "reroll the drums" mean "pick a number nobody else is using".
|
|
174
|
+
* Deriving instead — `deriveSeed(project, 'drums', 'chorus', 2)` — turns a
|
|
175
|
+
* reroll into an extra path segment, and every other part keeps the seed it
|
|
176
|
+
* had because its path did not change.
|
|
177
|
+
*/
|
|
178
|
+
/**
|
|
179
|
+
* The address of one derivation: a namespace, and whatever names or indices
|
|
180
|
+
* locate the thing being seeded.
|
|
181
|
+
*
|
|
182
|
+
* Strings and numbers are kept apart by the hash, so `'3'` and `3` address
|
|
183
|
+
* different things, as do `['ab', 'c']` and `['a', 'bc']`.
|
|
184
|
+
*
|
|
185
|
+
* @category Utilities
|
|
186
|
+
*/
|
|
187
|
+
type SeedPath = readonly (string | number)[];
|
|
188
|
+
/**
|
|
189
|
+
* Derive a child seed from a root seed and a path.
|
|
190
|
+
*
|
|
191
|
+
* The result is a 32-bit integer, so it can be passed straight to
|
|
192
|
+
* {@link createRng}. Derivation is a pure function of its arguments — the same
|
|
193
|
+
* root and path give the same seed in every runtime — and near-identical paths
|
|
194
|
+
* (`'drums'` against `'drum'`, bar 1 against bar 2) give unrelated seeds: the
|
|
195
|
+
* path is hashed with FNV-1a and finished with the MurmurHash3 fmix32
|
|
196
|
+
* avalanche step.
|
|
197
|
+
*
|
|
198
|
+
* @param root The project seed; an integer in 0..0xffffffff.
|
|
199
|
+
* @param path The address to derive at; string or finite number segments.
|
|
200
|
+
* @returns The derived seed, an integer in 0..0xffffffff.
|
|
201
|
+
* @throws If the root is not a 32-bit integer, or a segment is neither a
|
|
202
|
+
* string nor a finite number.
|
|
203
|
+
* @example
|
|
204
|
+
* ```ts
|
|
205
|
+
* import { createRng, deriveSeed } from '@libraz/libcantus';
|
|
206
|
+
* const drums = deriveSeed(42, 'drums', 'chorus', 3);
|
|
207
|
+
* const rng = createRng(drums); // rerolling only the drums: bump the 3
|
|
208
|
+
* ```
|
|
209
|
+
* @category Utilities
|
|
210
|
+
*/
|
|
211
|
+
declare function deriveSeed(root: number, ...path: SeedPath): number;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Position-addressed randomness and threshold sampling.
|
|
215
|
+
*
|
|
216
|
+
* Drawing in call order makes a generator's output depend on how many draws
|
|
217
|
+
* preceded it, so editing a parameter in the middle of a piece redraws
|
|
218
|
+
* everything after it. Addressing each draw by where it happens — bar, beat,
|
|
219
|
+
* voice — removes that coupling: the value at a position depends on the
|
|
220
|
+
* position alone, so a change anywhere leaves every other position as it was.
|
|
221
|
+
*
|
|
222
|
+
* Deciding inclusion by comparing a fixed positional draw against a complexity
|
|
223
|
+
* dial gives the property a slider needs: raising the dial only adds events,
|
|
224
|
+
* and everything already sounding stays where it was.
|
|
225
|
+
*/
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* A random source addressed by position rather than by call order.
|
|
229
|
+
*
|
|
230
|
+
* @category Utilities
|
|
231
|
+
*/
|
|
232
|
+
type PositionalRng = {
|
|
233
|
+
/**
|
|
234
|
+
* The draw belonging to a position: a float in [0, 1) that depends only on
|
|
235
|
+
* the seed and the path, so it is the same however often and in whatever
|
|
236
|
+
* order it is asked for.
|
|
237
|
+
*/
|
|
238
|
+
at: (...path: SeedPath) => number;
|
|
239
|
+
};
|
|
240
|
+
/**
|
|
241
|
+
* Create a position-addressed random source.
|
|
242
|
+
*
|
|
243
|
+
* `at` holds no cursor: it is a pure function of the seed and the path, safe to
|
|
244
|
+
* call in any order, any number of times, from anywhere in a generator. Its
|
|
245
|
+
* draws are independent of {@link deriveSeed} at the same path, so a generator
|
|
246
|
+
* may seed a sub-part and take a positional draw at one position without
|
|
247
|
+
* getting the same number twice.
|
|
248
|
+
*
|
|
249
|
+
* @param seed The seed; an integer in 0..0xffffffff, typically from
|
|
250
|
+
* {@link deriveSeed}.
|
|
251
|
+
* @returns The positional source.
|
|
252
|
+
* @throws If the seed is not a 32-bit integer.
|
|
253
|
+
* @example
|
|
254
|
+
* ```ts
|
|
255
|
+
* import { createPositionalRng, deriveSeed } from '@libraz/libcantus';
|
|
256
|
+
* const rng = createPositionalRng(deriveSeed(42, 'drums'));
|
|
257
|
+
* rng.at('ghost', 4, 2.5); // the same value however often it is asked for
|
|
258
|
+
* ```
|
|
259
|
+
* @category Utilities
|
|
260
|
+
*/
|
|
261
|
+
declare function createPositionalRng(seed: number): PositionalRng;
|
|
262
|
+
/**
|
|
263
|
+
* Whether the event at a position belongs in the output at a given complexity.
|
|
264
|
+
*
|
|
265
|
+
* The test is `at(...path) < complexity`, and because the draw is fixed by the
|
|
266
|
+
* position, the selected set grows monotonically with complexity: for any
|
|
267
|
+
* `c1 < c2` the events at `c1` are a subset of the events at `c2` over the same
|
|
268
|
+
* seed and the same path space. Complexity 0 selects nothing and complexity 1
|
|
269
|
+
* selects everything.
|
|
270
|
+
*
|
|
271
|
+
* @param rng The positional source; may be a caller-supplied one.
|
|
272
|
+
* @param complexity The dial, in [0, 1].
|
|
273
|
+
* @param path The position to test.
|
|
274
|
+
* @returns True when the position is included.
|
|
275
|
+
* @throws If the complexity is outside [0, 1], or the source returns a value
|
|
276
|
+
* outside [0, 1).
|
|
277
|
+
* @example
|
|
278
|
+
* ```ts
|
|
279
|
+
* import { createPositionalRng, includeAt } from '@libraz/libcantus';
|
|
280
|
+
* const rng = createPositionalRng(7);
|
|
281
|
+
* includeAt(rng, 0.3, 'ghost', 4, 2.5); // raising 0.3 only adds hits
|
|
282
|
+
* ```
|
|
283
|
+
* @category Utilities
|
|
284
|
+
*/
|
|
285
|
+
declare function includeAt(rng: PositionalRng, complexity: number, ...path: SeedPath): boolean;
|
|
286
|
+
|
|
287
|
+
export { type InstrumentProfile as I, LIMBS as L, type PercussionProfile as P, type SeedPath as S, type InstrumentProfileCommon as a, type InstrumentProfileLike as b, type Limb as c, type PositionalRng as d, type StringFingering as e, type StringedProfile as f, canSound as g, createPositionalRng as h, deriveSeed as i, fingeringsFor as j, foldIntoRange as k, includeAt as l, instrumentRange as m };
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { a as Articulation } from './index-B9MNZboQ.cjs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Every limb a percussionist strikes with, in declaration order.
|
|
5
|
+
*
|
|
6
|
+
* @category Core
|
|
7
|
+
*/
|
|
8
|
+
declare const LIMBS: readonly ["rightHand", "leftHand", "rightFoot", "leftFoot"];
|
|
9
|
+
/**
|
|
10
|
+
* One limb of a percussionist.
|
|
11
|
+
*
|
|
12
|
+
* @category Core
|
|
13
|
+
*/
|
|
14
|
+
type Limb = (typeof LIMBS)[number];
|
|
15
|
+
/**
|
|
16
|
+
* Fields every instrument profile carries, whatever its family.
|
|
17
|
+
*
|
|
18
|
+
* @category Core
|
|
19
|
+
*/
|
|
20
|
+
type InstrumentProfileCommon = {
|
|
21
|
+
/** Instrument name, for issue messages and for the caller's own display. */
|
|
22
|
+
name: string;
|
|
23
|
+
/** Techniques this instrument can produce; anything else it cannot play. */
|
|
24
|
+
articulations: readonly Articulation[];
|
|
25
|
+
/** Greatest number of notes that can sound at once. */
|
|
26
|
+
polyphony: number;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* A fretted or fretless string instrument, described by what it is rather than
|
|
30
|
+
* by the range it happens to have.
|
|
31
|
+
*
|
|
32
|
+
* The sounding range follows from `tuning` and `frets`: string `s` sounds
|
|
33
|
+
* `tuning[s] .. tuning[s] + frets`. Storing `[min, max]` instead would make a
|
|
34
|
+
* five-string bass, a drop tuning, a seven-string guitar and an extended-range
|
|
35
|
+
* neck four unrelated special cases, and would leave "the neck is not long
|
|
36
|
+
* enough" indistinguishable from "the note is below the instrument".
|
|
37
|
+
*
|
|
38
|
+
* @category Core
|
|
39
|
+
*/
|
|
40
|
+
type StringedProfile = InstrumentProfileCommon & {
|
|
41
|
+
kind: 'stringed';
|
|
42
|
+
/** Open-string MIDI pitch per string, lowest-sounding string first. */
|
|
43
|
+
tuning: readonly number[];
|
|
44
|
+
/** Highest fret number; fret 0 is the open string. */
|
|
45
|
+
frets: number;
|
|
46
|
+
/** Greatest fret span one hand can hold down at once. */
|
|
47
|
+
maxStretch: number;
|
|
48
|
+
/**
|
|
49
|
+
* The neck has no frets. Positions are continuous, so intonation is the
|
|
50
|
+
* player's and a slide is unbroken; in twelve-tone equal temperament the
|
|
51
|
+
* positions coincide with fret numbers, so range and stretch are read the
|
|
52
|
+
* same way.
|
|
53
|
+
*/
|
|
54
|
+
fretless?: boolean;
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* A percussion setup, described by the voices within reach of each limb.
|
|
58
|
+
*
|
|
59
|
+
* A kit's constraint is not range but limbs: a voice no limb can reach is not
|
|
60
|
+
* on the instrument, and two voices needing the same limb at the same instant
|
|
61
|
+
* cannot both sound.
|
|
62
|
+
*
|
|
63
|
+
* @category Core
|
|
64
|
+
*/
|
|
65
|
+
type PercussionProfile = InstrumentProfileCommon & {
|
|
66
|
+
kind: 'percussion';
|
|
67
|
+
/** The limbs the player has available. */
|
|
68
|
+
limbs: readonly Limb[];
|
|
69
|
+
/** Limbs that can strike each voice, in preference order, keyed by MIDI note. */
|
|
70
|
+
reach: Readonly<Record<number, readonly Limb[]>>;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* An instrument the library can check a passage against.
|
|
74
|
+
*
|
|
75
|
+
* @category Core
|
|
76
|
+
*/
|
|
77
|
+
type InstrumentProfile = StringedProfile | PercussionProfile;
|
|
78
|
+
/**
|
|
79
|
+
* Anything that names an instrument: a plain {@link InstrumentProfile}, or a
|
|
80
|
+
* value that serializes to one such as the `Instrument` class.
|
|
81
|
+
*
|
|
82
|
+
* Every entry point that takes an instrument takes this, so a caller holding
|
|
83
|
+
* the class hands it over as it is instead of unwrapping it for one call and
|
|
84
|
+
* wrapping it again for the next.
|
|
85
|
+
*
|
|
86
|
+
* @category Core
|
|
87
|
+
*/
|
|
88
|
+
type InstrumentProfileLike = InstrumentProfile | {
|
|
89
|
+
/** The profile data this value stands for. */
|
|
90
|
+
toJSON(): InstrumentProfile;
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* A fretting-hand position: which string, and how far up the neck.
|
|
94
|
+
*
|
|
95
|
+
* @category Core
|
|
96
|
+
*/
|
|
97
|
+
type StringFingering = {
|
|
98
|
+
/** Index into the `tuning` of a {@link StringedProfile}. */
|
|
99
|
+
string: number;
|
|
100
|
+
/** Fret number; 0 is the open string. */
|
|
101
|
+
fret: number;
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* The lowest and highest pitch an instrument sounds.
|
|
105
|
+
*
|
|
106
|
+
* For a string instrument this is derived from the tuning and the fret count,
|
|
107
|
+
* never stored; for a kit it is the extent of the voices the player's limbs
|
|
108
|
+
* reach. The range is not necessarily gapless — use {@link canSound} to ask
|
|
109
|
+
* about one pitch.
|
|
110
|
+
*
|
|
111
|
+
* @param profile The instrument.
|
|
112
|
+
* @returns The extreme sounding pitches, inclusive.
|
|
113
|
+
*
|
|
114
|
+
* @example
|
|
115
|
+
* ```ts
|
|
116
|
+
* import { BASS_5_STRING, instrumentRange } from '@libraz/libcantus';
|
|
117
|
+
* instrumentRange(BASS_5_STRING).low; // 23, low B
|
|
118
|
+
* ```
|
|
119
|
+
* @category Core
|
|
120
|
+
*/
|
|
121
|
+
declare function instrumentRange(profile: InstrumentProfile): {
|
|
122
|
+
low: number;
|
|
123
|
+
high: number;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Whether the instrument has this pitch at all.
|
|
127
|
+
*
|
|
128
|
+
* This is the first of the three playability layers: no amount of skill or
|
|
129
|
+
* rehearsal puts a note on an instrument that has no position for it.
|
|
130
|
+
*
|
|
131
|
+
* @param profile The instrument.
|
|
132
|
+
* @param pitch MIDI pitch.
|
|
133
|
+
* @returns True when some string and fret, or some limb the player has,
|
|
134
|
+
* produces it.
|
|
135
|
+
* @category Core
|
|
136
|
+
*/
|
|
137
|
+
declare function canSound(profile: InstrumentProfile, pitch: number): boolean;
|
|
138
|
+
/**
|
|
139
|
+
* Every position on the neck that sounds a pitch, lowest string first.
|
|
140
|
+
*
|
|
141
|
+
* @param profile The instrument.
|
|
142
|
+
* @param pitch MIDI pitch.
|
|
143
|
+
* @returns One entry per string that reaches the pitch; empty when none does.
|
|
144
|
+
* @category Core
|
|
145
|
+
*/
|
|
146
|
+
declare function fingeringsFor(profile: StringedProfile, pitch: number): StringFingering[];
|
|
147
|
+
/**
|
|
148
|
+
* Move a pitch by whole octaves until the instrument can sound it.
|
|
149
|
+
*
|
|
150
|
+
* This is what a player does with a line written below the instrument: the note
|
|
151
|
+
* comes back an octave up, keeping its pitch class and its place in the phrase.
|
|
152
|
+
* Substituting a different scale degree, or refusing the passage outright, is
|
|
153
|
+
* the wrong answer for a bass line.
|
|
154
|
+
*
|
|
155
|
+
* @param pitch MIDI pitch to place.
|
|
156
|
+
* @param profile The instrument.
|
|
157
|
+
* @returns The nearest octave transposition the instrument sounds, searching
|
|
158
|
+
* upward first; the pitch unchanged when no transposition of it is available.
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```ts
|
|
162
|
+
* import { BASS_4_STRING, foldIntoRange } from '@libraz/libcantus';
|
|
163
|
+
* foldIntoRange(27, BASS_4_STRING); // 39 — Eb1 is under the low E
|
|
164
|
+
* ```
|
|
165
|
+
* @category Core
|
|
166
|
+
*/
|
|
167
|
+
declare function foldIntoRange(pitch: number, profile: InstrumentProfile): number;
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Hierarchical seeds: one project number that addresses every part.
|
|
171
|
+
*
|
|
172
|
+
* A flat seed per generator makes the caller keep a table of numbers and,
|
|
173
|
+
* worse, makes "reroll the drums" mean "pick a number nobody else is using".
|
|
174
|
+
* Deriving instead — `deriveSeed(project, 'drums', 'chorus', 2)` — turns a
|
|
175
|
+
* reroll into an extra path segment, and every other part keeps the seed it
|
|
176
|
+
* had because its path did not change.
|
|
177
|
+
*/
|
|
178
|
+
/**
|
|
179
|
+
* The address of one derivation: a namespace, and whatever names or indices
|
|
180
|
+
* locate the thing being seeded.
|
|
181
|
+
*
|
|
182
|
+
* Strings and numbers are kept apart by the hash, so `'3'` and `3` address
|
|
183
|
+
* different things, as do `['ab', 'c']` and `['a', 'bc']`.
|
|
184
|
+
*
|
|
185
|
+
* @category Utilities
|
|
186
|
+
*/
|
|
187
|
+
type SeedPath = readonly (string | number)[];
|
|
188
|
+
/**
|
|
189
|
+
* Derive a child seed from a root seed and a path.
|
|
190
|
+
*
|
|
191
|
+
* The result is a 32-bit integer, so it can be passed straight to
|
|
192
|
+
* {@link createRng}. Derivation is a pure function of its arguments — the same
|
|
193
|
+
* root and path give the same seed in every runtime — and near-identical paths
|
|
194
|
+
* (`'drums'` against `'drum'`, bar 1 against bar 2) give unrelated seeds: the
|
|
195
|
+
* path is hashed with FNV-1a and finished with the MurmurHash3 fmix32
|
|
196
|
+
* avalanche step.
|
|
197
|
+
*
|
|
198
|
+
* @param root The project seed; an integer in 0..0xffffffff.
|
|
199
|
+
* @param path The address to derive at; string or finite number segments.
|
|
200
|
+
* @returns The derived seed, an integer in 0..0xffffffff.
|
|
201
|
+
* @throws If the root is not a 32-bit integer, or a segment is neither a
|
|
202
|
+
* string nor a finite number.
|
|
203
|
+
* @example
|
|
204
|
+
* ```ts
|
|
205
|
+
* import { createRng, deriveSeed } from '@libraz/libcantus';
|
|
206
|
+
* const drums = deriveSeed(42, 'drums', 'chorus', 3);
|
|
207
|
+
* const rng = createRng(drums); // rerolling only the drums: bump the 3
|
|
208
|
+
* ```
|
|
209
|
+
* @category Utilities
|
|
210
|
+
*/
|
|
211
|
+
declare function deriveSeed(root: number, ...path: SeedPath): number;
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Position-addressed randomness and threshold sampling.
|
|
215
|
+
*
|
|
216
|
+
* Drawing in call order makes a generator's output depend on how many draws
|
|
217
|
+
* preceded it, so editing a parameter in the middle of a piece redraws
|
|
218
|
+
* everything after it. Addressing each draw by where it happens — bar, beat,
|
|
219
|
+
* voice — removes that coupling: the value at a position depends on the
|
|
220
|
+
* position alone, so a change anywhere leaves every other position as it was.
|
|
221
|
+
*
|
|
222
|
+
* Deciding inclusion by comparing a fixed positional draw against a complexity
|
|
223
|
+
* dial gives the property a slider needs: raising the dial only adds events,
|
|
224
|
+
* and everything already sounding stays where it was.
|
|
225
|
+
*/
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* A random source addressed by position rather than by call order.
|
|
229
|
+
*
|
|
230
|
+
* @category Utilities
|
|
231
|
+
*/
|
|
232
|
+
type PositionalRng = {
|
|
233
|
+
/**
|
|
234
|
+
* The draw belonging to a position: a float in [0, 1) that depends only on
|
|
235
|
+
* the seed and the path, so it is the same however often and in whatever
|
|
236
|
+
* order it is asked for.
|
|
237
|
+
*/
|
|
238
|
+
at: (...path: SeedPath) => number;
|
|
239
|
+
};
|
|
240
|
+
/**
|
|
241
|
+
* Create a position-addressed random source.
|
|
242
|
+
*
|
|
243
|
+
* `at` holds no cursor: it is a pure function of the seed and the path, safe to
|
|
244
|
+
* call in any order, any number of times, from anywhere in a generator. Its
|
|
245
|
+
* draws are independent of {@link deriveSeed} at the same path, so a generator
|
|
246
|
+
* may seed a sub-part and take a positional draw at one position without
|
|
247
|
+
* getting the same number twice.
|
|
248
|
+
*
|
|
249
|
+
* @param seed The seed; an integer in 0..0xffffffff, typically from
|
|
250
|
+
* {@link deriveSeed}.
|
|
251
|
+
* @returns The positional source.
|
|
252
|
+
* @throws If the seed is not a 32-bit integer.
|
|
253
|
+
* @example
|
|
254
|
+
* ```ts
|
|
255
|
+
* import { createPositionalRng, deriveSeed } from '@libraz/libcantus';
|
|
256
|
+
* const rng = createPositionalRng(deriveSeed(42, 'drums'));
|
|
257
|
+
* rng.at('ghost', 4, 2.5); // the same value however often it is asked for
|
|
258
|
+
* ```
|
|
259
|
+
* @category Utilities
|
|
260
|
+
*/
|
|
261
|
+
declare function createPositionalRng(seed: number): PositionalRng;
|
|
262
|
+
/**
|
|
263
|
+
* Whether the event at a position belongs in the output at a given complexity.
|
|
264
|
+
*
|
|
265
|
+
* The test is `at(...path) < complexity`, and because the draw is fixed by the
|
|
266
|
+
* position, the selected set grows monotonically with complexity: for any
|
|
267
|
+
* `c1 < c2` the events at `c1` are a subset of the events at `c2` over the same
|
|
268
|
+
* seed and the same path space. Complexity 0 selects nothing and complexity 1
|
|
269
|
+
* selects everything.
|
|
270
|
+
*
|
|
271
|
+
* @param rng The positional source; may be a caller-supplied one.
|
|
272
|
+
* @param complexity The dial, in [0, 1].
|
|
273
|
+
* @param path The position to test.
|
|
274
|
+
* @returns True when the position is included.
|
|
275
|
+
* @throws If the complexity is outside [0, 1], or the source returns a value
|
|
276
|
+
* outside [0, 1).
|
|
277
|
+
* @example
|
|
278
|
+
* ```ts
|
|
279
|
+
* import { createPositionalRng, includeAt } from '@libraz/libcantus';
|
|
280
|
+
* const rng = createPositionalRng(7);
|
|
281
|
+
* includeAt(rng, 0.3, 'ghost', 4, 2.5); // raising 0.3 only adds hits
|
|
282
|
+
* ```
|
|
283
|
+
* @category Utilities
|
|
284
|
+
*/
|
|
285
|
+
declare function includeAt(rng: PositionalRng, complexity: number, ...path: SeedPath): boolean;
|
|
286
|
+
|
|
287
|
+
export { type InstrumentProfile as I, LIMBS as L, type PercussionProfile as P, type SeedPath as S, type InstrumentProfileCommon as a, type InstrumentProfileLike as b, type Limb as c, type PositionalRng as d, type StringFingering as e, type StringedProfile as f, canSound as g, createPositionalRng as h, deriveSeed as i, fingeringsFor as j, foldIntoRange as k, includeAt as l, instrumentRange as m };
|