@ugfoundation/swaralipi-js 0.3.0 → 0.3.2

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-QPSHVNDS.js → batch-5LFXQ67K.js} +5 -5
  2. package/dist/{batch-QPSHVNDS.js.map → batch-5LFXQ67K.js.map} +1 -1
  3. package/dist/{chunk-V5ZSUJCA.js → chunk-32LOBRC2.js} +3 -3
  4. package/dist/{chunk-V5ZSUJCA.js.map → chunk-32LOBRC2.js.map} +1 -1
  5. package/dist/{chunk-4YTOLN3C.js → chunk-QVNFEWMM.js} +6 -6
  6. package/dist/chunk-QVNFEWMM.js.map +1 -0
  7. package/dist/{chunk-VCRWS3F3.js → chunk-XQYXZPHK.js} +3 -3
  8. package/dist/{chunk-VCRWS3F3.js.map → chunk-XQYXZPHK.js.map} +1 -1
  9. package/dist/{chunk-H6HC44PZ.js → chunk-YE7UVYRY.js} +5 -3
  10. package/dist/chunk-YE7UVYRY.js.map +1 -0
  11. package/dist/cli.cjs +6 -4
  12. package/dist/cli.cjs.map +1 -1
  13. package/dist/cli.js +3 -3
  14. package/dist/convert/index.cjs +6 -4
  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 +62 -19
  23. package/dist/player/index.cjs.map +1 -1
  24. package/dist/player/index.d.cts +16 -10
  25. package/dist/player/index.d.ts +16 -10
  26. package/dist/player/index.js +63 -22
  27. package/dist/player/index.js.map +1 -1
  28. package/dist/{schedule-PTHUONrA.d.ts → schedule-BaE3JGHe.d.ts} +2 -2
  29. package/dist/{schedule-DhO_Xzz4.d.cts → schedule-CHXz3V6T.d.cts} +2 -2
  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-FmEulsjY.d.cts → taals-DxJ8wRtF.d.cts} +8 -1
  36. package/dist/{taals-FmEulsjY.d.ts → taals-DxJ8wRtF.d.ts} +8 -1
  37. package/dist/viewer/index.cjs +4 -2
  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-4YTOLN3C.js.map +0 -1
  44. package/dist/chunk-H6HC44PZ.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { S as Swaralipi, O as Octave, a as SvaraLetter, N as Note } from '../schema-D3wyNRqM.cjs';
2
- import { T as TaalId } from '../taals-FmEulsjY.cjs';
3
- import { I as InstrumentId, a as InstrumentDef, S as Schedule } from '../schedule-DhO_Xzz4.cjs';
4
- export { D as DEFAULT_SOUNDS_BASE, b as DRUM_KIT_BASE, c as DrumVoice, d as INSTRUMENTS, P as PlayEvent, e as SALAMANDER_BASE, f as SAMPLED_TAALS, g as ScheduleOptions, T as TABLA_BPMS, h as TABLA_VARIATIONS, i as TONEJS_INSTRUMENTS_BASE, j as ToneModule, k as buildSchedule, l as drumPattern, m as instrumentSampleUrl, n as nearestTablaBpm, o as normalizeBase, p as probeUrl, q as quantizeLoopEnd, s as santoorUrls, r as subsetUrls, t as tablaBpms, u as tablaUrl } from '../schedule-DhO_Xzz4.cjs';
2
+ import { T as TaalId } from '../taals-DxJ8wRtF.cjs';
3
+ import { I as InstrumentId, a as InstrumentDef, S as Schedule, D as DrumVoice } from '../schedule-CHXz3V6T.cjs';
4
+ export { b as DEFAULT_SOUNDS_BASE, c as DRUM_KIT_BASE, d as INSTRUMENTS, P as PlayEvent, e as SALAMANDER_BASE, f as SAMPLED_TAALS, g as ScheduleOptions, T as TABLA_BPMS, h as TABLA_VARIATIONS, i as TONEJS_INSTRUMENTS_BASE, j as ToneModule, k as buildSchedule, l as drumPattern, m as instrumentSampleUrl, n as nearestTablaBpm, o as normalizeBase, p as probeUrl, q as quantizeLoopEnd, s as santoorUrls, r as subsetUrls, t as tablaBpms, u as tablaUrl } from '../schedule-CHXz3V6T.cjs';
5
5
  import 'zod';
6
6
  import 'tone';
7
7
 
@@ -204,10 +204,12 @@ declare function encodeMp3(channels: Float32Array[], sampleRate: number, kbps?:
204
204
  * tests) in plain Node, and carries no dependency (the SMF format is trivial),
205
205
  * which keeps it clear of any redistribution/license question.
206
206
  *
207
- * Scope = the **melody voice only**. Tabla is prerecorded audio loops with no
208
- * per-stroke note data, so there's nothing to turn into MIDI notes; tanpura is
209
- * a fixed background drone. A `.mid` here is the notation line — the user opens
210
- * it in any DAW / notation editor and assigns whatever instrument they like.
207
+ * Scope = the **melody voice**, plus — when `drums` is on — the Western-drumset
208
+ * translation of the taal (the same `drumPattern` the player sequences) written
209
+ * as a **separate GM channel-10 track**. Tabla is prerecorded audio loops with
210
+ * no per-stroke note data, so there's nothing to turn into MIDI notes; tanpura
211
+ * is a fixed background drone. A `.mid` here is the notation line (+ optional
212
+ * drums) — the user opens it in any DAW and assigns instruments.
211
213
  */
212
214
 
213
215
  interface MidiExportOptions {
@@ -219,6 +221,8 @@ interface MidiExportOptions {
219
221
  bpm?: number;
220
222
  /** Track name meta-event. Default the song title. */
221
223
  name?: string;
224
+ /** Also export the taal's drumset pattern as a separate GM channel-10 track. */
225
+ drums?: boolean;
222
226
  }
223
227
  interface MidiExportResult {
224
228
  blob: Blob;
@@ -227,12 +231,14 @@ interface MidiExportResult {
227
231
  mimeType: string;
228
232
  }
229
233
  /**
230
- * Encode a schedule to a format-0 Standard MIDI File (one track, one channel).
231
- * Pure — no Tone, no browser — so it's directly unit-testable.
234
+ * Encode a schedule to a Standard MIDI File. Melody rides channel 0; when
235
+ * `drums` is given, its per-beat voices ride a second GM channel-10 track
236
+ * (the file becomes format 1). Pure — no Tone, no browser — directly testable.
232
237
  */
233
238
  declare function encodeMidi(schedule: Schedule, opts: {
234
239
  bpm: number;
235
240
  name?: string;
241
+ drums?: DrumVoice[];
236
242
  }): Uint8Array;
237
243
  /**
238
244
  * Export a swaralipi doc to a `.mid` Blob (melody voice only). No browser APIs
@@ -283,4 +289,4 @@ declare function midiToSampleName(midi: number): string;
283
289
  /** MIDI → santoor sample filename stem (`cs4`): lowercase, `#` → `s`. */
284
290
  declare function sampleFileName(midi: number): string;
285
291
 
286
- export { type AudioExportOptions, type AudioExportResult, type AudioFormat, type BindSheetOptions, InstrumentDef, InstrumentId, type MidiExportOptions, type MidiExportResult, OCTAVE_OFFSET, type PlayerOptions, type PlayerState, type RhythmMode, SHUDDHA, Schedule, SwaralipiPlayer, encodeMidi, encodeMp3, encodeWav, exportAudio, exportMidi, midiToSampleName, parseTonic, sampleFileName, svaraSemitone, svaraToMidi };
292
+ export { type AudioExportOptions, type AudioExportResult, type AudioFormat, type BindSheetOptions, DrumVoice, InstrumentDef, InstrumentId, type MidiExportOptions, type MidiExportResult, OCTAVE_OFFSET, type PlayerOptions, type PlayerState, type RhythmMode, SHUDDHA, Schedule, SwaralipiPlayer, encodeMidi, encodeMp3, encodeWav, exportAudio, exportMidi, midiToSampleName, parseTonic, sampleFileName, svaraSemitone, svaraToMidi };
@@ -1,7 +1,7 @@
1
1
  import { S as Swaralipi, O as Octave, a as SvaraLetter, N as Note } from '../schema-D3wyNRqM.js';
2
- import { T as TaalId } from '../taals-FmEulsjY.js';
3
- import { I as InstrumentId, a as InstrumentDef, S as Schedule } from '../schedule-PTHUONrA.js';
4
- export { D as DEFAULT_SOUNDS_BASE, b as DRUM_KIT_BASE, c as DrumVoice, d as INSTRUMENTS, P as PlayEvent, e as SALAMANDER_BASE, f as SAMPLED_TAALS, g as ScheduleOptions, T as TABLA_BPMS, h as TABLA_VARIATIONS, i as TONEJS_INSTRUMENTS_BASE, j as ToneModule, k as buildSchedule, l as drumPattern, m as instrumentSampleUrl, n as nearestTablaBpm, o as normalizeBase, p as probeUrl, q as quantizeLoopEnd, s as santoorUrls, r as subsetUrls, t as tablaBpms, u as tablaUrl } from '../schedule-PTHUONrA.js';
2
+ import { T as TaalId } from '../taals-DxJ8wRtF.js';
3
+ import { I as InstrumentId, a as InstrumentDef, S as Schedule, D as DrumVoice } from '../schedule-BaE3JGHe.js';
4
+ export { b as DEFAULT_SOUNDS_BASE, c as DRUM_KIT_BASE, d as INSTRUMENTS, P as PlayEvent, e as SALAMANDER_BASE, f as SAMPLED_TAALS, g as ScheduleOptions, T as TABLA_BPMS, h as TABLA_VARIATIONS, i as TONEJS_INSTRUMENTS_BASE, j as ToneModule, k as buildSchedule, l as drumPattern, m as instrumentSampleUrl, n as nearestTablaBpm, o as normalizeBase, p as probeUrl, q as quantizeLoopEnd, s as santoorUrls, r as subsetUrls, t as tablaBpms, u as tablaUrl } from '../schedule-BaE3JGHe.js';
5
5
  import 'zod';
6
6
  import 'tone';
7
7
 
@@ -204,10 +204,12 @@ declare function encodeMp3(channels: Float32Array[], sampleRate: number, kbps?:
204
204
  * tests) in plain Node, and carries no dependency (the SMF format is trivial),
205
205
  * which keeps it clear of any redistribution/license question.
206
206
  *
207
- * Scope = the **melody voice only**. Tabla is prerecorded audio loops with no
208
- * per-stroke note data, so there's nothing to turn into MIDI notes; tanpura is
209
- * a fixed background drone. A `.mid` here is the notation line — the user opens
210
- * it in any DAW / notation editor and assigns whatever instrument they like.
207
+ * Scope = the **melody voice**, plus — when `drums` is on — the Western-drumset
208
+ * translation of the taal (the same `drumPattern` the player sequences) written
209
+ * as a **separate GM channel-10 track**. Tabla is prerecorded audio loops with
210
+ * no per-stroke note data, so there's nothing to turn into MIDI notes; tanpura
211
+ * is a fixed background drone. A `.mid` here is the notation line (+ optional
212
+ * drums) — the user opens it in any DAW and assigns instruments.
211
213
  */
212
214
 
213
215
  interface MidiExportOptions {
@@ -219,6 +221,8 @@ interface MidiExportOptions {
219
221
  bpm?: number;
220
222
  /** Track name meta-event. Default the song title. */
221
223
  name?: string;
224
+ /** Also export the taal's drumset pattern as a separate GM channel-10 track. */
225
+ drums?: boolean;
222
226
  }
223
227
  interface MidiExportResult {
224
228
  blob: Blob;
@@ -227,12 +231,14 @@ interface MidiExportResult {
227
231
  mimeType: string;
228
232
  }
229
233
  /**
230
- * Encode a schedule to a format-0 Standard MIDI File (one track, one channel).
231
- * Pure — no Tone, no browser — so it's directly unit-testable.
234
+ * Encode a schedule to a Standard MIDI File. Melody rides channel 0; when
235
+ * `drums` is given, its per-beat voices ride a second GM channel-10 track
236
+ * (the file becomes format 1). Pure — no Tone, no browser — directly testable.
232
237
  */
233
238
  declare function encodeMidi(schedule: Schedule, opts: {
234
239
  bpm: number;
235
240
  name?: string;
241
+ drums?: DrumVoice[];
236
242
  }): Uint8Array;
237
243
  /**
238
244
  * Export a swaralipi doc to a `.mid` Blob (melody voice only). No browser APIs
@@ -283,4 +289,4 @@ declare function midiToSampleName(midi: number): string;
283
289
  /** MIDI → santoor sample filename stem (`cs4`): lowercase, `#` → `s`. */
284
290
  declare function sampleFileName(midi: number): string;
285
291
 
286
- export { type AudioExportOptions, type AudioExportResult, type AudioFormat, type BindSheetOptions, InstrumentDef, InstrumentId, type MidiExportOptions, type MidiExportResult, OCTAVE_OFFSET, type PlayerOptions, type PlayerState, type RhythmMode, SHUDDHA, Schedule, SwaralipiPlayer, encodeMidi, encodeMp3, encodeWav, exportAudio, exportMidi, midiToSampleName, parseTonic, sampleFileName, svaraSemitone, svaraToMidi };
292
+ export { type AudioExportOptions, type AudioExportResult, type AudioFormat, type BindSheetOptions, DrumVoice, InstrumentDef, InstrumentId, type MidiExportOptions, type MidiExportResult, OCTAVE_OFFSET, type PlayerOptions, type PlayerState, type RhythmMode, SHUDDHA, Schedule, SwaralipiPlayer, encodeMidi, encodeMp3, encodeWav, exportAudio, exportMidi, midiToSampleName, parseTonic, sampleFileName, svaraSemitone, svaraToMidi };
@@ -1,6 +1,6 @@
1
- import { DEFAULT_SOUNDS_BASE, INSTRUMENTS, SAMPLED_TAALS, instrumentSampleUrl, probeUrl, normalizeBase, nearestTablaBpm, tablaBpms, buildSchedule, quantizeLoopEnd, midiToSampleName, createSampler, tablaUrl, createDrumKit, drumPattern, createMetronome, TANPURA_PCS, createTanpura, parseTonic } from '../chunk-V5ZSUJCA.js';
2
- export { DEFAULT_SOUNDS_BASE, DRUM_KIT_BASE, INSTRUMENTS, OCTAVE_OFFSET, SALAMANDER_BASE, SAMPLED_TAALS, SHUDDHA, TABLA_BPMS, TABLA_VARIATIONS, TONEJS_INSTRUMENTS_BASE, buildSchedule, drumPattern, instrumentSampleUrl, midiToSampleName, nearestTablaBpm, normalizeBase, parseTonic, probeUrl, quantizeLoopEnd, sampleFileName, santoorUrls, subsetUrls, svaraSemitone, svaraToMidi, tablaBpms, tablaUrl } from '../chunk-V5ZSUJCA.js';
3
- import { TAALS } from '../chunk-H6HC44PZ.js';
1
+ import { DEFAULT_SOUNDS_BASE, INSTRUMENTS, SAMPLED_TAALS, instrumentSampleUrl, probeUrl, normalizeBase, nearestTablaBpm, tablaBpms, buildSchedule, quantizeLoopEnd, midiToSampleName, createSampler, tablaUrl, createDrumKit, drumPattern, createMetronome, TANPURA_PCS, createTanpura, parseTonic } from '../chunk-32LOBRC2.js';
2
+ export { DEFAULT_SOUNDS_BASE, DRUM_KIT_BASE, INSTRUMENTS, OCTAVE_OFFSET, SALAMANDER_BASE, SAMPLED_TAALS, SHUDDHA, TABLA_BPMS, TABLA_VARIATIONS, TONEJS_INSTRUMENTS_BASE, buildSchedule, drumPattern, instrumentSampleUrl, midiToSampleName, nearestTablaBpm, normalizeBase, parseTonic, probeUrl, quantizeLoopEnd, sampleFileName, santoorUrls, subsetUrls, svaraSemitone, svaraToMidi, tablaBpms, tablaUrl } from '../chunk-32LOBRC2.js';
3
+ import { TAALS } from '../chunk-YE7UVYRY.js';
4
4
 
5
5
  // src/player/player.ts
6
6
  var STOP_FADE_SEC = 0.5;
@@ -700,34 +700,72 @@ function utf8(s) {
700
700
  function u32be(n) {
701
701
  return [n >>> 24 & 255, n >>> 16 & 255, n >>> 8 & 255, n & 255];
702
702
  }
703
+ var DRUM_NOTE = {
704
+ kick: 36,
705
+ snare: 38,
706
+ hihat: 42,
707
+ // closed hi-hat
708
+ tom1: 48,
709
+ tom2: 45,
710
+ tom3: 41
711
+ };
712
+ function trackChunk(raw, head) {
713
+ raw.sort((a, b) => a.tick - b.tick || Number(a.on) - Number(b.on));
714
+ const track = [...head];
715
+ let last = 0;
716
+ for (const e of raw) {
717
+ const status = (e.on ? 144 : 128) | e.ch & 15;
718
+ track.push(...vlq(e.tick - last), status, e.note & 127, e.vel & 127);
719
+ last = e.tick;
720
+ }
721
+ track.push(...vlq(0), 255, 47, 0);
722
+ return [77, 84, 114, 107, ...u32be(track.length), ...track];
723
+ }
703
724
  function encodeMidi(schedule, opts) {
704
- const raw = [];
725
+ const melody = [];
705
726
  for (const ev of schedule.events) {
706
727
  if (ev.midi === null) continue;
707
728
  const start = Math.round(ev.beat * PPQ);
708
729
  const end = Math.max(Math.round((ev.beat + ev.durationBeats) * PPQ), start + 1);
709
730
  if (ev.kan !== null) {
710
731
  const kOn = Math.max(start - Math.round(0.12 * PPQ), 0);
711
- raw.push({ tick: kOn, on: true, note: ev.kan, vel: 70 });
712
- raw.push({ tick: start, on: false, note: ev.kan, vel: 0 });
732
+ melody.push({ tick: kOn, on: true, note: ev.kan, vel: 70, ch: 0 });
733
+ melody.push({ tick: start, on: false, note: ev.kan, vel: 0, ch: 0 });
713
734
  }
714
- raw.push({ tick: start, on: true, note: ev.midi, vel: 100 });
715
- raw.push({ tick: end, on: false, note: ev.midi, vel: 0 });
735
+ melody.push({ tick: start, on: true, note: ev.midi, vel: 100, ch: 0 });
736
+ melody.push({ tick: end, on: false, note: ev.midi, vel: 0, ch: 0 });
716
737
  }
717
- raw.sort((a, b) => a.tick - b.tick || Number(a.on) - Number(b.on));
718
- const track = [];
719
- if (opts.name) track.push(...vlq(0), 255, 3, ...vlq(utf8(opts.name).length), ...utf8(opts.name));
720
738
  const usPerBeat = Math.round(6e7 / opts.bpm);
721
- track.push(...vlq(0), 255, 81, 3, usPerBeat >> 16 & 255, usPerBeat >> 8 & 255, usPerBeat & 255);
722
- let last = 0;
723
- for (const e of raw) {
724
- track.push(...vlq(e.tick - last), e.on ? 144 : 128, e.note & 127, e.vel & 127);
725
- last = e.tick;
726
- }
727
- track.push(...vlq(0), 255, 47, 0);
728
- const header = [77, 84, 104, 100, ...u32be(6), 0, 0, 0, 1, PPQ >> 8 & 255, PPQ & 255];
729
- const trackChunk = [77, 84, 114, 107, ...u32be(track.length), ...track];
730
- return new Uint8Array([...header, ...trackChunk]);
739
+ const head = [];
740
+ if (opts.name) head.push(...vlq(0), 255, 3, ...vlq(utf8(opts.name).length), ...utf8(opts.name));
741
+ head.push(...vlq(0), 255, 81, 3, usPerBeat >> 16 & 255, usPerBeat >> 8 & 255, usPerBeat & 255);
742
+ const chunks = [trackChunk(melody, head)];
743
+ if (opts.drums && opts.drums.length > 0) {
744
+ const drum = [];
745
+ const totalBeats = Math.ceil(schedule.totalBeats);
746
+ for (let beat = 0; beat < totalBeats; beat++) {
747
+ const note = DRUM_NOTE[opts.drums[beat % opts.drums.length]];
748
+ const t = beat * PPQ;
749
+ drum.push({ tick: t, on: true, note, vel: 100, ch: 9 });
750
+ drum.push({ tick: t + Math.round(PPQ / 4), on: false, note, vel: 0, ch: 9 });
751
+ }
752
+ chunks.push(trackChunk(drum, [...vlq(0), 255, 3, ...vlq(5), 68, 114, 117, 109, 115]));
753
+ }
754
+ const format = chunks.length > 1 ? 1 : 0;
755
+ const header = [
756
+ 77,
757
+ 84,
758
+ 104,
759
+ 100,
760
+ ...u32be(6),
761
+ 0,
762
+ format,
763
+ chunks.length >> 8 & 255,
764
+ chunks.length & 255,
765
+ PPQ >> 8 & 255,
766
+ PPQ & 255
767
+ ];
768
+ return new Uint8Array([...header, ...chunks.flat()]);
731
769
  }
732
770
  function safeName2(title) {
733
771
  return (title ?? "").replace(/[\\/:*?"<>|]+/g, "").trim() || "swaralipi";
@@ -735,7 +773,10 @@ function safeName2(title) {
735
773
  function exportMidi(doc, options = {}) {
736
774
  const schedule = buildSchedule(doc, { tonic: options.tonic, baseOctave: options.baseOctave });
737
775
  const bpm = options.bpm ?? doc.meta.bpm ?? 90;
738
- const bytes = encodeMidi(schedule, { bpm, name: options.name ?? doc.meta.title });
776
+ const taalId = doc.meta.taal?.id;
777
+ const taal = taalId !== void 0 && taalId in TAALS ? TAALS[taalId] : { numBeats: 4, sam: 0 };
778
+ const drums = options.drums ? drumPattern(taal) : void 0;
779
+ const bytes = encodeMidi(schedule, { bpm, name: options.name ?? doc.meta.title, drums });
739
780
  return {
740
781
  blob: new Blob([bytes], { type: "audio/midi" }),
741
782
  filename: `${safeName2(doc.meta.title)}.mid`,