@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.
Files changed (44) hide show
  1. package/dist/{batch-6YAUMVHC.js → batch-V4X3GN4N.js} +5 -5
  2. package/dist/{batch-6YAUMVHC.js.map → batch-V4X3GN4N.js.map} +1 -1
  3. package/dist/{chunk-YE7UVYRY.js → chunk-6ETPFHZ6.js} +5 -3
  4. package/dist/chunk-6ETPFHZ6.js.map +1 -0
  5. package/dist/{chunk-32LOBRC2.js → chunk-6QW7MSJO.js} +24 -5
  6. package/dist/chunk-6QW7MSJO.js.map +1 -0
  7. package/dist/{chunk-RTCRSOVW.js → chunk-RTKWFJFI.js} +3 -3
  8. package/dist/{chunk-RTCRSOVW.js.map → chunk-RTKWFJFI.js.map} +1 -1
  9. package/dist/{chunk-UBJZUKQD.js → chunk-ZS43BDP7.js} +4 -4
  10. package/dist/{chunk-UBJZUKQD.js.map → chunk-ZS43BDP7.js.map} +1 -1
  11. package/dist/cli.cjs +4 -2
  12. package/dist/cli.cjs.map +1 -1
  13. package/dist/cli.js +3 -3
  14. package/dist/convert/index.cjs +4 -2
  15. package/dist/convert/index.cjs.map +1 -1
  16. package/dist/convert/index.js +3 -3
  17. package/dist/index.cjs +4 -2
  18. package/dist/index.cjs.map +1 -1
  19. package/dist/index.d.cts +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.js +2 -2
  22. package/dist/player/index.cjs +51 -13
  23. package/dist/player/index.cjs.map +1 -1
  24. package/dist/player/index.d.cts +3 -3
  25. package/dist/player/index.d.ts +3 -3
  26. package/dist/player/index.js +31 -14
  27. package/dist/player/index.js.map +1 -1
  28. package/dist/{schedule-BaE3JGHe.d.ts → schedule-CAns78TD.d.ts} +11 -1
  29. package/dist/{schedule-CHXz3V6T.d.cts → schedule-D3JyEhuO.d.cts} +11 -1
  30. package/dist/spec/index.cjs +4 -2
  31. package/dist/spec/index.cjs.map +1 -1
  32. package/dist/spec/index.d.cts +2 -2
  33. package/dist/spec/index.d.ts +2 -2
  34. package/dist/spec/index.js +2 -2
  35. package/dist/{taals-DxJ8wRtF.d.ts → taals-CjzBuZ35.d.cts} +8 -1
  36. package/dist/{taals-DxJ8wRtF.d.cts → taals-CjzBuZ35.d.ts} +8 -1
  37. package/dist/viewer/index.cjs +17 -3
  38. package/dist/viewer/index.cjs.map +1 -1
  39. package/dist/viewer/index.d.cts +2 -2
  40. package/dist/viewer/index.d.ts +2 -2
  41. package/dist/viewer/index.js +2 -2
  42. package/package.json +1 -1
  43. package/dist/chunk-32LOBRC2.js.map +0 -1
  44. package/dist/chunk-YE7UVYRY.js.map +0 -1
@@ -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
- return { events, totalBeats: cursor };
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
- this.#tabla = new Tone.Player({ url, loop: true }).connect(master);
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 ?? (taalId !== void 0 ? TAALS[taalId].numBeats : 4);
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 durationSec = Math.max(schedule.totalBeats * spb + (def.release ?? 1) + fadeOutSec + 0.5, 0.5);
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
- tabla = new Tone.Player({ url, loop: true }).connect(master);
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);