@ugfoundation/swaralipi-js 0.3.2 → 0.3.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/dist/{batch-5LFXQ67K.js → batch-6YAUMVHC.js} +4 -4
- package/dist/{batch-5LFXQ67K.js.map → batch-6YAUMVHC.js.map} +1 -1
- package/dist/{chunk-XQYXZPHK.js → chunk-RTCRSOVW.js} +3 -3
- package/dist/{chunk-XQYXZPHK.js.map → chunk-RTCRSOVW.js.map} +1 -1
- package/dist/{chunk-QVNFEWMM.js → chunk-UBJZUKQD.js} +3 -3
- package/dist/{chunk-QVNFEWMM.js.map → chunk-UBJZUKQD.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 +67 -10
- package/dist/player/index.cjs.map +1 -1
- package/dist/player/index.d.cts +14 -1
- package/dist/player/index.d.ts +14 -1
- package/dist/player/index.js +67 -10
- 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 +25 -1
- package/dist/viewer/index.cjs.map +1 -1
- package/dist/viewer/index.d.cts +35 -3
- package/dist/viewer/index.d.ts +35 -3
- package/dist/viewer/index.js +23 -1
- package/dist/viewer/index.js.map +1 -1
- package/package.json +1 -1
package/dist/player/index.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as Swaralipi, O as Octave, a as SvaraLetter, N as Note } from '../schema-D3wyNRqM.cjs';
|
|
1
|
+
import { S as Swaralipi, T as Taal, O as Octave, a as SvaraLetter, N as Note } from '../schema-D3wyNRqM.cjs';
|
|
2
2
|
import { T as TaalId } from '../taals-DxJ8wRtF.cjs';
|
|
3
3
|
import { I as InstrumentId, a as InstrumentDef, S as Schedule, D as DrumVoice } from '../schedule-CHXz3V6T.cjs';
|
|
4
4
|
export { b as DEFAULT_SOUNDS_BASE, c as DRUM_KIT_BASE, d as INSTRUMENTS, P as PlayEvent, e as SALAMANDER_BASE, f as SAMPLED_TAALS, g as ScheduleOptions, T as TABLA_BPMS, h as TABLA_VARIATIONS, i as TONEJS_INSTRUMENTS_BASE, j as ToneModule, k as buildSchedule, l as drumPattern, m as instrumentSampleUrl, n as nearestTablaBpm, o as normalizeBase, p as probeUrl, q as quantizeLoopEnd, s as santoorUrls, r as subsetUrls, t as tablaBpms, u as tablaUrl } from '../schedule-CHXz3V6T.cjs';
|
|
@@ -48,6 +48,10 @@ 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
|
+
/** Taal mode only (`loadTaal`): called on the draw thread once per matra with
|
|
52
|
+
* the beat index within the cycle (0-based, `< numBeats`), so a consumer can
|
|
53
|
+
* highlight the active theka bol / drum-staff note. Never fired in doc mode. */
|
|
54
|
+
onBeat?: (beatInCycle: number) => void;
|
|
51
55
|
/** Called on every state transition. */
|
|
52
56
|
onState?: (state: PlayerState) => void;
|
|
53
57
|
}
|
|
@@ -82,6 +86,15 @@ declare class SwaralipiPlayer {
|
|
|
82
86
|
get usesTabla(): boolean;
|
|
83
87
|
/** Set the document to play. Stops any current playback. */
|
|
84
88
|
load(doc: Swaralipi): void;
|
|
89
|
+
/**
|
|
90
|
+
* Taal mode: loop one avartan of a taal's theka (rhythm only, no melody) —
|
|
91
|
+
* for a standalone theka player. `play()` loops indefinitely (tabla loop /
|
|
92
|
+
* drum sequence) and fires `onBeat(beatInCycle)` once per matra on the draw
|
|
93
|
+
* thread so the consumer can highlight the active bol / drum-staff note. The
|
|
94
|
+
* taal's `id` should be a sampled taal (see `SAMPLED_TAALS`) for tabla audio;
|
|
95
|
+
* `variation` picks a tabla loop variation. Stops any current playback.
|
|
96
|
+
*/
|
|
97
|
+
loadTaal(taal: Taal): void;
|
|
85
98
|
/** Start (or resume) playback. Must be triggered from a user gesture so the
|
|
86
99
|
* browser permits `AudioContext` resume. */
|
|
87
100
|
play(): Promise<void>;
|
package/dist/player/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { S as Swaralipi, O as Octave, a as SvaraLetter, N as Note } from '../schema-D3wyNRqM.js';
|
|
1
|
+
import { S as Swaralipi, T as Taal, O as Octave, a as SvaraLetter, N as Note } from '../schema-D3wyNRqM.js';
|
|
2
2
|
import { T as TaalId } from '../taals-DxJ8wRtF.js';
|
|
3
3
|
import { I as InstrumentId, a as InstrumentDef, S as Schedule, D as DrumVoice } from '../schedule-BaE3JGHe.js';
|
|
4
4
|
export { b as DEFAULT_SOUNDS_BASE, c as DRUM_KIT_BASE, d as INSTRUMENTS, P as PlayEvent, e as SALAMANDER_BASE, f as SAMPLED_TAALS, g as ScheduleOptions, T as TABLA_BPMS, h as TABLA_VARIATIONS, i as TONEJS_INSTRUMENTS_BASE, j as ToneModule, k as buildSchedule, l as drumPattern, m as instrumentSampleUrl, n as nearestTablaBpm, o as normalizeBase, p as probeUrl, q as quantizeLoopEnd, s as santoorUrls, r as subsetUrls, t as tablaBpms, u as tablaUrl } from '../schedule-BaE3JGHe.js';
|
|
@@ -48,6 +48,10 @@ 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
|
+
/** Taal mode only (`loadTaal`): called on the draw thread once per matra with
|
|
52
|
+
* the beat index within the cycle (0-based, `< numBeats`), so a consumer can
|
|
53
|
+
* highlight the active theka bol / drum-staff note. Never fired in doc mode. */
|
|
54
|
+
onBeat?: (beatInCycle: number) => void;
|
|
51
55
|
/** Called on every state transition. */
|
|
52
56
|
onState?: (state: PlayerState) => void;
|
|
53
57
|
}
|
|
@@ -82,6 +86,15 @@ declare class SwaralipiPlayer {
|
|
|
82
86
|
get usesTabla(): boolean;
|
|
83
87
|
/** Set the document to play. Stops any current playback. */
|
|
84
88
|
load(doc: Swaralipi): void;
|
|
89
|
+
/**
|
|
90
|
+
* Taal mode: loop one avartan of a taal's theka (rhythm only, no melody) —
|
|
91
|
+
* for a standalone theka player. `play()` loops indefinitely (tabla loop /
|
|
92
|
+
* drum sequence) and fires `onBeat(beatInCycle)` once per matra on the draw
|
|
93
|
+
* thread so the consumer can highlight the active bol / drum-staff note. The
|
|
94
|
+
* taal's `id` should be a sampled taal (see `SAMPLED_TAALS`) for tabla audio;
|
|
95
|
+
* `variation` picks a tabla loop variation. Stops any current playback.
|
|
96
|
+
*/
|
|
97
|
+
loadTaal(taal: Taal): void;
|
|
85
98
|
/** Start (or resume) playback. Must be triggered from a user gesture so the
|
|
86
99
|
* browser permits `AudioContext` resume. */
|
|
87
100
|
play(): Promise<void>;
|
package/dist/player/index.js
CHANGED
|
@@ -22,6 +22,9 @@ var SwaralipiPlayer = class {
|
|
|
22
22
|
/** Per-song setBpm tweak: beats meta.bpm but is cleared by the next load(). */
|
|
23
23
|
#bpmTweak;
|
|
24
24
|
#doc;
|
|
25
|
+
/** Taal mode (`loadTaal`): loops one avartan of this taal's theka with no
|
|
26
|
+
* melody. Mutually exclusive with `#doc`. */
|
|
27
|
+
#taal;
|
|
25
28
|
#state = "idle";
|
|
26
29
|
#tone;
|
|
27
30
|
/** Master bus everything routes through, so stop() can fade it out. */
|
|
@@ -50,6 +53,7 @@ var SwaralipiPlayer = class {
|
|
|
50
53
|
tanpura: options.tanpura ?? true,
|
|
51
54
|
previewLimitSec: options.previewLimitSec ?? Infinity,
|
|
52
55
|
onHighlight: options.onHighlight,
|
|
56
|
+
onBeat: options.onBeat,
|
|
53
57
|
onState: options.onState,
|
|
54
58
|
onLimit: options.onLimit
|
|
55
59
|
};
|
|
@@ -89,6 +93,23 @@ var SwaralipiPlayer = class {
|
|
|
89
93
|
load(doc) {
|
|
90
94
|
this.#halt();
|
|
91
95
|
this.#doc = doc;
|
|
96
|
+
this.#taal = void 0;
|
|
97
|
+
this.#taalOverride = void 0;
|
|
98
|
+
this.#bpmTweak = void 0;
|
|
99
|
+
this.#setState("ready");
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Taal mode: loop one avartan of a taal's theka (rhythm only, no melody) —
|
|
103
|
+
* for a standalone theka player. `play()` loops indefinitely (tabla loop /
|
|
104
|
+
* drum sequence) and fires `onBeat(beatInCycle)` once per matra on the draw
|
|
105
|
+
* thread so the consumer can highlight the active bol / drum-staff note. The
|
|
106
|
+
* taal's `id` should be a sampled taal (see `SAMPLED_TAALS`) for tabla audio;
|
|
107
|
+
* `variation` picks a tabla loop variation. Stops any current playback.
|
|
108
|
+
*/
|
|
109
|
+
loadTaal(taal) {
|
|
110
|
+
this.#halt();
|
|
111
|
+
this.#taal = taal;
|
|
112
|
+
this.#doc = void 0;
|
|
92
113
|
this.#taalOverride = void 0;
|
|
93
114
|
this.#bpmTweak = void 0;
|
|
94
115
|
this.#setState("ready");
|
|
@@ -96,7 +117,7 @@ var SwaralipiPlayer = class {
|
|
|
96
117
|
/** Start (or resume) playback. Must be triggered from a user gesture so the
|
|
97
118
|
* browser permits `AudioContext` resume. */
|
|
98
119
|
async play() {
|
|
99
|
-
if (this.#doc === void 0 || this.#state === "playing") return;
|
|
120
|
+
if (this.#doc === void 0 && this.#taal === void 0 || this.#state === "playing") return;
|
|
100
121
|
const Tone = await this.#ensureTone();
|
|
101
122
|
await Tone.start();
|
|
102
123
|
if (this.#state === "paused") {
|
|
@@ -144,7 +165,7 @@ var SwaralipiPlayer = class {
|
|
|
144
165
|
this.#master.gain.cancelScheduledValues(now);
|
|
145
166
|
this.#master.gain.rampTo(0, STOP_FADE_SEC, now);
|
|
146
167
|
this.#emitHighlight(null);
|
|
147
|
-
this.#setState(this.#doc ? "ready" : "idle");
|
|
168
|
+
this.#setState(this.#doc || this.#taal ? "ready" : "idle");
|
|
148
169
|
this.#cancelFadeTimer();
|
|
149
170
|
this.#fadeTimer = setTimeout(() => this.#halt(), STOP_FADE_SEC * 1e3 + 60);
|
|
150
171
|
return;
|
|
@@ -166,7 +187,7 @@ var SwaralipiPlayer = class {
|
|
|
166
187
|
this.#restoreMasterGain();
|
|
167
188
|
this.#scheduleIds = [];
|
|
168
189
|
this.#emitHighlight(null);
|
|
169
|
-
if (this.#state !== "idle") this.#setState(this.#doc ? "ready" : "idle");
|
|
190
|
+
if (this.#state !== "idle") this.#setState(this.#doc || this.#taal ? "ready" : "idle");
|
|
170
191
|
}
|
|
171
192
|
#cancelFadeTimer() {
|
|
172
193
|
if (this.#fadeTimer !== void 0) {
|
|
@@ -280,15 +301,16 @@ var SwaralipiPlayer = class {
|
|
|
280
301
|
#emitHighlight(path) {
|
|
281
302
|
this.#opts.onHighlight?.(path);
|
|
282
303
|
}
|
|
283
|
-
/** The tabla variation to play: the doc's own (
|
|
284
|
-
* explicit setTaal override plays the taal's default variation. */
|
|
304
|
+
/** The tabla variation to play: taal-mode taal's, the doc's own (auto mode),
|
|
305
|
+
* else none — an explicit setTaal override plays the taal's default variation. */
|
|
285
306
|
#variation() {
|
|
307
|
+
if (this.#taal !== void 0) return this.#taal.variation;
|
|
286
308
|
return this.#taalAuto ? this.#doc?.meta.taal?.variation : void 0;
|
|
287
309
|
}
|
|
288
|
-
/** Resolve the active taal: an explicit override, else
|
|
310
|
+
/** Resolve the active taal id: taal mode's taal, an explicit override, else
|
|
311
|
+
* the doc's — but only when it names a sampled taal. */
|
|
289
312
|
#taalId() {
|
|
290
|
-
|
|
291
|
-
const id = this.#doc?.meta.taal?.id;
|
|
313
|
+
const id = this.#taal?.id ?? (!this.#taalAuto && this.#taalOverride !== void 0 ? this.#taalOverride : this.#doc?.meta.taal?.id);
|
|
292
314
|
return id !== void 0 && id in TAALS ? id : void 0;
|
|
293
315
|
}
|
|
294
316
|
/** Resolve an instrument id to its def (custom overrides built-ins; the
|
|
@@ -345,6 +367,7 @@ var SwaralipiPlayer = class {
|
|
|
345
367
|
/** Build the melody instrument(s)/rhythm/drone for the current settings and
|
|
346
368
|
* wait for their buffers, then schedule everything on the Transport. */
|
|
347
369
|
async #arrange(Tone) {
|
|
370
|
+
if (this.#taal !== void 0) return this.#arrangeTaal(Tone);
|
|
348
371
|
if (this.#doc === void 0) return;
|
|
349
372
|
const base = this.#opts.soundsBaseUrl;
|
|
350
373
|
const transport = Tone.getTransport();
|
|
@@ -390,6 +413,40 @@ var SwaralipiPlayer = class {
|
|
|
390
413
|
}, schedule.totalBeats * spb);
|
|
391
414
|
transport.scheduleOnce(() => this.stop(), endAt);
|
|
392
415
|
}
|
|
416
|
+
/** Taal mode: loop one avartan forever (tabla loop / drum sequence via
|
|
417
|
+
* #buildRhythm), firing onBeat + a `String(beatInCycle)` highlight each matra
|
|
418
|
+
* on the draw thread. No melody, no tanpura, no auto-stop — it plays until
|
|
419
|
+
* stop(). */
|
|
420
|
+
async #arrangeTaal(Tone) {
|
|
421
|
+
if (this.#taal === void 0) return;
|
|
422
|
+
const base = this.#opts.soundsBaseUrl;
|
|
423
|
+
const transport = Tone.getTransport();
|
|
424
|
+
transport.cancel(0);
|
|
425
|
+
transport.position = 0;
|
|
426
|
+
this.#scheduleIds = [];
|
|
427
|
+
this.#ensureMaster(Tone);
|
|
428
|
+
this.#restoreMasterGain();
|
|
429
|
+
const bpm = this.#effectiveBpm();
|
|
430
|
+
transport.bpm.value = bpm;
|
|
431
|
+
const spb = 60 / bpm;
|
|
432
|
+
this.#buildRhythm(Tone, transport, base, spb);
|
|
433
|
+
await Tone.loaded();
|
|
434
|
+
if (this.#tabla?.buffer?.loaded) {
|
|
435
|
+
this.#tabla.loopEnd = quantizeLoopEnd(this.#tabla.buffer.duration, spb);
|
|
436
|
+
}
|
|
437
|
+
const draw = Tone.getDraw();
|
|
438
|
+
const n = Math.max(1, this.#taal.numBeats);
|
|
439
|
+
let beat = 0;
|
|
440
|
+
const id = transport.scheduleRepeat((time) => {
|
|
441
|
+
const b = beat % n;
|
|
442
|
+
draw.schedule(() => {
|
|
443
|
+
this.#emitHighlight(String(b));
|
|
444
|
+
this.#opts.onBeat?.(b);
|
|
445
|
+
}, time);
|
|
446
|
+
beat += 1;
|
|
447
|
+
}, spb, 0);
|
|
448
|
+
this.#scheduleIds.push(id);
|
|
449
|
+
}
|
|
393
450
|
/** Build (or reuse) the melody sampler for `id`, loading only the samples
|
|
394
451
|
* covering `range`. A cached sampler is reused while it still covers the
|
|
395
452
|
* range; otherwise it's rebuilt (so changing songs/tonic widens it lazily). */
|
|
@@ -420,7 +477,7 @@ var SwaralipiPlayer = class {
|
|
|
420
477
|
if (this.#opts.rhythm === "drums") {
|
|
421
478
|
this.#drums ??= createDrumKit(Tone).connect(master);
|
|
422
479
|
const drums = this.#drums;
|
|
423
|
-
const taal = taalId !== void 0 ? TAALS[taalId] : { numBeats: 4, sam: 0 };
|
|
480
|
+
const taal = this.#taal ?? (taalId !== void 0 ? TAALS[taalId] : { numBeats: 4, sam: 0 });
|
|
424
481
|
const pattern = drumPattern(taal);
|
|
425
482
|
let beat2 = 0;
|
|
426
483
|
const id2 = transport.scheduleRepeat((time) => {
|
|
@@ -434,7 +491,7 @@ var SwaralipiPlayer = class {
|
|
|
434
491
|
if (this.#opts.rhythm === "none" || base === void 0) return;
|
|
435
492
|
this.#metronome ??= createMetronome(Tone, base).connect(master);
|
|
436
493
|
const metro = this.#metronome;
|
|
437
|
-
const cycle = taalId !== void 0 ? TAALS[taalId].numBeats : 4;
|
|
494
|
+
const cycle = this.#taal?.numBeats ?? (taalId !== void 0 ? TAALS[taalId].numBeats : 4);
|
|
438
495
|
let beat = 0;
|
|
439
496
|
const id = transport.scheduleRepeat((time) => {
|
|
440
497
|
metro.volume.setValueAtTime(beat % cycle === 0 ? 0 : -8, time);
|