@ugfoundation/swaralipi-js 0.3.3 → 0.4.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/dist/{batch-6YAUMVHC.js → batch-V4X3GN4N.js} +5 -5
- package/dist/{batch-6YAUMVHC.js.map → batch-V4X3GN4N.js.map} +1 -1
- package/dist/{chunk-YE7UVYRY.js → chunk-6ETPFHZ6.js} +5 -3
- package/dist/chunk-6ETPFHZ6.js.map +1 -0
- package/dist/{chunk-32LOBRC2.js → chunk-6QW7MSJO.js} +24 -5
- package/dist/chunk-6QW7MSJO.js.map +1 -0
- package/dist/{chunk-RTCRSOVW.js → chunk-RTKWFJFI.js} +3 -3
- package/dist/{chunk-RTCRSOVW.js.map → chunk-RTKWFJFI.js.map} +1 -1
- package/dist/{chunk-UBJZUKQD.js → chunk-ZS43BDP7.js} +4 -4
- package/dist/{chunk-UBJZUKQD.js.map → chunk-ZS43BDP7.js.map} +1 -1
- package/dist/cli.cjs +4 -2
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +3 -3
- package/dist/convert/index.cjs +4 -2
- package/dist/convert/index.cjs.map +1 -1
- package/dist/convert/index.js +3 -3
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/player/index.cjs +51 -13
- package/dist/player/index.cjs.map +1 -1
- package/dist/player/index.d.cts +3 -3
- package/dist/player/index.d.ts +3 -3
- package/dist/player/index.js +31 -14
- package/dist/player/index.js.map +1 -1
- package/dist/{schedule-BaE3JGHe.d.ts → schedule-CAns78TD.d.ts} +11 -1
- package/dist/{schedule-CHXz3V6T.d.cts → schedule-D3JyEhuO.d.cts} +11 -1
- package/dist/spec/index.cjs +4 -2
- package/dist/spec/index.cjs.map +1 -1
- package/dist/spec/index.d.cts +2 -2
- package/dist/spec/index.d.ts +2 -2
- package/dist/spec/index.js +2 -2
- package/dist/{taals-DxJ8wRtF.d.ts → taals-CjzBuZ35.d.cts} +8 -1
- package/dist/{taals-DxJ8wRtF.d.cts → taals-CjzBuZ35.d.ts} +8 -1
- package/dist/viewer/index.cjs +17 -3
- package/dist/viewer/index.cjs.map +1 -1
- package/dist/viewer/index.d.cts +2 -2
- package/dist/viewer/index.d.ts +2 -2
- package/dist/viewer/index.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-32LOBRC2.js.map +0 -1
- package/dist/chunk-YE7UVYRY.js.map +0 -1
package/dist/player/index.cjs
CHANGED
|
@@ -17,8 +17,10 @@ var TAALS = {
|
|
|
17
17
|
teentaal: { id: "teentaal", numBeats: 16, bhaags: [4, 4, 4, 4], sam: 0, khaali: [8] },
|
|
18
18
|
aratheka: { id: "aratheka", numBeats: 16, bhaags: [4, 4, 4, 4], sam: 0, khaali: [8] },
|
|
19
19
|
// আড়াঠেকা
|
|
20
|
-
jhampak: { id: "jhampak", numBeats: 5, bhaags: [3, 2], sam: 0 }
|
|
20
|
+
jhampak: { id: "jhampak", numBeats: 5, bhaags: [3, 2], sam: 0 },
|
|
21
21
|
// ঝম্পক
|
|
22
|
+
dhamaar: { id: "dhamaar", numBeats: 14, bhaags: [5, 2, 3, 4], sam: 0, khaali: [7] }
|
|
23
|
+
// ধামার
|
|
22
24
|
};
|
|
23
25
|
|
|
24
26
|
// src/player/pitch.ts
|
|
@@ -97,15 +99,25 @@ function sampleFileName(midi) {
|
|
|
97
99
|
|
|
98
100
|
// src/player/schedule.ts
|
|
99
101
|
var isSilent = (n) => n.svara === void 0 && n.rest !== true && n.sustain !== true;
|
|
102
|
+
var SAM_TICK = /^\d+[fF]$/;
|
|
100
103
|
function buildSchedule(doc, opts = {}) {
|
|
101
104
|
const tonicPc = parseTonic(opts.tonic ?? doc.meta.tonic);
|
|
102
105
|
const baseOctave = opts.baseOctave ?? 4;
|
|
103
106
|
const events = [];
|
|
104
107
|
let cursor = 0;
|
|
105
108
|
let lastPitched = -1;
|
|
109
|
+
let samBeat;
|
|
106
110
|
doc.parts.forEach((part, p) => {
|
|
107
111
|
part.rows.forEach((row, r) => {
|
|
108
112
|
row.notes.forEach((note, i) => {
|
|
113
|
+
if (samBeat === void 0 && note.above !== void 0) {
|
|
114
|
+
for (const a of note.above) {
|
|
115
|
+
if (a.raw !== void 0 && SAM_TICK.test(a.raw)) {
|
|
116
|
+
samBeat = cursor;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
109
121
|
if (note.bar !== void 0 || isSilent(note)) return;
|
|
110
122
|
const path = `${p}.${r}.${i}`;
|
|
111
123
|
const dur = note.durationMatra ?? 1;
|
|
@@ -129,7 +141,9 @@ function buildSchedule(doc, opts = {}) {
|
|
|
129
141
|
});
|
|
130
142
|
});
|
|
131
143
|
});
|
|
132
|
-
|
|
144
|
+
const numBeats = doc.meta.taal?.numBeats;
|
|
145
|
+
const pickupBeats = samBeat !== void 0 && numBeats !== void 0 && numBeats > 0 ? (numBeats - samBeat % numBeats) % numBeats : 0;
|
|
146
|
+
return { events, totalBeats: cursor, pickupBeats };
|
|
133
147
|
}
|
|
134
148
|
|
|
135
149
|
// src/player/instruments.ts
|
|
@@ -324,6 +338,13 @@ function tablaUrl(base, taalId, bpm, variation) {
|
|
|
324
338
|
const seg = tablaSeg(taalId, variation);
|
|
325
339
|
return `${normalizeBase(base)}tabla/${seg}/${seg}${bpm}bpm.mp3`;
|
|
326
340
|
}
|
|
341
|
+
var TABLA_TONIC_SEMITONE = 0;
|
|
342
|
+
function tablaShiftSemitones(tonicSemitone) {
|
|
343
|
+
const d = ((tonicSemitone - TABLA_TONIC_SEMITONE) % 12 + 12) % 12;
|
|
344
|
+
return d > 6 ? d - 12 : d;
|
|
345
|
+
}
|
|
346
|
+
var TABLA_GRAIN_SEC = 0.06;
|
|
347
|
+
var TABLA_GRAIN_OVERLAP_SEC = 0.03;
|
|
327
348
|
function metronomeUrl(base) {
|
|
328
349
|
return `${normalizeBase(base)}metronome/metro2.mp3`;
|
|
329
350
|
}
|
|
@@ -403,6 +424,7 @@ var SwaralipiPlayer = class {
|
|
|
403
424
|
#tanpura;
|
|
404
425
|
#tanpuraKey = "";
|
|
405
426
|
#metronome;
|
|
427
|
+
/** The looping tabla — a `GrainPlayer` when the tonic needs it retuned. */
|
|
406
428
|
#tabla;
|
|
407
429
|
#drums;
|
|
408
430
|
#scheduleIds = [];
|
|
@@ -760,8 +782,9 @@ var SwaralipiPlayer = class {
|
|
|
760
782
|
this.#tabla.loopEnd = quantizeLoopEnd(this.#tabla.buffer.duration, spb);
|
|
761
783
|
}
|
|
762
784
|
const draw = Tone.getDraw();
|
|
785
|
+
const shift = this.#opts.rhythm !== "none" ? schedule.pickupBeats * spb : 0;
|
|
763
786
|
for (const ev of schedule.events) {
|
|
764
|
-
const at = ev.beat * spb;
|
|
787
|
+
const at = ev.beat * spb + shift;
|
|
765
788
|
if (ev.midi !== null) {
|
|
766
789
|
const name = midiToSampleName(ev.midi);
|
|
767
790
|
const dur = Math.max(ev.durationBeats * spb, 0.05);
|
|
@@ -775,10 +798,10 @@ var SwaralipiPlayer = class {
|
|
|
775
798
|
draw.schedule(() => this.#emitHighlight(ev.path), time);
|
|
776
799
|
}, at);
|
|
777
800
|
}
|
|
778
|
-
const endAt = schedule.totalBeats * spb + spb;
|
|
801
|
+
const endAt = schedule.totalBeats * spb + shift + spb;
|
|
779
802
|
transport.scheduleOnce((time) => {
|
|
780
803
|
draw.schedule(() => this.#emitHighlight(null), time);
|
|
781
|
-
}, schedule.totalBeats * spb);
|
|
804
|
+
}, schedule.totalBeats * spb + shift);
|
|
782
805
|
transport.scheduleOnce(() => this.stop(), endAt);
|
|
783
806
|
}
|
|
784
807
|
/** Taal mode: loop one avartan forever (tabla loop / drum sequence via
|
|
@@ -838,14 +861,21 @@ var SwaralipiPlayer = class {
|
|
|
838
861
|
const wantsTabla = this.#opts.rhythm === "tabla" && taalId !== void 0 && SAMPLED_TAALS.has(taalId) && base !== void 0;
|
|
839
862
|
if (wantsTabla) {
|
|
840
863
|
const url = tablaUrl(base, taalId, nearestTablaBpm(this.bpm, tablaBpms(taalId)), this.#variation());
|
|
841
|
-
|
|
864
|
+
const shift = tablaShiftSemitones(this.#tonicSemitone());
|
|
865
|
+
this.#tabla = (shift === 0 ? new Tone.Player({ url, loop: true }) : new Tone.GrainPlayer({
|
|
866
|
+
url,
|
|
867
|
+
loop: true,
|
|
868
|
+
detune: shift * 100,
|
|
869
|
+
grainSize: TABLA_GRAIN_SEC,
|
|
870
|
+
overlap: TABLA_GRAIN_OVERLAP_SEC
|
|
871
|
+
})).connect(master);
|
|
842
872
|
this.#tabla.sync().start(0);
|
|
843
873
|
return;
|
|
844
874
|
}
|
|
845
875
|
if (this.#opts.rhythm === "drums") {
|
|
846
876
|
this.#drums ??= createDrumKit(Tone).connect(master);
|
|
847
877
|
const drums = this.#drums;
|
|
848
|
-
const taal = this.#taal ?? (taalId !== void 0 ? TAALS[taalId] : { numBeats: 4, sam: 0 }
|
|
878
|
+
const taal = this.#taal ?? (taalId !== void 0 ? TAALS[taalId] : this.#doc?.meta.taal) ?? { numBeats: 4, sam: 0 };
|
|
849
879
|
const pattern = drumPattern(taal);
|
|
850
880
|
let beat2 = 0;
|
|
851
881
|
const id2 = transport.scheduleRepeat((time) => {
|
|
@@ -859,7 +889,7 @@ var SwaralipiPlayer = class {
|
|
|
859
889
|
if (this.#opts.rhythm === "none" || base === void 0) return;
|
|
860
890
|
this.#metronome ??= createMetronome(Tone, base).connect(master);
|
|
861
891
|
const metro = this.#metronome;
|
|
862
|
-
const cycle = this.#taal?.numBeats ??
|
|
892
|
+
const cycle = this.#taal?.numBeats ?? this.#doc?.meta.taal?.numBeats ?? 4;
|
|
863
893
|
let beat = 0;
|
|
864
894
|
const id = transport.scheduleRepeat((time) => {
|
|
865
895
|
metro.volume.setValueAtTime(beat % cycle === 0 ? 0 : -8, time);
|
|
@@ -943,7 +973,8 @@ async function exportAudio(doc, options = {}) {
|
|
|
943
973
|
const spb = 60 / bpm;
|
|
944
974
|
const id = options.instrument ?? "piano";
|
|
945
975
|
const def = pickDef(id, options);
|
|
946
|
-
const
|
|
976
|
+
const shift = rhythm !== "none" ? schedule.pickupBeats * spb : 0;
|
|
977
|
+
const durationSec = Math.max(schedule.totalBeats * spb + shift + (def.release ?? 1) + fadeOutSec + 0.5, 0.5);
|
|
947
978
|
const rendered = await Tone.Offline(
|
|
948
979
|
async ({ transport }) => {
|
|
949
980
|
transport.bpm.value = bpm;
|
|
@@ -952,7 +983,7 @@ async function exportAudio(doc, options = {}) {
|
|
|
952
983
|
const sampler = createSampler(Tone, def, base, range).connect(master);
|
|
953
984
|
for (const ev of schedule.events) {
|
|
954
985
|
if (ev.midi === null) continue;
|
|
955
|
-
const at = ev.beat * spb;
|
|
986
|
+
const at = ev.beat * spb + shift;
|
|
956
987
|
const dur = Math.max(ev.durationBeats * spb, 0.05);
|
|
957
988
|
if (ev.kan !== null) {
|
|
958
989
|
const name2 = midiToSampleName(ev.kan);
|
|
@@ -965,7 +996,7 @@ async function exportAudio(doc, options = {}) {
|
|
|
965
996
|
let tabla;
|
|
966
997
|
if (rhythm === "drums" && base) {
|
|
967
998
|
const drums = createDrumKit(Tone).connect(master);
|
|
968
|
-
const taal = taalId !== void 0 ? TAALS[taalId] : { numBeats: 4, sam: 0 };
|
|
999
|
+
const taal = taalId !== void 0 ? TAALS[taalId] : doc.meta.taal ?? { numBeats: 4, sam: 0 };
|
|
969
1000
|
const pattern = drumPattern(taal);
|
|
970
1001
|
let beat = 0;
|
|
971
1002
|
transport.scheduleRepeat((time) => {
|
|
@@ -976,11 +1007,18 @@ async function exportAudio(doc, options = {}) {
|
|
|
976
1007
|
} else if (rhythm !== "none" && base) {
|
|
977
1008
|
if (usesTabla && taalId) {
|
|
978
1009
|
const url = tablaUrl(base, taalId, bpm, variation);
|
|
979
|
-
|
|
1010
|
+
const semis = tablaShiftSemitones(parseTonic(options.tonic ?? doc.meta.tonic));
|
|
1011
|
+
tabla = (semis === 0 ? new Tone.Player({ url, loop: true }) : new Tone.GrainPlayer({
|
|
1012
|
+
url,
|
|
1013
|
+
loop: true,
|
|
1014
|
+
detune: semis * 100,
|
|
1015
|
+
grainSize: TABLA_GRAIN_SEC,
|
|
1016
|
+
overlap: TABLA_GRAIN_OVERLAP_SEC
|
|
1017
|
+
})).connect(master);
|
|
980
1018
|
tabla.sync().start(0);
|
|
981
1019
|
} else {
|
|
982
1020
|
const metro = createMetronome(Tone, base).connect(master);
|
|
983
|
-
const cycle = taalId !== void 0 ? TAALS[taalId].numBeats : 4;
|
|
1021
|
+
const cycle = taalId !== void 0 ? TAALS[taalId].numBeats : doc.meta.taal?.numBeats ?? 4;
|
|
984
1022
|
let beat = 0;
|
|
985
1023
|
transport.scheduleRepeat((time) => {
|
|
986
1024
|
metro.volume.setValueAtTime(beat % cycle === 0 ? 0 : -8, time);
|