@ugfoundation/swaralipi-js 0.3.4 → 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 (37) hide show
  1. package/dist/{batch-Y3HKAL3B.js → batch-V4X3GN4N.js} +4 -4
  2. package/dist/{batch-Y3HKAL3B.js.map → batch-V4X3GN4N.js.map} +1 -1
  3. package/dist/{chunk-AIGSKUD3.js → chunk-6QW7MSJO.js} +23 -4
  4. package/dist/chunk-6QW7MSJO.js.map +1 -0
  5. package/dist/{chunk-IG2CIUTP.js → chunk-RTKWFJFI.js} +3 -3
  6. package/dist/{chunk-IG2CIUTP.js.map → chunk-RTKWFJFI.js.map} +1 -1
  7. package/dist/{chunk-KPKD53NA.js → chunk-ZS43BDP7.js} +3 -3
  8. package/dist/{chunk-KPKD53NA.js.map → chunk-ZS43BDP7.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 +48 -12
  19. package/dist/player/index.cjs.map +1 -1
  20. package/dist/player/index.d.cts +2 -2
  21. package/dist/player/index.d.ts +2 -2
  22. package/dist/player/index.js +30 -13
  23. package/dist/player/index.js.map +1 -1
  24. package/dist/{schedule-dXV5cUQI.d.ts → schedule-CAns78TD.d.ts} +10 -0
  25. package/dist/{schedule-C2WN7YOK.d.cts → schedule-D3JyEhuO.d.cts} +10 -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 +14 -2
  32. package/dist/viewer/index.cjs.map +1 -1
  33. package/dist/viewer/index.d.cts +1 -1
  34. package/dist/viewer/index.d.ts +1 -1
  35. package/dist/viewer/index.js +1 -1
  36. package/package.json +1 -1
  37. package/dist/chunk-AIGSKUD3.js.map +0 -1
@@ -99,15 +99,25 @@ function sampleFileName(midi) {
99
99
 
100
100
  // src/player/schedule.ts
101
101
  var isSilent = (n) => n.svara === void 0 && n.rest !== true && n.sustain !== true;
102
+ var SAM_TICK = /^\d+[fF]$/;
102
103
  function buildSchedule(doc, opts = {}) {
103
104
  const tonicPc = parseTonic(opts.tonic ?? doc.meta.tonic);
104
105
  const baseOctave = opts.baseOctave ?? 4;
105
106
  const events = [];
106
107
  let cursor = 0;
107
108
  let lastPitched = -1;
109
+ let samBeat;
108
110
  doc.parts.forEach((part, p) => {
109
111
  part.rows.forEach((row, r) => {
110
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
+ }
111
121
  if (note.bar !== void 0 || isSilent(note)) return;
112
122
  const path = `${p}.${r}.${i}`;
113
123
  const dur = note.durationMatra ?? 1;
@@ -131,7 +141,9 @@ function buildSchedule(doc, opts = {}) {
131
141
  });
132
142
  });
133
143
  });
134
- 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 };
135
147
  }
136
148
 
137
149
  // src/player/instruments.ts
@@ -326,6 +338,13 @@ function tablaUrl(base, taalId, bpm, variation) {
326
338
  const seg = tablaSeg(taalId, variation);
327
339
  return `${normalizeBase(base)}tabla/${seg}/${seg}${bpm}bpm.mp3`;
328
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;
329
348
  function metronomeUrl(base) {
330
349
  return `${normalizeBase(base)}metronome/metro2.mp3`;
331
350
  }
@@ -405,6 +424,7 @@ var SwaralipiPlayer = class {
405
424
  #tanpura;
406
425
  #tanpuraKey = "";
407
426
  #metronome;
427
+ /** The looping tabla — a `GrainPlayer` when the tonic needs it retuned. */
408
428
  #tabla;
409
429
  #drums;
410
430
  #scheduleIds = [];
@@ -762,8 +782,9 @@ var SwaralipiPlayer = class {
762
782
  this.#tabla.loopEnd = quantizeLoopEnd(this.#tabla.buffer.duration, spb);
763
783
  }
764
784
  const draw = Tone.getDraw();
785
+ const shift = this.#opts.rhythm !== "none" ? schedule.pickupBeats * spb : 0;
765
786
  for (const ev of schedule.events) {
766
- const at = ev.beat * spb;
787
+ const at = ev.beat * spb + shift;
767
788
  if (ev.midi !== null) {
768
789
  const name = midiToSampleName(ev.midi);
769
790
  const dur = Math.max(ev.durationBeats * spb, 0.05);
@@ -777,10 +798,10 @@ var SwaralipiPlayer = class {
777
798
  draw.schedule(() => this.#emitHighlight(ev.path), time);
778
799
  }, at);
779
800
  }
780
- const endAt = schedule.totalBeats * spb + spb;
801
+ const endAt = schedule.totalBeats * spb + shift + spb;
781
802
  transport.scheduleOnce((time) => {
782
803
  draw.schedule(() => this.#emitHighlight(null), time);
783
- }, schedule.totalBeats * spb);
804
+ }, schedule.totalBeats * spb + shift);
784
805
  transport.scheduleOnce(() => this.stop(), endAt);
785
806
  }
786
807
  /** Taal mode: loop one avartan forever (tabla loop / drum sequence via
@@ -840,14 +861,21 @@ var SwaralipiPlayer = class {
840
861
  const wantsTabla = this.#opts.rhythm === "tabla" && taalId !== void 0 && SAMPLED_TAALS.has(taalId) && base !== void 0;
841
862
  if (wantsTabla) {
842
863
  const url = tablaUrl(base, taalId, nearestTablaBpm(this.bpm, tablaBpms(taalId)), this.#variation());
843
- 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);
844
872
  this.#tabla.sync().start(0);
845
873
  return;
846
874
  }
847
875
  if (this.#opts.rhythm === "drums") {
848
876
  this.#drums ??= createDrumKit(Tone).connect(master);
849
877
  const drums = this.#drums;
850
- 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 };
851
879
  const pattern = drumPattern(taal);
852
880
  let beat2 = 0;
853
881
  const id2 = transport.scheduleRepeat((time) => {
@@ -861,7 +889,7 @@ var SwaralipiPlayer = class {
861
889
  if (this.#opts.rhythm === "none" || base === void 0) return;
862
890
  this.#metronome ??= createMetronome(Tone, base).connect(master);
863
891
  const metro = this.#metronome;
864
- const cycle = this.#taal?.numBeats ?? (taalId !== void 0 ? TAALS[taalId].numBeats : 4);
892
+ const cycle = this.#taal?.numBeats ?? this.#doc?.meta.taal?.numBeats ?? 4;
865
893
  let beat = 0;
866
894
  const id = transport.scheduleRepeat((time) => {
867
895
  metro.volume.setValueAtTime(beat % cycle === 0 ? 0 : -8, time);
@@ -945,7 +973,8 @@ async function exportAudio(doc, options = {}) {
945
973
  const spb = 60 / bpm;
946
974
  const id = options.instrument ?? "piano";
947
975
  const def = pickDef(id, options);
948
- 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);
949
978
  const rendered = await Tone.Offline(
950
979
  async ({ transport }) => {
951
980
  transport.bpm.value = bpm;
@@ -954,7 +983,7 @@ async function exportAudio(doc, options = {}) {
954
983
  const sampler = createSampler(Tone, def, base, range).connect(master);
955
984
  for (const ev of schedule.events) {
956
985
  if (ev.midi === null) continue;
957
- const at = ev.beat * spb;
986
+ const at = ev.beat * spb + shift;
958
987
  const dur = Math.max(ev.durationBeats * spb, 0.05);
959
988
  if (ev.kan !== null) {
960
989
  const name2 = midiToSampleName(ev.kan);
@@ -967,7 +996,7 @@ async function exportAudio(doc, options = {}) {
967
996
  let tabla;
968
997
  if (rhythm === "drums" && base) {
969
998
  const drums = createDrumKit(Tone).connect(master);
970
- 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 };
971
1000
  const pattern = drumPattern(taal);
972
1001
  let beat = 0;
973
1002
  transport.scheduleRepeat((time) => {
@@ -978,11 +1007,18 @@ async function exportAudio(doc, options = {}) {
978
1007
  } else if (rhythm !== "none" && base) {
979
1008
  if (usesTabla && taalId) {
980
1009
  const url = tablaUrl(base, taalId, bpm, variation);
981
- 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);
982
1018
  tabla.sync().start(0);
983
1019
  } else {
984
1020
  const metro = createMetronome(Tone, base).connect(master);
985
- const cycle = taalId !== void 0 ? TAALS[taalId].numBeats : 4;
1021
+ const cycle = taalId !== void 0 ? TAALS[taalId].numBeats : doc.meta.taal?.numBeats ?? 4;
986
1022
  let beat = 0;
987
1023
  transport.scheduleRepeat((time) => {
988
1024
  metro.volume.setValueAtTime(beat % cycle === 0 ? 0 : -8, time);