@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.
Files changed (37) hide show
  1. package/dist/{batch-SCSIUDB3.js → batch-TVEIT2OJ.js} +4 -4
  2. package/dist/{batch-SCSIUDB3.js.map → batch-TVEIT2OJ.js.map} +1 -1
  3. package/dist/{chunk-KYNCQ7OF.js → chunk-HDN3YZ6K.js} +3 -3
  4. package/dist/{chunk-KYNCQ7OF.js.map → chunk-HDN3YZ6K.js.map} +1 -1
  5. package/dist/chunk-V5ZSUJCA.js +354 -0
  6. package/dist/chunk-V5ZSUJCA.js.map +1 -0
  7. package/dist/{chunk-DINRYGRI.js → chunk-WBSIKYWB.js} +3 -3
  8. package/dist/{chunk-DINRYGRI.js.map → chunk-WBSIKYWB.js.map} +1 -1
  9. package/dist/cli.cjs +1 -1
  10. package/dist/cli.cjs.map +1 -1
  11. package/dist/cli.js +2 -2
  12. package/dist/convert/index.cjs +1 -1
  13. package/dist/convert/index.cjs.map +1 -1
  14. package/dist/convert/index.js +2 -2
  15. package/dist/index.cjs +1 -1
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.js +1 -1
  18. package/dist/player/index.cjs +117 -20
  19. package/dist/player/index.cjs.map +1 -1
  20. package/dist/player/index.d.cts +24 -164
  21. package/dist/player/index.d.ts +24 -164
  22. package/dist/player/index.js +91 -343
  23. package/dist/player/index.js.map +1 -1
  24. package/dist/schedule-BhiP_maG.d.ts +167 -0
  25. package/dist/schedule-Dsb2paUE.d.cts +167 -0
  26. package/dist/spec/index.cjs +1 -1
  27. package/dist/spec/index.cjs.map +1 -1
  28. package/dist/spec/index.d.cts +1 -1
  29. package/dist/spec/index.d.ts +1 -1
  30. package/dist/spec/index.js +1 -1
  31. package/dist/viewer/index.cjs +584 -54
  32. package/dist/viewer/index.cjs.map +1 -1
  33. package/dist/viewer/index.d.cts +177 -18
  34. package/dist/viewer/index.d.ts +177 -18
  35. package/dist/viewer/index.js +322 -30
  36. package/dist/viewer/index.js.map +1 -1
  37. package/package.json +7 -2
@@ -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 !== "none" && base) {
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 raw = [];
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
- raw.push({ tick: kOn, on: true, note: ev.kan, vel: 70 });
1024
- raw.push({ tick: start, on: false, note: ev.kan, vel: 0 });
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
- raw.push({ tick: start, on: true, note: ev.midi, vel: 100 });
1027
- raw.push({ tick: end, on: false, note: ev.midi, vel: 0 });
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
- track.push(...vlq(0), 255, 81, 3, usPerBeat >> 16 & 255, usPerBeat >> 8 & 255, usPerBeat & 255);
1034
- let last = 0;
1035
- for (const e of raw) {
1036
- track.push(...vlq(e.tick - last), e.on ? 144 : 128, e.note & 127, e.vel & 127);
1037
- last = e.tick;
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
- track.push(...vlq(0), 255, 47, 0);
1040
- const header = [77, 84, 104, 100, ...u32be(6), 0, 0, 0, 1, PPQ >> 8 & 255, PPQ & 255];
1041
- const trackChunk = [77, 84, 114, 107, ...u32be(track.length), ...track];
1042
- return new Uint8Array([...header, ...trackChunk]);
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 bytes = encodeMidi(schedule, { bpm, name: options.name ?? doc.meta.title });
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;