@ugfoundation/swaralipi-js 0.7.4 → 0.7.6
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/dist/{batch-S6KXVZ7Z.js → batch-5NMEPJ6E.js} +4 -4
- package/dist/{batch-S6KXVZ7Z.js.map → batch-5NMEPJ6E.js.map} +1 -1
- package/dist/{chunk-NSZXESTN.js → chunk-ITOP5O5M.js} +3 -3
- package/dist/{chunk-NSZXESTN.js.map → chunk-ITOP5O5M.js.map} +1 -1
- package/dist/{chunk-ZDSF7Y33.js → chunk-NJGHFQJP.js} +3 -3
- package/dist/{chunk-ZDSF7Y33.js.map → chunk-NJGHFQJP.js.map} +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +2 -2
- package/dist/convert/index.cjs +1 -1
- package/dist/convert/index.cjs.map +1 -1
- package/dist/convert/index.js +2 -2
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/player/index.cjs +33 -4
- package/dist/player/index.cjs.map +1 -1
- package/dist/player/index.d.cts +22 -0
- package/dist/player/index.d.ts +22 -0
- package/dist/player/index.js +34 -5
- package/dist/player/index.js.map +1 -1
- package/dist/spec/index.cjs +1 -1
- package/dist/spec/index.cjs.map +1 -1
- package/dist/spec/index.d.cts +1 -1
- package/dist/spec/index.d.ts +1 -1
- package/dist/spec/index.js +1 -1
- package/dist/viewer/index.cjs +1 -1
- package/dist/viewer/index.cjs.map +1 -1
- package/package.json +1 -1
package/dist/player/index.d.cts
CHANGED
|
@@ -48,6 +48,15 @@ interface PlayerOptions {
|
|
|
48
48
|
onLimit?: () => void;
|
|
49
49
|
/** Called with the active note's `part.row.note` path, or null when idle. */
|
|
50
50
|
onHighlight?: (path: string | null) => void;
|
|
51
|
+
/** The same path, but fired on the AUDIO clock — Tone's worker ticker, the one
|
|
52
|
+
* that fires the notes themselves — so it keeps arriving while the page is
|
|
53
|
+
* hidden. `onHighlight` cannot: it runs on Tone.Draw, i.e.
|
|
54
|
+
* `requestAnimationFrame`, which stops the moment a screen locks, so anything
|
|
55
|
+
* hanging off it freezes until the reader looks at the page again. Use this for
|
|
56
|
+
* consumers that are not drawing (media-session metadata, analytics); it fires
|
|
57
|
+
* early — one transport lookahead (~0.1s) plus MEDIA_LEAD_SEC — and only in
|
|
58
|
+
* doc mode. */
|
|
59
|
+
onEvent?: (path: string | null) => void;
|
|
51
60
|
/** Taal mode only (`loadTaal`): called on the draw thread once per matra with
|
|
52
61
|
* the beat index within the cycle (0-based, `< numBeats`), so a consumer can
|
|
53
62
|
* highlight the active theka bol / drum-staff note. Never fired in doc mode. */
|
|
@@ -84,6 +93,19 @@ declare class SwaralipiPlayer {
|
|
|
84
93
|
get taalId(): TaalId | undefined;
|
|
85
94
|
/** Whether the effective rhythm will actually be a sampled tabla. */
|
|
86
95
|
get usesTabla(): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Length of one play-through in seconds, for a progress bar (the OS media
|
|
98
|
+
* session's, typically). Derived from the loaded notation on every read and
|
|
99
|
+
* never stored, because every input can move: re-engrave the document, drag
|
|
100
|
+
* the tempo, switch the rhythm off, and this changes with it. `Infinity` in
|
|
101
|
+
* taal mode (one avartan looping, no end — the value the Media Session spec
|
|
102
|
+
* reserves for exactly that), 0 with nothing loaded, and clamped by a preview
|
|
103
|
+
* cap when one is set, since that is when playback will really stop.
|
|
104
|
+
*/
|
|
105
|
+
get durationSec(): number;
|
|
106
|
+
/** Seconds played so far, off the Transport's own clock — so it holds while
|
|
107
|
+
* paused, and reads 0 before the first play and after stop(). */
|
|
108
|
+
get positionSec(): number;
|
|
87
109
|
/** Set the document to play. Stops any current playback. */
|
|
88
110
|
load(doc: Swaralipi): void;
|
|
89
111
|
/**
|
package/dist/player/index.d.ts
CHANGED
|
@@ -48,6 +48,15 @@ interface PlayerOptions {
|
|
|
48
48
|
onLimit?: () => void;
|
|
49
49
|
/** Called with the active note's `part.row.note` path, or null when idle. */
|
|
50
50
|
onHighlight?: (path: string | null) => void;
|
|
51
|
+
/** The same path, but fired on the AUDIO clock — Tone's worker ticker, the one
|
|
52
|
+
* that fires the notes themselves — so it keeps arriving while the page is
|
|
53
|
+
* hidden. `onHighlight` cannot: it runs on Tone.Draw, i.e.
|
|
54
|
+
* `requestAnimationFrame`, which stops the moment a screen locks, so anything
|
|
55
|
+
* hanging off it freezes until the reader looks at the page again. Use this for
|
|
56
|
+
* consumers that are not drawing (media-session metadata, analytics); it fires
|
|
57
|
+
* early — one transport lookahead (~0.1s) plus MEDIA_LEAD_SEC — and only in
|
|
58
|
+
* doc mode. */
|
|
59
|
+
onEvent?: (path: string | null) => void;
|
|
51
60
|
/** Taal mode only (`loadTaal`): called on the draw thread once per matra with
|
|
52
61
|
* the beat index within the cycle (0-based, `< numBeats`), so a consumer can
|
|
53
62
|
* highlight the active theka bol / drum-staff note. Never fired in doc mode. */
|
|
@@ -84,6 +93,19 @@ declare class SwaralipiPlayer {
|
|
|
84
93
|
get taalId(): TaalId | undefined;
|
|
85
94
|
/** Whether the effective rhythm will actually be a sampled tabla. */
|
|
86
95
|
get usesTabla(): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Length of one play-through in seconds, for a progress bar (the OS media
|
|
98
|
+
* session's, typically). Derived from the loaded notation on every read and
|
|
99
|
+
* never stored, because every input can move: re-engrave the document, drag
|
|
100
|
+
* the tempo, switch the rhythm off, and this changes with it. `Infinity` in
|
|
101
|
+
* taal mode (one avartan looping, no end — the value the Media Session spec
|
|
102
|
+
* reserves for exactly that), 0 with nothing loaded, and clamped by a preview
|
|
103
|
+
* cap when one is set, since that is when playback will really stop.
|
|
104
|
+
*/
|
|
105
|
+
get durationSec(): number;
|
|
106
|
+
/** Seconds played so far, off the Transport's own clock — so it holds while
|
|
107
|
+
* paused, and reads 0 before the first play and after stop(). */
|
|
108
|
+
get positionSec(): number;
|
|
87
109
|
/** Set the document to play. Stops any current playback. */
|
|
88
110
|
load(doc: Swaralipi): void;
|
|
89
111
|
/**
|
package/dist/player/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { DEFAULT_SOUNDS_BASE, INSTRUMENTS, SAMPLED_TAALS, instrumentSampleUrl, probeUrl, normalizeBase, nearestTablaBpm, tablaBpms,
|
|
1
|
+
import { DEFAULT_SOUNDS_BASE, INSTRUMENTS, SAMPLED_TAALS, buildSchedule, instrumentSampleUrl, probeUrl, normalizeBase, nearestTablaBpm, tablaBpms, quantizeLoopEnd, midiToSampleName, glideTargets, triggerGlide, createSampler, createBuffers, tablaUrl, tablaShiftSemitones, TABLA_GRAIN_OVERLAP_SEC, TABLA_GRAIN_SEC, createDrumKit, drumPattern, createMetronome, TANPURA_PCS, createTanpura, parseTonic, MEEND_HOLD, sCurve } from '../chunk-2SAZWTTK.js';
|
|
2
2
|
export { ARC_RAW_RE, DEFAULT_SOUNDS_BASE, DRUM_KIT_BASE, INSTRUMENTS, MEEND_HOLD, OCTAVE_OFFSET, SALAMANDER_BASE, SAMPLED_TAALS, SHUDDHA, TABLA_BPMS, TABLA_VARIATIONS, TONEJS_INSTRUMENTS_BASE, buildSchedule, createBuffers, drumPattern, glideTargets, instrumentSampleUrl, isArcRaw, meendContinuations, midiToSampleName, nearestSampleKey, nearestTablaBpm, normalizeBase, parseTonic, probeUrl, quantizeLoopEnd, sCurve, sampleFileName, santoorUrls, subsetUrls, svaraSemitone, svaraToMidi, tablaBpms, tablaUrl, triggerGlide } from '../chunk-2SAZWTTK.js';
|
|
3
3
|
import { TAALS } from '../chunk-UF5OSJRP.js';
|
|
4
4
|
|
|
5
5
|
// src/player/player.ts
|
|
6
6
|
var STOP_FADE_SEC = 0.5;
|
|
7
|
+
var MEDIA_LEAD_SEC = 0.25;
|
|
7
8
|
function midiRange(schedule) {
|
|
8
9
|
let lo = Infinity;
|
|
9
10
|
let hi = -Infinity;
|
|
@@ -17,6 +18,9 @@ function midiRange(schedule) {
|
|
|
17
18
|
}
|
|
18
19
|
return lo <= hi ? { lo, hi } : void 0;
|
|
19
20
|
}
|
|
21
|
+
function playDurationSec(schedule, spb, rhythm) {
|
|
22
|
+
return schedule.totalBeats * spb + (rhythm !== "none" ? schedule.pickupBeats * spb : 0) + spb;
|
|
23
|
+
}
|
|
20
24
|
async function loadedOrWarn(Tone) {
|
|
21
25
|
try {
|
|
22
26
|
await Tone.loaded();
|
|
@@ -63,6 +67,7 @@ var SwaralipiPlayer = class {
|
|
|
63
67
|
tanpura: options.tanpura ?? true,
|
|
64
68
|
previewLimitSec: options.previewLimitSec ?? Infinity,
|
|
65
69
|
onHighlight: options.onHighlight,
|
|
70
|
+
onEvent: options.onEvent,
|
|
66
71
|
onBeat: options.onBeat,
|
|
67
72
|
onState: options.onState,
|
|
68
73
|
onLimit: options.onLimit
|
|
@@ -96,6 +101,27 @@ var SwaralipiPlayer = class {
|
|
|
96
101
|
const id = this.#taalId();
|
|
97
102
|
return this.#opts.rhythm === "tabla" && id !== void 0 && SAMPLED_TAALS.has(id) && !!this.#opts.soundsBaseUrl;
|
|
98
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* Length of one play-through in seconds, for a progress bar (the OS media
|
|
106
|
+
* session's, typically). Derived from the loaded notation on every read and
|
|
107
|
+
* never stored, because every input can move: re-engrave the document, drag
|
|
108
|
+
* the tempo, switch the rhythm off, and this changes with it. `Infinity` in
|
|
109
|
+
* taal mode (one avartan looping, no end — the value the Media Session spec
|
|
110
|
+
* reserves for exactly that), 0 with nothing loaded, and clamped by a preview
|
|
111
|
+
* cap when one is set, since that is when playback will really stop.
|
|
112
|
+
*/
|
|
113
|
+
get durationSec() {
|
|
114
|
+
if (this.#taal !== void 0) return Infinity;
|
|
115
|
+
if (this.#doc === void 0) return 0;
|
|
116
|
+
const schedule = buildSchedule(this.#doc, { tonic: this.#opts.tonic, baseOctave: this.#opts.baseOctave });
|
|
117
|
+
const full = playDurationSec(schedule, 60 / this.#effectiveBpm(), this.#opts.rhythm);
|
|
118
|
+
return Math.min(full, this.#opts.previewLimitSec);
|
|
119
|
+
}
|
|
120
|
+
/** Seconds played so far, off the Transport's own clock — so it holds while
|
|
121
|
+
* paused, and reads 0 before the first play and after stop(). */
|
|
122
|
+
get positionSec() {
|
|
123
|
+
return this.#tone?.getTransport().seconds ?? 0;
|
|
124
|
+
}
|
|
99
125
|
/* ----------------------------------------------------------------- */
|
|
100
126
|
/* Loading + transport control */
|
|
101
127
|
/* ----------------------------------------------------------------- */
|
|
@@ -408,6 +434,7 @@ var SwaralipiPlayer = class {
|
|
|
408
434
|
}
|
|
409
435
|
const draw = Tone.getDraw();
|
|
410
436
|
const shift = this.#opts.rhythm !== "none" ? schedule.pickupBeats * spb : 0;
|
|
437
|
+
const mediaLead = Math.min(MEDIA_LEAD_SEC, spb);
|
|
411
438
|
schedule.events.forEach((ev, i) => {
|
|
412
439
|
const at = ev.beat * spb + shift;
|
|
413
440
|
if (ev.midi !== null && ev.glide !== true) {
|
|
@@ -441,12 +468,14 @@ var SwaralipiPlayer = class {
|
|
|
441
468
|
transport.scheduleOnce((time) => melody.triggerAttackRelease(name, dur, time), at);
|
|
442
469
|
}
|
|
443
470
|
}
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
}
|
|
471
|
+
if (this.#opts.onEvent !== void 0) {
|
|
472
|
+
transport.scheduleOnce(() => this.#opts.onEvent?.(ev.path), Math.max(at - mediaLead, 0));
|
|
473
|
+
}
|
|
474
|
+
transport.scheduleOnce((time) => draw.schedule(() => this.#emitHighlight(ev.path), time), at);
|
|
447
475
|
});
|
|
448
|
-
const endAt = schedule
|
|
476
|
+
const endAt = playDurationSec(schedule, spb, this.#opts.rhythm);
|
|
449
477
|
transport.scheduleOnce((time) => {
|
|
478
|
+
this.#opts.onEvent?.(null);
|
|
450
479
|
draw.schedule(() => this.#emitHighlight(null), time);
|
|
451
480
|
}, schedule.totalBeats * spb + shift);
|
|
452
481
|
transport.scheduleOnce(() => this.stop(), endAt);
|