@ugfoundation/swaralipi-js 0.2.1 → 0.3.1
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-SCSIUDB3.js → batch-TVEIT2OJ.js} +4 -4
- package/dist/{batch-SCSIUDB3.js.map → batch-TVEIT2OJ.js.map} +1 -1
- package/dist/{chunk-KYNCQ7OF.js → chunk-HDN3YZ6K.js} +3 -3
- package/dist/{chunk-KYNCQ7OF.js.map → chunk-HDN3YZ6K.js.map} +1 -1
- package/dist/chunk-V5ZSUJCA.js +354 -0
- package/dist/chunk-V5ZSUJCA.js.map +1 -0
- package/dist/{chunk-DINRYGRI.js → chunk-WBSIKYWB.js} +3 -3
- package/dist/{chunk-DINRYGRI.js.map → chunk-WBSIKYWB.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 +117 -20
- package/dist/player/index.cjs.map +1 -1
- package/dist/player/index.d.cts +24 -164
- package/dist/player/index.d.ts +24 -164
- package/dist/player/index.js +91 -343
- package/dist/player/index.js.map +1 -1
- package/dist/schedule-BhiP_maG.d.ts +167 -0
- package/dist/schedule-Dsb2paUE.d.cts +167 -0
- 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 +584 -54
- package/dist/viewer/index.cjs.map +1 -1
- package/dist/viewer/index.d.cts +177 -18
- package/dist/viewer/index.d.ts +177 -18
- package/dist/viewer/index.js +322 -30
- package/dist/viewer/index.js.map +1 -1
- package/package.json +7 -2
package/dist/player/index.cjs
CHANGED
|
@@ -329,6 +329,34 @@ function createMetronome(Tone, base) {
|
|
|
329
329
|
return new Tone.Player(metronomeUrl(base));
|
|
330
330
|
}
|
|
331
331
|
var TANPURA_PCS = ["c", "cs", "d", "ds", "e", "f", "fs", "g", "gs", "a", "as", "b"];
|
|
332
|
+
var DRUM_KIT_BASE = "https://tonejs.github.io/audio/drum-samples/acoustic-kit/";
|
|
333
|
+
var DRUM_FILES = {
|
|
334
|
+
kick: "kick.mp3",
|
|
335
|
+
snare: "snare.mp3",
|
|
336
|
+
hihat: "hihat.mp3",
|
|
337
|
+
tom1: "tom1.mp3",
|
|
338
|
+
tom2: "tom2.mp3",
|
|
339
|
+
tom3: "tom3.mp3"
|
|
340
|
+
};
|
|
341
|
+
function createDrumKit(Tone, base = DRUM_KIT_BASE) {
|
|
342
|
+
const urls = {};
|
|
343
|
+
for (const [voice, file] of Object.entries(DRUM_FILES)) urls[voice] = file;
|
|
344
|
+
return new Tone.Players({ urls, baseUrl: normalizeBase(base) });
|
|
345
|
+
}
|
|
346
|
+
function drumPattern(taal) {
|
|
347
|
+
const n = taal.numBeats;
|
|
348
|
+
const sam = taal.sam ?? 0;
|
|
349
|
+
const khaali = new Set(taal.khaali ?? []);
|
|
350
|
+
const bhaags = taal.bhaags ?? [n];
|
|
351
|
+
const pattern = new Array(n).fill("hihat");
|
|
352
|
+
let pos = 0;
|
|
353
|
+
for (const len of bhaags) {
|
|
354
|
+
pattern[pos] = khaali.has(pos) ? "hihat" : "snare";
|
|
355
|
+
pos += len;
|
|
356
|
+
}
|
|
357
|
+
pattern[sam] = "kick";
|
|
358
|
+
return pattern;
|
|
359
|
+
}
|
|
332
360
|
function createSampler(Tone, def, soundsBaseUrl, range) {
|
|
333
361
|
const base = def.local ? `${normalizeBase(soundsBaseUrl ?? "")}${def.baseUrl}` : def.baseUrl;
|
|
334
362
|
const urls = range ? subsetUrls(def.urls, range.lo, range.hi) : def.urls;
|
|
@@ -371,6 +399,7 @@ var SwaralipiPlayer = class {
|
|
|
371
399
|
#tanpuraKey = "";
|
|
372
400
|
#metronome;
|
|
373
401
|
#tabla;
|
|
402
|
+
#drums;
|
|
374
403
|
#scheduleIds = [];
|
|
375
404
|
#fadeTimer;
|
|
376
405
|
constructor(options = {}) {
|
|
@@ -601,8 +630,9 @@ var SwaralipiPlayer = class {
|
|
|
601
630
|
this.#tanpura?.dispose();
|
|
602
631
|
this.#metronome?.dispose();
|
|
603
632
|
this.#tabla?.dispose();
|
|
633
|
+
this.#drums?.dispose();
|
|
604
634
|
this.#master?.dispose();
|
|
605
|
-
this.#tanpura = this.#metronome = this.#tabla = this.#master = void 0;
|
|
635
|
+
this.#tanpura = this.#metronome = this.#tabla = this.#drums = this.#master = void 0;
|
|
606
636
|
}
|
|
607
637
|
/* ----------------------------------------------------------------- */
|
|
608
638
|
/* Internals */
|
|
@@ -753,6 +783,20 @@ var SwaralipiPlayer = class {
|
|
|
753
783
|
this.#tabla.sync().start(0);
|
|
754
784
|
return;
|
|
755
785
|
}
|
|
786
|
+
if (this.#opts.rhythm === "drums") {
|
|
787
|
+
this.#drums ??= createDrumKit(Tone).connect(master);
|
|
788
|
+
const drums = this.#drums;
|
|
789
|
+
const taal = taalId !== void 0 ? TAALS[taalId] : { numBeats: 4, sam: 0 };
|
|
790
|
+
const pattern = drumPattern(taal);
|
|
791
|
+
let beat2 = 0;
|
|
792
|
+
const id2 = transport.scheduleRepeat((time) => {
|
|
793
|
+
const voice = pattern[beat2 % pattern.length];
|
|
794
|
+
if (drums.has(voice)) drums.player(voice).start(time);
|
|
795
|
+
beat2 += 1;
|
|
796
|
+
}, spb, 0);
|
|
797
|
+
this.#scheduleIds.push(id2);
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
756
800
|
if (this.#opts.rhythm === "none" || base === void 0) return;
|
|
757
801
|
this.#metronome ??= createMetronome(Tone, base).connect(master);
|
|
758
802
|
const metro = this.#metronome;
|
|
@@ -860,7 +904,17 @@ async function exportAudio(doc, options = {}) {
|
|
|
860
904
|
}
|
|
861
905
|
}
|
|
862
906
|
let tabla;
|
|
863
|
-
if (rhythm
|
|
907
|
+
if (rhythm === "drums" && base) {
|
|
908
|
+
const drums = createDrumKit(Tone).connect(master);
|
|
909
|
+
const taal = taalId !== void 0 ? TAALS[taalId] : { numBeats: 4, sam: 0 };
|
|
910
|
+
const pattern = drumPattern(taal);
|
|
911
|
+
let beat = 0;
|
|
912
|
+
transport.scheduleRepeat((time) => {
|
|
913
|
+
const voice = pattern[beat % pattern.length];
|
|
914
|
+
if (drums.has(voice)) drums.player(voice).start(time);
|
|
915
|
+
beat += 1;
|
|
916
|
+
}, spb, 0);
|
|
917
|
+
} else if (rhythm !== "none" && base) {
|
|
864
918
|
if (usesTabla && taalId) {
|
|
865
919
|
const url = tablaUrl(base, taalId, bpm, variation);
|
|
866
920
|
tabla = new Tone.Player({ url, loop: true }).connect(master);
|
|
@@ -1012,34 +1066,72 @@ function utf8(s) {
|
|
|
1012
1066
|
function u32be(n) {
|
|
1013
1067
|
return [n >>> 24 & 255, n >>> 16 & 255, n >>> 8 & 255, n & 255];
|
|
1014
1068
|
}
|
|
1069
|
+
var DRUM_NOTE = {
|
|
1070
|
+
kick: 36,
|
|
1071
|
+
snare: 38,
|
|
1072
|
+
hihat: 42,
|
|
1073
|
+
// closed hi-hat
|
|
1074
|
+
tom1: 48,
|
|
1075
|
+
tom2: 45,
|
|
1076
|
+
tom3: 41
|
|
1077
|
+
};
|
|
1078
|
+
function trackChunk(raw, head) {
|
|
1079
|
+
raw.sort((a, b) => a.tick - b.tick || Number(a.on) - Number(b.on));
|
|
1080
|
+
const track = [...head];
|
|
1081
|
+
let last = 0;
|
|
1082
|
+
for (const e of raw) {
|
|
1083
|
+
const status = (e.on ? 144 : 128) | e.ch & 15;
|
|
1084
|
+
track.push(...vlq(e.tick - last), status, e.note & 127, e.vel & 127);
|
|
1085
|
+
last = e.tick;
|
|
1086
|
+
}
|
|
1087
|
+
track.push(...vlq(0), 255, 47, 0);
|
|
1088
|
+
return [77, 84, 114, 107, ...u32be(track.length), ...track];
|
|
1089
|
+
}
|
|
1015
1090
|
function encodeMidi(schedule, opts) {
|
|
1016
|
-
const
|
|
1091
|
+
const melody = [];
|
|
1017
1092
|
for (const ev of schedule.events) {
|
|
1018
1093
|
if (ev.midi === null) continue;
|
|
1019
1094
|
const start = Math.round(ev.beat * PPQ);
|
|
1020
1095
|
const end = Math.max(Math.round((ev.beat + ev.durationBeats) * PPQ), start + 1);
|
|
1021
1096
|
if (ev.kan !== null) {
|
|
1022
1097
|
const kOn = Math.max(start - Math.round(0.12 * PPQ), 0);
|
|
1023
|
-
|
|
1024
|
-
|
|
1098
|
+
melody.push({ tick: kOn, on: true, note: ev.kan, vel: 70, ch: 0 });
|
|
1099
|
+
melody.push({ tick: start, on: false, note: ev.kan, vel: 0, ch: 0 });
|
|
1025
1100
|
}
|
|
1026
|
-
|
|
1027
|
-
|
|
1101
|
+
melody.push({ tick: start, on: true, note: ev.midi, vel: 100, ch: 0 });
|
|
1102
|
+
melody.push({ tick: end, on: false, note: ev.midi, vel: 0, ch: 0 });
|
|
1028
1103
|
}
|
|
1029
|
-
raw.sort((a, b) => a.tick - b.tick || Number(a.on) - Number(b.on));
|
|
1030
|
-
const track = [];
|
|
1031
|
-
if (opts.name) track.push(...vlq(0), 255, 3, ...vlq(utf8(opts.name).length), ...utf8(opts.name));
|
|
1032
1104
|
const usPerBeat = Math.round(6e7 / opts.bpm);
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1105
|
+
const head = [];
|
|
1106
|
+
if (opts.name) head.push(...vlq(0), 255, 3, ...vlq(utf8(opts.name).length), ...utf8(opts.name));
|
|
1107
|
+
head.push(...vlq(0), 255, 81, 3, usPerBeat >> 16 & 255, usPerBeat >> 8 & 255, usPerBeat & 255);
|
|
1108
|
+
const chunks = [trackChunk(melody, head)];
|
|
1109
|
+
if (opts.drums && opts.drums.length > 0) {
|
|
1110
|
+
const drum = [];
|
|
1111
|
+
const totalBeats = Math.ceil(schedule.totalBeats);
|
|
1112
|
+
for (let beat = 0; beat < totalBeats; beat++) {
|
|
1113
|
+
const note = DRUM_NOTE[opts.drums[beat % opts.drums.length]];
|
|
1114
|
+
const t = beat * PPQ;
|
|
1115
|
+
drum.push({ tick: t, on: true, note, vel: 100, ch: 9 });
|
|
1116
|
+
drum.push({ tick: t + Math.round(PPQ / 4), on: false, note, vel: 0, ch: 9 });
|
|
1117
|
+
}
|
|
1118
|
+
chunks.push(trackChunk(drum, [...vlq(0), 255, 3, ...vlq(5), 68, 114, 117, 109, 115]));
|
|
1038
1119
|
}
|
|
1039
|
-
|
|
1040
|
-
const header = [
|
|
1041
|
-
|
|
1042
|
-
|
|
1120
|
+
const format = chunks.length > 1 ? 1 : 0;
|
|
1121
|
+
const header = [
|
|
1122
|
+
77,
|
|
1123
|
+
84,
|
|
1124
|
+
104,
|
|
1125
|
+
100,
|
|
1126
|
+
...u32be(6),
|
|
1127
|
+
0,
|
|
1128
|
+
format,
|
|
1129
|
+
chunks.length >> 8 & 255,
|
|
1130
|
+
chunks.length & 255,
|
|
1131
|
+
PPQ >> 8 & 255,
|
|
1132
|
+
PPQ & 255
|
|
1133
|
+
];
|
|
1134
|
+
return new Uint8Array([...header, ...chunks.flat()]);
|
|
1043
1135
|
}
|
|
1044
1136
|
function safeName2(title) {
|
|
1045
1137
|
return (title ?? "").replace(/[\\/:*?"<>|]+/g, "").trim() || "swaralipi";
|
|
@@ -1047,7 +1139,10 @@ function safeName2(title) {
|
|
|
1047
1139
|
function exportMidi(doc, options = {}) {
|
|
1048
1140
|
const schedule = buildSchedule(doc, { tonic: options.tonic, baseOctave: options.baseOctave });
|
|
1049
1141
|
const bpm = options.bpm ?? doc.meta.bpm ?? 90;
|
|
1050
|
-
const
|
|
1142
|
+
const taalId = doc.meta.taal?.id;
|
|
1143
|
+
const taal = taalId !== void 0 && taalId in TAALS ? TAALS[taalId] : { numBeats: 4, sam: 0 };
|
|
1144
|
+
const drums = options.drums ? drumPattern(taal) : void 0;
|
|
1145
|
+
const bytes = encodeMidi(schedule, { bpm, name: options.name ?? doc.meta.title, drums });
|
|
1051
1146
|
return {
|
|
1052
1147
|
blob: new Blob([bytes], { type: "audio/midi" }),
|
|
1053
1148
|
filename: `${safeName2(doc.meta.title)}.mid`,
|
|
@@ -1056,6 +1151,7 @@ function exportMidi(doc, options = {}) {
|
|
|
1056
1151
|
}
|
|
1057
1152
|
|
|
1058
1153
|
exports.DEFAULT_SOUNDS_BASE = DEFAULT_SOUNDS_BASE;
|
|
1154
|
+
exports.DRUM_KIT_BASE = DRUM_KIT_BASE;
|
|
1059
1155
|
exports.INSTRUMENTS = INSTRUMENTS;
|
|
1060
1156
|
exports.OCTAVE_OFFSET = OCTAVE_OFFSET;
|
|
1061
1157
|
exports.SALAMANDER_BASE = SALAMANDER_BASE;
|
|
@@ -1066,6 +1162,7 @@ exports.TABLA_BPMS = TABLA_BPMS;
|
|
|
1066
1162
|
exports.TABLA_VARIATIONS = TABLA_VARIATIONS;
|
|
1067
1163
|
exports.TONEJS_INSTRUMENTS_BASE = TONEJS_INSTRUMENTS_BASE;
|
|
1068
1164
|
exports.buildSchedule = buildSchedule;
|
|
1165
|
+
exports.drumPattern = drumPattern;
|
|
1069
1166
|
exports.encodeMidi = encodeMidi;
|
|
1070
1167
|
exports.encodeMp3 = encodeMp3;
|
|
1071
1168
|
exports.encodeWav = encodeWav;
|