@ugfoundation/swaralipi-js 0.11.1 → 0.13.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.
- package/README.md +7 -1
- package/dist/{batch-RAM2S4QO.js → batch-BS2AIZOR.js} +6 -5
- package/dist/batch-BS2AIZOR.js.map +1 -0
- package/dist/{chunk-3EWAKXLM.js → chunk-3ZBNANDF.js} +3 -3
- package/dist/{chunk-3EWAKXLM.js.map → chunk-3ZBNANDF.js.map} +1 -1
- package/dist/{chunk-LRIAUFKF.js → chunk-5N6HGPFH.js} +8 -168
- package/dist/chunk-5N6HGPFH.js.map +1 -0
- package/dist/chunk-BSZ3WQUP.js +164 -0
- package/dist/chunk-BSZ3WQUP.js.map +1 -0
- package/dist/{chunk-SR7NFEGL.js → chunk-KOHSKHXI.js} +5 -3
- package/dist/chunk-KOHSKHXI.js.map +1 -0
- package/dist/{chunk-CF3JWIHP.js → chunk-ZNJTMQIX.js} +228 -64
- package/dist/chunk-ZNJTMQIX.js.map +1 -0
- package/dist/cli.cjs +6 -4
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +3 -3
- package/dist/convert/index.cjs +6 -4
- package/dist/convert/index.cjs.map +1 -1
- package/dist/convert/index.js +4 -3
- package/dist/index.cjs +4 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/{lay-DypMIIuf.d.ts → lay-CWlEhImP.d.ts} +8 -1
- package/dist/{lay-CvZeVon3.d.cts → lay-CXhC-HRO.d.cts} +8 -1
- package/dist/player/index.cjs +473 -123
- package/dist/player/index.cjs.map +1 -1
- package/dist/player/index.d.cts +14 -4
- package/dist/player/index.d.ts +14 -4
- package/dist/player/index.js +87 -62
- package/dist/player/index.js.map +1 -1
- package/dist/{schedule-DjcqM6A4.d.ts → schedule-ClyON98C.d.ts} +23 -11
- package/dist/{schedule-C4sP7uFu.d.cts → schedule-V-fgepvP.d.cts} +23 -11
- package/dist/spec/index.cjs +4 -2
- package/dist/spec/index.cjs.map +1 -1
- package/dist/spec/index.d.cts +2 -2
- package/dist/spec/index.d.ts +2 -2
- package/dist/spec/index.js +2 -2
- package/dist/viewer/index.cjs +369 -62
- package/dist/viewer/index.cjs.map +1 -1
- package/dist/viewer/index.d.cts +2 -2
- package/dist/viewer/index.d.ts +2 -2
- package/dist/viewer/index.js +3 -2
- package/dist/viewer/index.js.map +1 -1
- package/package.json +1 -1
- package/dist/batch-RAM2S4QO.js.map +0 -1
- package/dist/chunk-CF3JWIHP.js.map +0 -1
- package/dist/chunk-LRIAUFKF.js.map +0 -1
- package/dist/chunk-SR7NFEGL.js.map +0 -1
package/dist/player/index.d.cts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { S as Swaralipi, T as Taal, O as Octave, a as SvaraLetter, N as Note } from '../schema-DZag9qkF.cjs';
|
|
2
|
-
import { a as LaySpeeds, T as TaalId } from '../lay-
|
|
3
|
-
export { b as LAYS, c as LAY_SPEEDS, L as Lay, p as printedLay, r as resolveLaySpeeds, d as rowLay } from '../lay-
|
|
4
|
-
import { I as InstrumentId, a as InstrumentDef, S as Schedule, D as DrumVoice, T as TempoSegment } from '../schedule-
|
|
5
|
-
export { A as ARC_RAW_RE, b as DEFAULT_SOUNDS_BASE, c as DRUM_KIT_BASE, G as GRACE_STEP, d as GlideLeg, e as INSTRUMENTS, L as Line, M as MEEND_HOLD, P as PlayEvent, f as SALAMANDER_BASE, g as SAMPLED_TAALS, h as ScheduleOptions, i as TABLA_BPMS, j as TABLA_VARIATIONS, k as TONEJS_INSTRUMENTS_BASE, l as ToneModule, m as buildSchedule, n as createBuffers, o as drumPattern, p as eventAt, q as glideTargets, r as graceTouches, s as instrumentSampleUrl, t as isArcRaw, u as meendContinuations, v as nearestSampleKey, w as nearestTablaBpm, x as normalizeBase, y as probeUrl, z as quantizeLoopEnd, B as santoorUrls, C as scheduleLines, E as subsetUrls, F as tablaBpms, H as tablaUrl, J as triggerGlide } from '../schedule-
|
|
2
|
+
import { a as LaySpeeds, T as TaalId } from '../lay-CXhC-HRO.cjs';
|
|
3
|
+
export { b as LAYS, c as LAY_SPEEDS, L as Lay, p as printedLay, r as resolveLaySpeeds, d as rowLay } from '../lay-CXhC-HRO.cjs';
|
|
4
|
+
import { I as InstrumentId, a as InstrumentDef, S as Schedule, D as DrumVoice, T as TempoSegment } from '../schedule-V-fgepvP.cjs';
|
|
5
|
+
export { A as ARC_RAW_RE, b as DEFAULT_SOUNDS_BASE, c as DRUM_KIT_BASE, G as GRACE_STEP, d as GlideLeg, e as INSTRUMENTS, L as Line, M as MEEND_HOLD, P as PlayEvent, f as SALAMANDER_BASE, g as SAMPLED_TAALS, h as ScheduleOptions, i as TABLA_BPMS, j as TABLA_VARIATIONS, k as TONEJS_INSTRUMENTS_BASE, l as ToneModule, m as buildSchedule, n as createBuffers, o as drumPattern, p as eventAt, q as glideTargets, r as graceTouches, s as instrumentSampleUrl, t as isArcRaw, u as meendContinuations, v as nearestSampleKey, w as nearestTablaBpm, x as normalizeBase, y as probeUrl, z as quantizeLoopEnd, B as santoorUrls, C as scheduleLines, E as subsetUrls, F as tablaBpms, H as tablaUrl, J as triggerGlide } from '../schedule-V-fgepvP.cjs';
|
|
6
6
|
import 'zod';
|
|
7
7
|
import 'tone';
|
|
8
8
|
|
|
@@ -57,6 +57,10 @@ interface PlayerOptions {
|
|
|
57
57
|
rhythm?: RhythmMode;
|
|
58
58
|
/** Play the tanpura drone. Default true. */
|
|
59
59
|
tanpura?: boolean;
|
|
60
|
+
/** Sing the song's form: each `{ }` twice, `( )` only the first time, the
|
|
61
|
+
* `[ ]` notes on the repeat, and back to the asthayi at each kali's end
|
|
62
|
+
* (when a ‖ says where to stop). Default true; false plays it as written. */
|
|
63
|
+
repeats?: boolean;
|
|
60
64
|
/** Stop playback after this many seconds (a policy-neutral preview cap).
|
|
61
65
|
* Default `Infinity` (unlimited). The library only enforces the number it is
|
|
62
66
|
* handed; who is capped / the value / the upsell is the consumer's policy. */
|
|
@@ -260,6 +264,9 @@ interface AudioExportOptions {
|
|
|
260
264
|
taal?: TaalId | 'auto';
|
|
261
265
|
/** Include the tanpura drone. Default false. */
|
|
262
266
|
tanpura?: boolean;
|
|
267
|
+
/** Sing the song's form (repeats, returns to the asthayi), as the player
|
|
268
|
+
* does. Default true; false keeps the written order. */
|
|
269
|
+
repeats?: boolean;
|
|
263
270
|
/** Output format. Default `wav` (lossless, zero-dependency). `mp3` needs the
|
|
264
271
|
* optional peer dep `@breezystack/lamejs`. */
|
|
265
272
|
format?: AudioFormat;
|
|
@@ -336,6 +343,9 @@ interface MidiExportOptions {
|
|
|
336
343
|
name?: string;
|
|
337
344
|
/** Also export the taal's drumset pattern as a separate GM channel-10 track. */
|
|
338
345
|
drums?: boolean;
|
|
346
|
+
/** Sing the song's form (repeats, returns to the asthayi), as the player
|
|
347
|
+
* does. Default true; false keeps the written order. */
|
|
348
|
+
repeats?: boolean;
|
|
339
349
|
}
|
|
340
350
|
interface MidiExportResult {
|
|
341
351
|
blob: Blob;
|
package/dist/player/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { S as Swaralipi, T as Taal, O as Octave, a as SvaraLetter, N as Note } from '../schema-DZag9qkF.js';
|
|
2
|
-
import { a as LaySpeeds, T as TaalId } from '../lay-
|
|
3
|
-
export { b as LAYS, c as LAY_SPEEDS, L as Lay, p as printedLay, r as resolveLaySpeeds, d as rowLay } from '../lay-
|
|
4
|
-
import { I as InstrumentId, a as InstrumentDef, S as Schedule, D as DrumVoice, T as TempoSegment } from '../schedule-
|
|
5
|
-
export { A as ARC_RAW_RE, b as DEFAULT_SOUNDS_BASE, c as DRUM_KIT_BASE, G as GRACE_STEP, d as GlideLeg, e as INSTRUMENTS, L as Line, M as MEEND_HOLD, P as PlayEvent, f as SALAMANDER_BASE, g as SAMPLED_TAALS, h as ScheduleOptions, i as TABLA_BPMS, j as TABLA_VARIATIONS, k as TONEJS_INSTRUMENTS_BASE, l as ToneModule, m as buildSchedule, n as createBuffers, o as drumPattern, p as eventAt, q as glideTargets, r as graceTouches, s as instrumentSampleUrl, t as isArcRaw, u as meendContinuations, v as nearestSampleKey, w as nearestTablaBpm, x as normalizeBase, y as probeUrl, z as quantizeLoopEnd, B as santoorUrls, C as scheduleLines, E as subsetUrls, F as tablaBpms, H as tablaUrl, J as triggerGlide } from '../schedule-
|
|
2
|
+
import { a as LaySpeeds, T as TaalId } from '../lay-CWlEhImP.js';
|
|
3
|
+
export { b as LAYS, c as LAY_SPEEDS, L as Lay, p as printedLay, r as resolveLaySpeeds, d as rowLay } from '../lay-CWlEhImP.js';
|
|
4
|
+
import { I as InstrumentId, a as InstrumentDef, S as Schedule, D as DrumVoice, T as TempoSegment } from '../schedule-ClyON98C.js';
|
|
5
|
+
export { A as ARC_RAW_RE, b as DEFAULT_SOUNDS_BASE, c as DRUM_KIT_BASE, G as GRACE_STEP, d as GlideLeg, e as INSTRUMENTS, L as Line, M as MEEND_HOLD, P as PlayEvent, f as SALAMANDER_BASE, g as SAMPLED_TAALS, h as ScheduleOptions, i as TABLA_BPMS, j as TABLA_VARIATIONS, k as TONEJS_INSTRUMENTS_BASE, l as ToneModule, m as buildSchedule, n as createBuffers, o as drumPattern, p as eventAt, q as glideTargets, r as graceTouches, s as instrumentSampleUrl, t as isArcRaw, u as meendContinuations, v as nearestSampleKey, w as nearestTablaBpm, x as normalizeBase, y as probeUrl, z as quantizeLoopEnd, B as santoorUrls, C as scheduleLines, E as subsetUrls, F as tablaBpms, H as tablaUrl, J as triggerGlide } from '../schedule-ClyON98C.js';
|
|
6
6
|
import 'zod';
|
|
7
7
|
import 'tone';
|
|
8
8
|
|
|
@@ -57,6 +57,10 @@ interface PlayerOptions {
|
|
|
57
57
|
rhythm?: RhythmMode;
|
|
58
58
|
/** Play the tanpura drone. Default true. */
|
|
59
59
|
tanpura?: boolean;
|
|
60
|
+
/** Sing the song's form: each `{ }` twice, `( )` only the first time, the
|
|
61
|
+
* `[ ]` notes on the repeat, and back to the asthayi at each kali's end
|
|
62
|
+
* (when a ‖ says where to stop). Default true; false plays it as written. */
|
|
63
|
+
repeats?: boolean;
|
|
60
64
|
/** Stop playback after this many seconds (a policy-neutral preview cap).
|
|
61
65
|
* Default `Infinity` (unlimited). The library only enforces the number it is
|
|
62
66
|
* handed; who is capped / the value / the upsell is the consumer's policy. */
|
|
@@ -260,6 +264,9 @@ interface AudioExportOptions {
|
|
|
260
264
|
taal?: TaalId | 'auto';
|
|
261
265
|
/** Include the tanpura drone. Default false. */
|
|
262
266
|
tanpura?: boolean;
|
|
267
|
+
/** Sing the song's form (repeats, returns to the asthayi), as the player
|
|
268
|
+
* does. Default true; false keeps the written order. */
|
|
269
|
+
repeats?: boolean;
|
|
263
270
|
/** Output format. Default `wav` (lossless, zero-dependency). `mp3` needs the
|
|
264
271
|
* optional peer dep `@breezystack/lamejs`. */
|
|
265
272
|
format?: AudioFormat;
|
|
@@ -336,6 +343,9 @@ interface MidiExportOptions {
|
|
|
336
343
|
name?: string;
|
|
337
344
|
/** Also export the taal's drumset pattern as a separate GM channel-10 track. */
|
|
338
345
|
drums?: boolean;
|
|
346
|
+
/** Sing the song's form (repeats, returns to the asthayi), as the player
|
|
347
|
+
* does. Default true; false keeps the written order. */
|
|
348
|
+
repeats?: boolean;
|
|
339
349
|
}
|
|
340
350
|
interface MidiExportResult {
|
|
341
351
|
blob: Blob;
|
package/dist/player/index.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { DEFAULT_SOUNDS_BASE, INSTRUMENTS, SAMPLED_TAALS, buildSchedule, eventAt, quantizeLoopEnd, instrumentSampleUrl, probeUrl, scheduleLines, GRACE_STEP, normalizeBase, nearestTablaBpm, tablaBpms, graceTouches, midiToSampleName, glideTargets, triggerGlide, createSampler, createBuffers, tablaShiftSemitones, tablaUrl, TABLA_GRAIN_OVERLAP_SEC, TABLA_GRAIN_SEC, createDrumKit, drumPattern, createMetronome, TANPURA_PCS, createTanpura, parseTonic, MEEND_HOLD, sCurve } from '../chunk-
|
|
2
|
-
export { ARC_RAW_RE, DEFAULT_SOUNDS_BASE, DRUM_KIT_BASE, GRACE_STEP, INSTRUMENTS, MEEND_HOLD, OCTAVE_OFFSET, SALAMANDER_BASE, SAMPLED_TAALS, SHUDDHA, TABLA_BPMS, TABLA_VARIATIONS, TONEJS_INSTRUMENTS_BASE, buildSchedule, createBuffers, drumPattern, eventAt, glideTargets, graceTouches, instrumentSampleUrl, isArcRaw, meendContinuations, midiToSampleName, nearestSampleKey, nearestTablaBpm, normalizeBase, parseTonic, probeUrl, quantizeLoopEnd, sCurve, sampleFileName, santoorUrls, scheduleLines, subsetUrls, svaraSemitone, svaraToMidi, tablaBpms, tablaUrl, triggerGlide } from '../chunk-
|
|
3
|
-
import
|
|
4
|
-
|
|
1
|
+
import { DEFAULT_SOUNDS_BASE, INSTRUMENTS, SAMPLED_TAALS, buildSchedule, eventAt, lineAt, byRow, lineFor, quantizeLoopEnd, instrumentSampleUrl, probeUrl, scheduleLines, GRACE_STEP, normalizeBase, nearestTablaBpm, tablaBpms, graceTouches, midiToSampleName, glideTargets, triggerGlide, createSampler, createBuffers, tablaShiftSemitones, tablaUrl, TABLA_GRAIN_OVERLAP_SEC, TABLA_GRAIN_SEC, createDrumKit, drumPattern, createMetronome, TANPURA_PCS, createTanpura, parseTonic, MEEND_HOLD, sCurve } from '../chunk-ZNJTMQIX.js';
|
|
2
|
+
export { ARC_RAW_RE, DEFAULT_SOUNDS_BASE, DRUM_KIT_BASE, GRACE_STEP, INSTRUMENTS, MEEND_HOLD, OCTAVE_OFFSET, SALAMANDER_BASE, SAMPLED_TAALS, SHUDDHA, TABLA_BPMS, TABLA_VARIATIONS, TONEJS_INSTRUMENTS_BASE, buildSchedule, createBuffers, drumPattern, eventAt, glideTargets, graceTouches, instrumentSampleUrl, isArcRaw, meendContinuations, midiToSampleName, nearestSampleKey, nearestTablaBpm, normalizeBase, parseTonic, probeUrl, quantizeLoopEnd, sCurve, sampleFileName, santoorUrls, scheduleLines, subsetUrls, svaraSemitone, svaraToMidi, tablaBpms, tablaUrl, triggerGlide } from '../chunk-ZNJTMQIX.js';
|
|
3
|
+
import '../chunk-BSZ3WQUP.js';
|
|
4
|
+
import { TAALS } from '../chunk-KOHSKHXI.js';
|
|
5
|
+
export { LAYS, LAY_SPEEDS, printedLay, resolveLaySpeeds, rowLay } from '../chunk-KOHSKHXI.js';
|
|
5
6
|
|
|
6
7
|
// src/player/player.ts
|
|
7
8
|
var STOP_FADE_SEC = 0.5;
|
|
@@ -11,14 +12,6 @@ var TAAL_MODE_TEMPO = [
|
|
|
11
12
|
{ beat: 0, endBeat: Infinity, scale: 1, lay: "drut" }
|
|
12
13
|
];
|
|
13
14
|
var MEDIA_LEAD_SEC = 0.25;
|
|
14
|
-
function lineOf(lines, path) {
|
|
15
|
-
const [p = 0, r = 0] = path.split(".").map(Number);
|
|
16
|
-
const k = lines.findIndex((line) => {
|
|
17
|
-
const [lp = 0, lr = 0] = line.row.split(".").map(Number);
|
|
18
|
-
return lp > p || lp === p && lr >= r;
|
|
19
|
-
});
|
|
20
|
-
return k < 0 ? lines.length - 1 : k;
|
|
21
|
-
}
|
|
22
15
|
var seekableGrain = (Tone) => class extends Tone.GrainPlayer {
|
|
23
16
|
_start(time, offset, duration) {
|
|
24
17
|
super._start(time, offset === void 0 ? offset : this.toSeconds(offset) * this.playbackRate, duration);
|
|
@@ -79,9 +72,13 @@ var SwaralipiPlayer = class {
|
|
|
79
72
|
/* Practice: where the playhead is, what repeats, whose turn it is. */
|
|
80
73
|
/** The last note played or sought — what "this line" means to the controls. */
|
|
81
74
|
#cursor = null;
|
|
75
|
+
/** The cursor as an event index: which pass of a line sung more than once. */
|
|
76
|
+
#at = -1;
|
|
82
77
|
/** Where the next fresh play() starts (a seek made while stopped). */
|
|
83
78
|
#startAt = null;
|
|
84
79
|
#loop = null;
|
|
80
|
+
/** The repeat as line indices, the passes it holds. */
|
|
81
|
+
#span = null;
|
|
85
82
|
#echo = false;
|
|
86
83
|
/** With echo on, every other pass is the learner's: the melody rests. */
|
|
87
84
|
#turn = false;
|
|
@@ -107,6 +104,7 @@ var SwaralipiPlayer = class {
|
|
|
107
104
|
instruments: options.instruments ?? Object.keys(INSTRUMENTS),
|
|
108
105
|
rhythm: options.rhythm ?? "tabla",
|
|
109
106
|
tanpura: options.tanpura ?? true,
|
|
107
|
+
repeats: options.repeats ?? true,
|
|
110
108
|
previewLimitSec: options.previewLimitSec ?? Infinity,
|
|
111
109
|
onHighlight: options.onHighlight,
|
|
112
110
|
onLoop: options.onLoop,
|
|
@@ -254,22 +252,25 @@ var SwaralipiPlayer = class {
|
|
|
254
252
|
*/
|
|
255
253
|
seek(path) {
|
|
256
254
|
const plan = this.#plan();
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
255
|
+
if (plan !== void 0) this.#seekTo(plan, eventAt(plan.schedule.events, path, this.#at));
|
|
256
|
+
}
|
|
257
|
+
/** seek(), by event index — the pass, not just the line. */
|
|
258
|
+
#seekTo(plan, i) {
|
|
259
|
+
if (i < 0) return;
|
|
260
260
|
const at = plan.schedule.events[i].path;
|
|
261
261
|
this.#cursor = at;
|
|
262
|
+
this.#at = i;
|
|
262
263
|
this.#turn = false;
|
|
263
|
-
const k =
|
|
264
|
-
const [a, b] = this.#loopSpan(
|
|
265
|
-
if (this.#
|
|
264
|
+
const k = lineAt(plan.lines, i);
|
|
265
|
+
const [a, b] = this.#loopSpan();
|
|
266
|
+
if (this.#span !== null && (k < a || k > b)) this.#setSpan(plan.lines, k, k);
|
|
266
267
|
this.#emitLoop();
|
|
267
268
|
if (this.#timeline !== void 0 && (this.#state === "playing" || this.#state === "paused")) {
|
|
268
269
|
this.#applyLoop();
|
|
269
270
|
this.#jump(this.#eventSec(i));
|
|
270
271
|
if (this.#state === "paused") this.#emitHighlight(at);
|
|
271
272
|
} else {
|
|
272
|
-
this.#startAt =
|
|
273
|
+
this.#startAt = i;
|
|
273
274
|
this.#emitHighlight(at);
|
|
274
275
|
}
|
|
275
276
|
}
|
|
@@ -282,20 +283,41 @@ var SwaralipiPlayer = class {
|
|
|
282
283
|
setLoop(from, to) {
|
|
283
284
|
this.#turn = false;
|
|
284
285
|
if (from === null) {
|
|
285
|
-
this.#loop = null;
|
|
286
|
+
this.#span = this.#loop = null;
|
|
286
287
|
this.#echo = false;
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
const here = this.#cursor === null ? -1 : lineOf(lines, this.#cursor);
|
|
291
|
-
const a = from === void 0 ? Math.max(here, 0) : lineOf(lines, from);
|
|
292
|
-
const b = to === void 0 ? a : lineOf(lines, to);
|
|
293
|
-
this.#loop = { from: lines[Math.min(a, b)].row, to: lines[Math.max(a, b)].row };
|
|
294
|
-
if (here < Math.min(a, b) || here > Math.max(a, b)) return this.seek(this.#loop.from);
|
|
288
|
+
this.#applyLoop();
|
|
289
|
+
this.#emitLoop();
|
|
290
|
+
return;
|
|
295
291
|
}
|
|
292
|
+
const plan = this.#plan();
|
|
293
|
+
if (plan === void 0 || plan.lines.length === 0) return;
|
|
294
|
+
const { lines } = plan;
|
|
295
|
+
const here = this.#at < 0 ? -1 : lineAt(lines, this.#at);
|
|
296
|
+
const a = from === void 0 ? Math.max(here, 0) : this.#lineOf(lines, from, here);
|
|
297
|
+
this.#repeat(plan, a, to === void 0 ? a : this.#lineOf(lines, to, a));
|
|
298
|
+
}
|
|
299
|
+
/** Repeat lines a..b (indices, either order); the playhead joins them. */
|
|
300
|
+
#repeat(plan, a, b) {
|
|
301
|
+
this.#setSpan(plan.lines, a, b);
|
|
302
|
+
const [lo, hi] = this.#loopSpan();
|
|
303
|
+
const here = this.#at < 0 ? -1 : lineAt(plan.lines, this.#at);
|
|
304
|
+
if (here < lo || here > hi) return this.#seekTo(plan, plan.lines[lo].first);
|
|
296
305
|
this.#applyLoop();
|
|
297
306
|
this.#emitLoop();
|
|
298
307
|
}
|
|
308
|
+
/** The repeat's passes, and the rows they cover for the sheet's shading —
|
|
309
|
+
* written order, since a span can cross a jump back to an earlier row. */
|
|
310
|
+
#setSpan(lines, a, b) {
|
|
311
|
+
this.#span = [Math.min(a, b), Math.max(a, b)];
|
|
312
|
+
const rows = lines.slice(this.#span[0], this.#span[1] + 1).map((line) => line.row).sort(byRow);
|
|
313
|
+
this.#loop = { from: rows[0], to: rows.at(-1) };
|
|
314
|
+
}
|
|
315
|
+
/** The line `path` names, at its pass nearest line `near`; the last line
|
|
316
|
+
* when nothing sounds from there on. */
|
|
317
|
+
#lineOf(lines, path, near) {
|
|
318
|
+
const k = lineFor(lines, path, near);
|
|
319
|
+
return k < 0 ? lines.length - 1 : k;
|
|
320
|
+
}
|
|
299
321
|
/**
|
|
300
322
|
* Follow every pass of the repeat with a silent one: the tabla and tanpura
|
|
301
323
|
* keep time and the highlight keeps moving, but the melody rests so the
|
|
@@ -317,22 +339,22 @@ var SwaralipiPlayer = class {
|
|
|
317
339
|
const plan = this.#plan();
|
|
318
340
|
if (plan === void 0 || plan.lines.length === 0) return;
|
|
319
341
|
const { lines, schedule } = plan;
|
|
320
|
-
const k = this.#
|
|
321
|
-
const into = k < 0 ? 0 : schedule.events[
|
|
342
|
+
const k = this.#at < 0 ? -1 : lineAt(lines, this.#at);
|
|
343
|
+
const into = k < 0 ? 0 : schedule.events[this.#at].beat - lines[k].beat;
|
|
322
344
|
const step = delta < 0 && this.#state === "playing" && into > 1 ? delta + 1 : delta;
|
|
323
|
-
this
|
|
345
|
+
this.#seekTo(plan, lines[Math.min(Math.max(k + step, 0), lines.length - 1)].first);
|
|
324
346
|
}
|
|
325
347
|
/** Move by single notes (rests and holds included — each is a column). */
|
|
326
348
|
stepNote(delta) {
|
|
327
|
-
const
|
|
328
|
-
|
|
349
|
+
const plan = this.#plan();
|
|
350
|
+
const events = plan?.schedule.events ?? [];
|
|
351
|
+
let i = this.#at;
|
|
329
352
|
for (let left = Math.abs(delta); left > 0; left--) {
|
|
330
353
|
do
|
|
331
354
|
i += Math.sign(delta);
|
|
332
355
|
while (events[i]?.glide === true);
|
|
333
356
|
}
|
|
334
|
-
|
|
335
|
-
if (ev !== void 0) this.seek(ev.path);
|
|
357
|
+
if (plan !== void 0 && events.length > 0) this.#seekTo(plan, Math.min(Math.max(i, 0), events.length - 1));
|
|
336
358
|
}
|
|
337
359
|
/**
|
|
338
360
|
* Stop and rewind to the start. When sounding, the accompaniment (tabla /
|
|
@@ -527,21 +549,21 @@ var SwaralipiPlayer = class {
|
|
|
527
549
|
}
|
|
528
550
|
/** Grow the repeat (or start one from the current line) to take in `path`'s line. */
|
|
529
551
|
#extendLoop(path) {
|
|
530
|
-
const
|
|
531
|
-
if (lines.length === 0) return;
|
|
532
|
-
const
|
|
533
|
-
const
|
|
534
|
-
const [a, b] = this.#
|
|
535
|
-
this
|
|
552
|
+
const plan = this.#plan();
|
|
553
|
+
if (plan === void 0 || plan.lines.length === 0) return;
|
|
554
|
+
const here = this.#at < 0 ? -1 : lineAt(plan.lines, this.#at);
|
|
555
|
+
const k = this.#lineOf(plan.lines, path, here);
|
|
556
|
+
const [a, b] = this.#span ?? (here < 0 ? [k, k] : [here, here]);
|
|
557
|
+
this.#repeat(plan, Math.min(a, k), Math.max(b, k));
|
|
536
558
|
}
|
|
537
559
|
/** Shift+↑/↓: one more line above / below the repeat. */
|
|
538
560
|
#extendBy(delta) {
|
|
539
|
-
const
|
|
540
|
-
if (lines.length === 0) return;
|
|
541
|
-
const here = this.#
|
|
542
|
-
const [a, b] = this.#
|
|
543
|
-
const k = Math.min(Math.max((delta < 0 ? a : b) + delta, 0), lines.length - 1);
|
|
544
|
-
this.#
|
|
561
|
+
const plan = this.#plan();
|
|
562
|
+
if (plan === void 0 || plan.lines.length === 0) return;
|
|
563
|
+
const here = this.#at < 0 ? 0 : lineAt(plan.lines, this.#at);
|
|
564
|
+
const [a, b] = this.#span ?? [here, here];
|
|
565
|
+
const k = Math.min(Math.max((delta < 0 ? a : b) + delta, 0), plan.lines.length - 1);
|
|
566
|
+
this.#repeat(plan, Math.min(a, k), Math.max(b, k));
|
|
545
567
|
}
|
|
546
568
|
/**
|
|
547
569
|
* Check whether an instrument's samples are reachable (its CDN is up / the
|
|
@@ -601,7 +623,8 @@ var SwaralipiPlayer = class {
|
|
|
601
623
|
}
|
|
602
624
|
/** A new document starts with nothing repeated, from the top. */
|
|
603
625
|
#resetPractice() {
|
|
604
|
-
this.#cursor = this.#startAt = this.#loop = null;
|
|
626
|
+
this.#cursor = this.#startAt = this.#loop = this.#span = null;
|
|
627
|
+
this.#at = -1;
|
|
605
628
|
this.#echo = this.#turn = false;
|
|
606
629
|
this.#listened = 0;
|
|
607
630
|
this.#emitLoop();
|
|
@@ -615,12 +638,9 @@ var SwaralipiPlayer = class {
|
|
|
615
638
|
const schedule = buildSchedule(this.#doc, this.#scheduleOpts());
|
|
616
639
|
return { schedule, lines: scheduleLines(schedule) };
|
|
617
640
|
}
|
|
618
|
-
#lines() {
|
|
619
|
-
return this.#plan()?.lines ?? [];
|
|
620
|
-
}
|
|
621
641
|
/** The repeat as line indices (`[0, -1]`, i.e. empty, when off). */
|
|
622
|
-
#loopSpan(
|
|
623
|
-
return this.#
|
|
642
|
+
#loopSpan() {
|
|
643
|
+
return this.#span ?? [0, -1];
|
|
624
644
|
}
|
|
625
645
|
/** The repeat's lines on the arranged timeline, cached for the per-note
|
|
626
646
|
* checks (#hears), which run on every note. */
|
|
@@ -649,8 +669,8 @@ var SwaralipiPlayer = class {
|
|
|
649
669
|
#applyLoop() {
|
|
650
670
|
const transport = this.#tone?.getTransport();
|
|
651
671
|
if (transport === void 0 || this.#timeline === void 0) return;
|
|
652
|
-
this.#loopLines = this.#loopSpan(
|
|
653
|
-
if (this.#
|
|
672
|
+
this.#loopLines = this.#loopSpan();
|
|
673
|
+
if (this.#span === null) {
|
|
654
674
|
transport.loop = false;
|
|
655
675
|
return;
|
|
656
676
|
}
|
|
@@ -661,8 +681,8 @@ var SwaralipiPlayer = class {
|
|
|
661
681
|
* else the top. */
|
|
662
682
|
#startSec() {
|
|
663
683
|
const events = this.#timeline?.schedule.events ?? [];
|
|
664
|
-
if (this.#startAt !== null) return this.#eventSec(Math.
|
|
665
|
-
return this.#
|
|
684
|
+
if (this.#startAt !== null) return this.#eventSec(Math.min(this.#startAt, events.length - 1));
|
|
685
|
+
return this.#span === null ? 0 : this.#loopSec()[0];
|
|
666
686
|
}
|
|
667
687
|
/** Move the transport; notes still ringing from the old place are let go. */
|
|
668
688
|
#jump(sec) {
|
|
@@ -751,7 +771,8 @@ var SwaralipiPlayer = class {
|
|
|
751
771
|
return {
|
|
752
772
|
tonic: this.#opts.tonic,
|
|
753
773
|
baseOctave: this.#opts.baseOctave,
|
|
754
|
-
laySpeeds: this.#opts.laySpeeds
|
|
774
|
+
laySpeeds: this.#opts.laySpeeds,
|
|
775
|
+
repeats: this.#opts.repeats
|
|
755
776
|
};
|
|
756
777
|
}
|
|
757
778
|
/** Effective tempo: snapped to the taal's available tabla tempos while a
|
|
@@ -769,9 +790,10 @@ var SwaralipiPlayer = class {
|
|
|
769
790
|
* from the note it was on — slowing down a repeated line keeps repeating it. */
|
|
770
791
|
#reapply() {
|
|
771
792
|
if (this.#state !== "playing") return;
|
|
772
|
-
const at = this.#
|
|
793
|
+
const at = this.#at;
|
|
773
794
|
this.#halt();
|
|
774
|
-
|
|
795
|
+
this.#startAt = at < 0 ? null : at;
|
|
796
|
+
void this.play();
|
|
775
797
|
}
|
|
776
798
|
/** The master bus everything routes through (created once, → destination). */
|
|
777
799
|
#ensureMaster(Tone) {
|
|
@@ -863,6 +885,7 @@ var SwaralipiPlayer = class {
|
|
|
863
885
|
if (!heard(time)) return;
|
|
864
886
|
const turn = this.#turn;
|
|
865
887
|
this.#cursor = ev.path;
|
|
888
|
+
this.#at = i;
|
|
866
889
|
draw.schedule(() => this.#emitHighlight(ev.path, turn), time);
|
|
867
890
|
}, at);
|
|
868
891
|
});
|
|
@@ -1099,7 +1122,8 @@ async function exportAudio(doc, options = {}) {
|
|
|
1099
1122
|
const schedule = buildSchedule(doc, {
|
|
1100
1123
|
tonic: options.tonic,
|
|
1101
1124
|
baseOctave: options.baseOctave,
|
|
1102
|
-
laySpeeds: options.laySpeeds
|
|
1125
|
+
laySpeeds: options.laySpeeds,
|
|
1126
|
+
repeats: options.repeats ?? true
|
|
1103
1127
|
});
|
|
1104
1128
|
const range = midiRange2(schedule);
|
|
1105
1129
|
const taalId = resolveTaal(doc, options.taal);
|
|
@@ -1472,7 +1496,8 @@ function exportMidi(doc, options = {}) {
|
|
|
1472
1496
|
const schedule = buildSchedule(doc, {
|
|
1473
1497
|
tonic: options.tonic,
|
|
1474
1498
|
baseOctave: options.baseOctave,
|
|
1475
|
-
laySpeeds: options.laySpeeds
|
|
1499
|
+
laySpeeds: options.laySpeeds,
|
|
1500
|
+
repeats: options.repeats ?? true
|
|
1476
1501
|
});
|
|
1477
1502
|
const bpm = options.bpm ?? doc.meta.bpm ?? 90;
|
|
1478
1503
|
const drums = options.drums ? (seg) => {
|