tonus 0.10.2 → 0.10.3
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/CHANGELOG.md +20 -0
- package/dist/engines/score/phrasing.js +41 -3
- package/dist/engines/score/types.d.ts +8 -0
- package/docs/api/score.md +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to tonus. Newest first.
|
|
4
4
|
|
|
5
|
+
## 0.10.3 — 2026-09-04
|
|
6
|
+
|
|
7
|
+
The close ritards.
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- **The close now ritards.** Two faults in the phrasing made every phrase
|
|
12
|
+
hurry into its cadence. The duration arch was a symmetric window, 0 at both
|
|
13
|
+
ends, so the second half of a phrase ran quicker than its middle; and the
|
|
14
|
+
cadence lengthened only the one note before the bar, by a term small enough
|
|
15
|
+
that the arch's fall ate it. A full close was held no longer than a comma,
|
|
16
|
+
and an ictus two notes earlier often outlasted the final note. The duration
|
|
17
|
+
arch now rises and holds — the subsiding is a lightening, not a hurrying —
|
|
18
|
+
and the `accentus` profile gains a rallentando: `ritard` (its depth),
|
|
19
|
+
`ritardNotes` (its width), `ritardCurve` (how late it gathers), graded by
|
|
20
|
+
the divisio ladder so a comma leans, a double bar broadens, a breath moves
|
|
21
|
+
nothing, with `finisBoost` on the piece's own last `::`. `shapedDuration`
|
|
22
|
+
changes on the second half of every phrase; `duration` and `velocity` do
|
|
23
|
+
not. Override `ritard: 0` to remove the window.
|
|
24
|
+
|
|
5
25
|
## 0.10.2 — 2026-09-02
|
|
6
26
|
|
|
7
27
|
A stub is not a chant.
|
|
@@ -6,6 +6,10 @@ const PHRASING_PROFILES = {
|
|
|
6
6
|
curve: 0.1,
|
|
7
7
|
accent: 0.5,
|
|
8
8
|
cadence: 0.1,
|
|
9
|
+
ritard: 0.25,
|
|
10
|
+
ritardNotes: 2,
|
|
11
|
+
ritardCurve: 1.6,
|
|
12
|
+
finisBoost: 1.5,
|
|
9
13
|
tenor: 2.3,
|
|
10
14
|
baseVelocity: 0.65,
|
|
11
15
|
contourVel: 0.08,
|
|
@@ -19,6 +23,10 @@ const PHRASING_PROFILES = {
|
|
|
19
23
|
curve: 0.35,
|
|
20
24
|
accent: 0.8,
|
|
21
25
|
cadence: 0.15,
|
|
26
|
+
ritard: 0.4,
|
|
27
|
+
ritardNotes: 3,
|
|
28
|
+
ritardCurve: 1.6,
|
|
29
|
+
finisBoost: 1.5,
|
|
22
30
|
tenor: 1.2,
|
|
23
31
|
baseVelocity: 0.68,
|
|
24
32
|
contourVel: 0.18,
|
|
@@ -32,6 +40,10 @@ const PHRASING_PROFILES = {
|
|
|
32
40
|
curve: 0.25,
|
|
33
41
|
accent: 0.6,
|
|
34
42
|
cadence: 0.12,
|
|
43
|
+
ritard: 0.35,
|
|
44
|
+
ritardNotes: 3,
|
|
45
|
+
ritardCurve: 1.6,
|
|
46
|
+
finisBoost: 1.5,
|
|
35
47
|
tenor: 0.9,
|
|
36
48
|
baseVelocity: 0.69,
|
|
37
49
|
contourVel: 0.14,
|
|
@@ -45,6 +57,10 @@ const PHRASING_PROFILES = {
|
|
|
45
57
|
curve: 0.5,
|
|
46
58
|
accent: 1,
|
|
47
59
|
cadence: 0.2,
|
|
60
|
+
ritard: 0.5,
|
|
61
|
+
ritardNotes: 4,
|
|
62
|
+
ritardCurve: 1.6,
|
|
63
|
+
finisBoost: 1.5,
|
|
48
64
|
tenor: 1.7,
|
|
49
65
|
baseVelocity: 0.71,
|
|
50
66
|
contourVel: 0.22,
|
|
@@ -207,8 +223,8 @@ export function applyPhrasing(events, profile, tenorPc) {
|
|
|
207
223
|
}
|
|
208
224
|
if (currentPhrase.length > 0)
|
|
209
225
|
phrases.push(currentPhrase);
|
|
210
|
-
for (
|
|
211
|
-
const noteEntries =
|
|
226
|
+
for (let p = 0; p < phrases.length; p++) {
|
|
227
|
+
const noteEntries = phrases[p];
|
|
212
228
|
if (noteEntries.length === 0)
|
|
213
229
|
continue;
|
|
214
230
|
let minArsis = Infinity, maxArsis = -Infinity;
|
|
@@ -260,14 +276,36 @@ export function applyPhrasing(events, profile, tenorPc) {
|
|
|
260
276
|
velocity *=
|
|
261
277
|
1 - profile.cadence * divisioStrength * CADENCE_VELOCITY_FACTOR;
|
|
262
278
|
const finalVelocity = Math.max(VELOCITY_MIN, Math.min(VELOCITY_MAX, velocity * profile.baseVelocity));
|
|
279
|
+
// The duration arch is one-sided: it rises with the velocity arch over
|
|
280
|
+
// the first half and then HOLDS. The grand rythme's subsiding is a
|
|
281
|
+
// lightening, not a hurrying — a symmetric window here made every phrase
|
|
282
|
+
// accelerate into its cadence.
|
|
283
|
+
const durArch = t < 0.5 ? arch : 1;
|
|
263
284
|
const baseDur = note.performance.duration;
|
|
264
285
|
let durFactor = DURATION_BASE_FACTOR +
|
|
265
286
|
arsisRelative * DURATION_ARSIS_FACTOR +
|
|
266
|
-
|
|
287
|
+
durArch * DURATION_ARCH_FACTOR;
|
|
267
288
|
durFactor += (contourRelative - VELOCITY_CENTER) * profile.contourDur;
|
|
268
289
|
if (note.context.ictus)
|
|
269
290
|
durFactor *= profile.ictusBoost;
|
|
270
291
|
durFactor += profile.cadence * divisioStrength * CADENCE_DURATION_FACTOR;
|
|
292
|
+
// The rallentando into the close. The cadence term above lengthens only
|
|
293
|
+
// the one note before the bar, so without a window a full close was held
|
|
294
|
+
// no longer than a comma, and an ictus two notes earlier often outlasted
|
|
295
|
+
// the final note. The window covers the phrase's last `ritardNotes` (or
|
|
296
|
+
// all of a shorter phrase), rising 0 → 1 toward the bar along a power
|
|
297
|
+
// curve; the divisio ladder grades it, so a comma leans, a double bar
|
|
298
|
+
// broadens, a breath moves nothing. The piece's own last bar is not just
|
|
299
|
+
// another double bar. A ritard is a tempo scaling, so it multiplies.
|
|
300
|
+
const closing = noteEntries[noteEntries.length - 1].nextDivisio;
|
|
301
|
+
const span = Math.min(Math.max(1, Math.round(profile.ritardNotes)), noteEntries.length);
|
|
302
|
+
const tail = order - (noteEntries.length - span);
|
|
303
|
+
if (tail >= 0) {
|
|
304
|
+
const ramp = ((tail + 1) / span) ** profile.ritardCurve;
|
|
305
|
+
const finis = p === phrases.length - 1 && closing === "::" ? profile.finisBoost : 1;
|
|
306
|
+
durFactor *=
|
|
307
|
+
1 + profile.ritard * getDivisioStrength(closing) * finis * ramp;
|
|
308
|
+
}
|
|
271
309
|
const shapedDuration = Math.max(DURATION_MIN, Math.min(DURATION_MAX, baseDur * durFactor));
|
|
272
310
|
shaped.push({
|
|
273
311
|
...note,
|
|
@@ -132,6 +132,14 @@ export interface PhrasingProfile {
|
|
|
132
132
|
curve: number;
|
|
133
133
|
accent: number;
|
|
134
134
|
cadence: number;
|
|
135
|
+
/** The rallentando into a close: how far its last notes broaden, graded by the bar. */
|
|
136
|
+
ritard: number;
|
|
137
|
+
/** How many closing notes the rallentando covers. */
|
|
138
|
+
ritardNotes: number;
|
|
139
|
+
/** The ramp's power: 1 is linear, above 1 gathers the broadening late, like a singer. */
|
|
140
|
+
ritardCurve: number;
|
|
141
|
+
/** A multiplier on the piece's own last `::` — the end of the chant over any other double bar. */
|
|
142
|
+
finisBoost: number;
|
|
135
143
|
tenor: number;
|
|
136
144
|
baseVelocity: number;
|
|
137
145
|
contourVel: number;
|
package/docs/api/score.md
CHANGED
|
@@ -208,6 +208,8 @@ The `accentus` profile (`PhrasingProfile`):
|
|
|
208
208
|
| `curve` | depth of the phrase-level velocity arch |
|
|
209
209
|
| `accent` | accent emphasis within the phrase |
|
|
210
210
|
| `cadence` | weight given to phrase-final cadences |
|
|
211
|
+
| `ritard`, `ritardNotes` | depth and width, in notes, of the rallentando |
|
|
212
|
+
| `ritardCurve`, `finisBoost` | how late it gathers; the piece's own last `::` |
|
|
211
213
|
| `tenor` | pull toward the reciting tone |
|
|
212
214
|
| `baseVelocity`, `velSpread` | the velocity floor and the dynamic range above it |
|
|
213
215
|
| `contourVel`, `contourDur` | melodic-contour influence on velocity and duration |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tonus",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Medieval music analysis: GABC plainchant exports, liturgical calendar, tuning systems, ephemeris, and the harmony of the spheres",
|
|
6
6
|
"author": "Jeffrey Pierce <jeffrey@jeffreypierce.net>",
|