@waveform-playlist/core 11.3.0 → 12.0.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/dist/index.d.mts +181 -3
- package/dist/index.d.ts +181 -3
- package/dist/index.js +379 -12
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +364 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -22,24 +22,35 @@ var index_exports = {};
|
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
InteractionState: () => InteractionState,
|
|
24
24
|
MAX_CANVAS_WIDTH: () => MAX_CANVAS_WIDTH,
|
|
25
|
+
MIN_PIXELS_PER_UNIT: () => MIN_PIXELS_PER_UNIT,
|
|
25
26
|
PPQN: () => PPQN,
|
|
27
|
+
appendPeaks: () => appendPeaks,
|
|
28
|
+
appendToAudioBuffer: () => appendToAudioBuffer,
|
|
26
29
|
applyFadeIn: () => applyFadeIn,
|
|
27
30
|
applyFadeOut: () => applyFadeOut,
|
|
31
|
+
calculateDuration: () => calculateDuration,
|
|
28
32
|
clipDurationTime: () => clipDurationTime,
|
|
29
33
|
clipEndTime: () => clipEndTime,
|
|
30
34
|
clipOffsetTime: () => clipOffsetTime,
|
|
31
35
|
clipPixelWidth: () => clipPixelWidth,
|
|
32
36
|
clipStartTime: () => clipStartTime,
|
|
33
37
|
clipsOverlap: () => clipsOverlap,
|
|
38
|
+
computeMusicalTicks: () => computeMusicalTicks,
|
|
39
|
+
concatenateAudioData: () => concatenateAudioData,
|
|
40
|
+
createAudioBuffer: () => createAudioBuffer,
|
|
34
41
|
createClip: () => createClip,
|
|
35
42
|
createClipFromSeconds: () => createClipFromSeconds,
|
|
43
|
+
createClipFromTicks: () => createClipFromTicks,
|
|
36
44
|
createTimeline: () => createTimeline,
|
|
37
45
|
createTrack: () => createTrack,
|
|
38
46
|
dBToNormalized: () => dBToNormalized,
|
|
47
|
+
detectMeterChanges: () => detectMeterChanges,
|
|
39
48
|
exponentialCurve: () => exponentialCurve,
|
|
40
49
|
findGaps: () => findGaps,
|
|
50
|
+
gainToDb: () => gainToDb,
|
|
41
51
|
gainToNormalized: () => gainToNormalized,
|
|
42
52
|
generateCurve: () => generateCurve,
|
|
53
|
+
generatePeaks: () => generatePeaks,
|
|
43
54
|
getClipsAtSample: () => getClipsAtSample,
|
|
44
55
|
getClipsInRange: () => getClipsInRange,
|
|
45
56
|
getShortcutLabel: () => getShortcutLabel,
|
|
@@ -55,12 +66,14 @@ __export(index_exports, {
|
|
|
55
66
|
samplesToTicks: () => samplesToTicks,
|
|
56
67
|
secondsToPixels: () => secondsToPixels,
|
|
57
68
|
secondsToSamples: () => secondsToSamples,
|
|
69
|
+
snapTickToGrid: () => snapTickToGrid,
|
|
58
70
|
snapToGrid: () => snapToGrid,
|
|
71
|
+
snapToTicks: () => snapToTicks,
|
|
59
72
|
sortClipsByTime: () => sortClipsByTime,
|
|
60
73
|
ticksPerBar: () => ticksPerBar,
|
|
61
74
|
ticksPerBeat: () => ticksPerBeat,
|
|
62
|
-
|
|
63
|
-
|
|
75
|
+
ticksToSamples: () => ticksToSamples,
|
|
76
|
+
trackChannelCount: () => trackChannelCount
|
|
64
77
|
});
|
|
65
78
|
module.exports = __toCommonJS(index_exports);
|
|
66
79
|
|
|
@@ -68,10 +81,50 @@ module.exports = __toCommonJS(index_exports);
|
|
|
68
81
|
var MAX_CANVAS_WIDTH = 1e3;
|
|
69
82
|
|
|
70
83
|
// src/types/clip.ts
|
|
84
|
+
function createClipFromTicks(options) {
|
|
85
|
+
const { startTick, ticksToSeconds, bpm, ppqn } = options;
|
|
86
|
+
if (startTick < 0) {
|
|
87
|
+
throw new Error("createClipFromTicks: startTick must be non-negative");
|
|
88
|
+
}
|
|
89
|
+
let toSeconds;
|
|
90
|
+
if (ticksToSeconds) {
|
|
91
|
+
toSeconds = ticksToSeconds;
|
|
92
|
+
} else if (bpm !== void 0 && ppqn !== void 0) {
|
|
93
|
+
toSeconds = (tick) => tick * 60 / (ppqn * bpm);
|
|
94
|
+
} else {
|
|
95
|
+
throw new Error(
|
|
96
|
+
"createClipFromTicks: either ticksToSeconds callback or both bpm and ppqn are required"
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
const sampleRate = options.audioBuffer?.sampleRate ?? options.sampleRate ?? options.waveformData?.sample_rate;
|
|
100
|
+
if (sampleRate === void 0) {
|
|
101
|
+
throw new Error("createClipFromTicks: sampleRate is required when audioBuffer is not provided");
|
|
102
|
+
}
|
|
103
|
+
const startSample = Math.round(toSeconds(startTick) * sampleRate);
|
|
104
|
+
return createClip({
|
|
105
|
+
audioBuffer: options.audioBuffer,
|
|
106
|
+
startSample,
|
|
107
|
+
startTick,
|
|
108
|
+
durationSamples: options.durationSamples,
|
|
109
|
+
offsetSamples: options.offsetSamples,
|
|
110
|
+
gain: options.gain,
|
|
111
|
+
name: options.name,
|
|
112
|
+
color: options.color,
|
|
113
|
+
fadeIn: options.fadeIn,
|
|
114
|
+
fadeOut: options.fadeOut,
|
|
115
|
+
waveformData: options.waveformData,
|
|
116
|
+
sampleRate: options.sampleRate,
|
|
117
|
+
sourceDurationSamples: options.sourceDurationSamples,
|
|
118
|
+
midiNotes: options.midiNotes,
|
|
119
|
+
midiChannel: options.midiChannel,
|
|
120
|
+
midiProgram: options.midiProgram
|
|
121
|
+
});
|
|
122
|
+
}
|
|
71
123
|
function createClip(options) {
|
|
72
124
|
const {
|
|
73
125
|
audioBuffer,
|
|
74
126
|
startSample,
|
|
127
|
+
startTick,
|
|
75
128
|
offsetSamples = 0,
|
|
76
129
|
gain = 1,
|
|
77
130
|
name,
|
|
@@ -105,6 +158,7 @@ function createClip(options) {
|
|
|
105
158
|
id: generateId(),
|
|
106
159
|
audioBuffer,
|
|
107
160
|
startSample,
|
|
161
|
+
startTick,
|
|
108
162
|
durationSamples,
|
|
109
163
|
offsetSamples,
|
|
110
164
|
sampleRate,
|
|
@@ -151,6 +205,7 @@ function createClipFromSeconds(options) {
|
|
|
151
205
|
return createClip({
|
|
152
206
|
audioBuffer,
|
|
153
207
|
startSample: Math.round(startTime * sampleRate),
|
|
208
|
+
startTick: options.startTick,
|
|
154
209
|
durationSamples: Math.round(duration * sampleRate),
|
|
155
210
|
offsetSamples: Math.round(offset * sampleRate),
|
|
156
211
|
sampleRate,
|
|
@@ -300,14 +355,6 @@ function samplesToTicks(samples, bpm, sampleRate, ppqn = PPQN) {
|
|
|
300
355
|
function snapToGrid(ticks, gridSizeTicks) {
|
|
301
356
|
return Math.round(ticks / gridSizeTicks) * gridSizeTicks;
|
|
302
357
|
}
|
|
303
|
-
function ticksToBarBeatLabel(ticks, timeSignature, ppqn = PPQN) {
|
|
304
|
-
const barTicks = ticksPerBar(timeSignature, ppqn);
|
|
305
|
-
const beatTicks = ticksPerBeat(timeSignature, ppqn);
|
|
306
|
-
const bar = Math.floor(ticks / barTicks) + 1;
|
|
307
|
-
const beatInBar = Math.floor(ticks % barTicks / beatTicks) + 1;
|
|
308
|
-
if (beatInBar === 1) return `${bar}`;
|
|
309
|
-
return `${bar}.${beatInBar}`;
|
|
310
|
-
}
|
|
311
358
|
|
|
312
359
|
// src/utils/dBUtils.ts
|
|
313
360
|
var DEFAULT_FLOOR = -100;
|
|
@@ -332,12 +379,313 @@ function normalizedToDb(normalized, floor = DEFAULT_FLOOR) {
|
|
|
332
379
|
const clamped = Math.max(0, normalized);
|
|
333
380
|
return clamped * -floor + floor;
|
|
334
381
|
}
|
|
382
|
+
function gainToDb(gain) {
|
|
383
|
+
return 20 * Math.log10(Math.max(gain, 1e-4));
|
|
384
|
+
}
|
|
335
385
|
function gainToNormalized(gain, floor = DEFAULT_FLOOR) {
|
|
336
386
|
if (gain <= 0) return 0;
|
|
337
387
|
const db = 20 * Math.log10(gain);
|
|
338
388
|
return dBToNormalized(db, floor);
|
|
339
389
|
}
|
|
340
390
|
|
|
391
|
+
// src/utils/musicalTicks.ts
|
|
392
|
+
function snapToTicks(snapTo, timeSignature, ppqn = 960) {
|
|
393
|
+
const ts = timeSignature;
|
|
394
|
+
switch (snapTo) {
|
|
395
|
+
case "bar":
|
|
396
|
+
return ticksPerBar(ts, ppqn);
|
|
397
|
+
case "beat":
|
|
398
|
+
return ticksPerBeat(ts, ppqn);
|
|
399
|
+
case "1/2":
|
|
400
|
+
return ppqn * 2;
|
|
401
|
+
case "1/4":
|
|
402
|
+
return ppqn;
|
|
403
|
+
case "1/8":
|
|
404
|
+
return ppqn / 2;
|
|
405
|
+
case "1/16":
|
|
406
|
+
return ppqn / 4;
|
|
407
|
+
case "1/32":
|
|
408
|
+
return ppqn / 8;
|
|
409
|
+
case "1/2T":
|
|
410
|
+
return Math.round(ppqn * 2 * 2 / 3);
|
|
411
|
+
case "1/4T":
|
|
412
|
+
return Math.round(ppqn * 2 / 3);
|
|
413
|
+
case "1/8T":
|
|
414
|
+
return Math.round(ppqn * 2 / 6);
|
|
415
|
+
case "1/16T":
|
|
416
|
+
return Math.round(ppqn * 2 / 12);
|
|
417
|
+
case "off":
|
|
418
|
+
return 0;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
var MIN_PIXELS_PER_UNIT = 8;
|
|
422
|
+
var MIN_PIXELS_PER_LABEL = 60;
|
|
423
|
+
function computeMusicalTicks(params) {
|
|
424
|
+
const { meterEntries, ticksPerPixel, startPixel, endPixel, ppqn = 960 } = params;
|
|
425
|
+
const firstMeter = meterEntries[0] ?? { tick: 0, numerator: 4, denominator: 4 };
|
|
426
|
+
if (ticksPerPixel <= 0 || ppqn <= 0 || firstMeter.denominator <= 0) {
|
|
427
|
+
return { ticks: [], pixelsPerQuarterNote: 0, zoomLevel: "coarse" };
|
|
428
|
+
}
|
|
429
|
+
const pixelsPerQuarterNote = ppqn / ticksPerPixel;
|
|
430
|
+
const tpEighth = ppqn / 2;
|
|
431
|
+
const tpSixteenth = ppqn / 4;
|
|
432
|
+
const pixelsPerEighth = tpEighth / ticksPerPixel;
|
|
433
|
+
const pixelsPerSixteenth = tpSixteenth / ticksPerPixel;
|
|
434
|
+
let zoomLevel;
|
|
435
|
+
if (pixelsPerQuarterNote * 4 < MIN_PIXELS_PER_UNIT) {
|
|
436
|
+
zoomLevel = "coarse";
|
|
437
|
+
} else if (pixelsPerQuarterNote < MIN_PIXELS_PER_UNIT) {
|
|
438
|
+
zoomLevel = "bar";
|
|
439
|
+
} else if (pixelsPerEighth < MIN_PIXELS_PER_UNIT) {
|
|
440
|
+
zoomLevel = "beat";
|
|
441
|
+
} else if (pixelsPerSixteenth < MIN_PIXELS_PER_UNIT) {
|
|
442
|
+
zoomLevel = "eighth";
|
|
443
|
+
} else {
|
|
444
|
+
zoomLevel = "sixteenth";
|
|
445
|
+
}
|
|
446
|
+
let coarseQuarterNoteStep;
|
|
447
|
+
let coarseQuarterNotes = 0;
|
|
448
|
+
if (zoomLevel === "coarse") {
|
|
449
|
+
coarseQuarterNotes = 2;
|
|
450
|
+
while (coarseQuarterNotes * ppqn / ticksPerPixel < MIN_PIXELS_PER_UNIT) {
|
|
451
|
+
coarseQuarterNotes *= 2;
|
|
452
|
+
}
|
|
453
|
+
coarseQuarterNoteStep = coarseQuarterNotes;
|
|
454
|
+
}
|
|
455
|
+
const startTick = startPixel * ticksPerPixel;
|
|
456
|
+
const endTick = endPixel * ticksPerPixel;
|
|
457
|
+
const segments = [];
|
|
458
|
+
{
|
|
459
|
+
let cumulativeBars = 0;
|
|
460
|
+
for (let i = 0; i < meterEntries.length; i++) {
|
|
461
|
+
const meter = meterEntries[i];
|
|
462
|
+
const segmentStart = meter.tick;
|
|
463
|
+
const segmentEnd = i + 1 < meterEntries.length ? meterEntries[i + 1].tick : Number.MAX_SAFE_INTEGER;
|
|
464
|
+
const ts = [meter.numerator, meter.denominator];
|
|
465
|
+
const tpBar = ticksPerBar(ts, ppqn);
|
|
466
|
+
segments.push({
|
|
467
|
+
segmentStartTick: segmentStart,
|
|
468
|
+
segmentEndTick: segmentEnd,
|
|
469
|
+
meter,
|
|
470
|
+
barOffset: cumulativeBars
|
|
471
|
+
});
|
|
472
|
+
if (segmentEnd !== Number.MAX_SAFE_INTEGER) {
|
|
473
|
+
const segmentLen = segmentEnd - segmentStart;
|
|
474
|
+
cumulativeBars += Math.floor(segmentLen / tpBar);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
const ticks = [];
|
|
479
|
+
for (const { segmentStartTick, segmentEndTick, meter, barOffset } of segments) {
|
|
480
|
+
const ts = [meter.numerator, meter.denominator];
|
|
481
|
+
const tpBeat = ticksPerBeat(ts, ppqn);
|
|
482
|
+
const tpBar = ticksPerBar(ts, ppqn);
|
|
483
|
+
let stepTicks;
|
|
484
|
+
if (zoomLevel === "coarse") {
|
|
485
|
+
stepTicks = coarseQuarterNotes * ppqn;
|
|
486
|
+
} else if (zoomLevel === "bar") {
|
|
487
|
+
stepTicks = tpBar;
|
|
488
|
+
} else if (zoomLevel === "beat") {
|
|
489
|
+
stepTicks = tpBeat;
|
|
490
|
+
} else if (zoomLevel === "eighth") {
|
|
491
|
+
stepTicks = tpEighth;
|
|
492
|
+
} else {
|
|
493
|
+
stepTicks = tpSixteenth;
|
|
494
|
+
}
|
|
495
|
+
const segmentTickStart = Math.max(segmentStartTick, startTick);
|
|
496
|
+
const segmentTickEnd = Math.min(segmentEndTick - 1, endTick);
|
|
497
|
+
if (segmentTickStart > segmentTickEnd) {
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
const offsetIntoSegment = segmentTickStart - segmentStartTick;
|
|
501
|
+
const firstStepOffset = Math.floor(offsetIntoSegment / stepTicks) * stepTicks;
|
|
502
|
+
const firstStepTick = segmentStartTick + firstStepOffset;
|
|
503
|
+
for (let tick = firstStepTick; tick <= segmentTickEnd && tick < segmentEndTick; tick += stepTicks) {
|
|
504
|
+
const pixel = tick / ticksPerPixel;
|
|
505
|
+
if (pixel < startPixel || pixel > endPixel) {
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
const tickOffsetInSegment = tick - segmentStartTick;
|
|
509
|
+
let type;
|
|
510
|
+
if (tickOffsetInSegment % tpBar === 0) {
|
|
511
|
+
type = "major";
|
|
512
|
+
} else if (tickOffsetInSegment % tpBeat === 0) {
|
|
513
|
+
type = "minor";
|
|
514
|
+
} else {
|
|
515
|
+
type = "minorMinor";
|
|
516
|
+
}
|
|
517
|
+
const barIndexInSegment = Math.floor(tickOffsetInSegment / tpBar);
|
|
518
|
+
const barIndex = barOffset + barIndexInSegment;
|
|
519
|
+
let label;
|
|
520
|
+
if (type === "major") {
|
|
521
|
+
label = `${barIndex + 1}`;
|
|
522
|
+
} else if (type === "minor" && tpBeat / ticksPerPixel >= MIN_PIXELS_PER_LABEL) {
|
|
523
|
+
const beatInBar = Math.floor(tickOffsetInSegment % tpBar / tpBeat) + 1;
|
|
524
|
+
label = `${barIndex + 1}.${beatInBar}`;
|
|
525
|
+
}
|
|
526
|
+
ticks.push({ pixel, type, barIndex, ...label !== void 0 ? { label } : {} });
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
ticks.sort((a, b) => a.pixel - b.pixel);
|
|
530
|
+
const result = {
|
|
531
|
+
ticks,
|
|
532
|
+
pixelsPerQuarterNote,
|
|
533
|
+
zoomLevel,
|
|
534
|
+
...coarseQuarterNoteStep !== void 0 ? { coarseQuarterNoteStep } : {}
|
|
535
|
+
};
|
|
536
|
+
return result;
|
|
537
|
+
}
|
|
538
|
+
function snapTickToGrid(tick, snapTo, meterEntries, ppqn = 960) {
|
|
539
|
+
if (snapTo === "off") return tick;
|
|
540
|
+
let meter = meterEntries[0] ?? { tick: 0, numerator: 4, denominator: 4 };
|
|
541
|
+
for (const entry of meterEntries) {
|
|
542
|
+
if (entry.tick <= tick) {
|
|
543
|
+
meter = entry;
|
|
544
|
+
} else {
|
|
545
|
+
break;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
const ts = [meter.numerator, meter.denominator];
|
|
549
|
+
const gridSize = snapToTicks(snapTo, ts, ppqn);
|
|
550
|
+
if (gridSize <= 0) return tick;
|
|
551
|
+
const offset = tick - meter.tick;
|
|
552
|
+
return meter.tick + Math.round(offset / gridSize) * gridSize;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// src/utils/meterDetection.ts
|
|
556
|
+
function detectMeterChanges(beats, firstBeatTick, ppqn) {
|
|
557
|
+
const DEFAULT_NUMERATOR = 4;
|
|
558
|
+
const DENOMINATOR = 4;
|
|
559
|
+
const defaultResult = [
|
|
560
|
+
{ tick: 0, numerator: DEFAULT_NUMERATOR, denominator: DENOMINATOR }
|
|
561
|
+
];
|
|
562
|
+
if (beats.length === 0) {
|
|
563
|
+
return defaultResult;
|
|
564
|
+
}
|
|
565
|
+
const firstDownbeatIndex = beats.findIndex((b) => b.beat === 1);
|
|
566
|
+
if (firstDownbeatIndex === -1) {
|
|
567
|
+
return defaultResult;
|
|
568
|
+
}
|
|
569
|
+
const bars = [];
|
|
570
|
+
let barStartBeatIndex = firstDownbeatIndex;
|
|
571
|
+
for (let i = firstDownbeatIndex + 1; i < beats.length; i++) {
|
|
572
|
+
if (beats[i].beat === 1) {
|
|
573
|
+
bars.push({ beatIndex: barStartBeatIndex, count: i - barStartBeatIndex });
|
|
574
|
+
barStartBeatIndex = i;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
if (bars.length === 0) {
|
|
578
|
+
return defaultResult;
|
|
579
|
+
}
|
|
580
|
+
const rawEntries = [];
|
|
581
|
+
let prevNumerator = -1;
|
|
582
|
+
for (const bar of bars) {
|
|
583
|
+
const numerator = bar.count;
|
|
584
|
+
if (numerator !== prevNumerator) {
|
|
585
|
+
const tick = firstBeatTick + bar.beatIndex * ppqn;
|
|
586
|
+
rawEntries.push({ tick, numerator, denominator: DENOMINATOR });
|
|
587
|
+
prevNumerator = numerator;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
if (rawEntries.length === 0) {
|
|
591
|
+
return defaultResult;
|
|
592
|
+
}
|
|
593
|
+
if (rawEntries[0].tick === 0) {
|
|
594
|
+
return rawEntries;
|
|
595
|
+
}
|
|
596
|
+
const tick0Entry = {
|
|
597
|
+
tick: 0,
|
|
598
|
+
numerator: rawEntries[0].numerator,
|
|
599
|
+
denominator: DENOMINATOR
|
|
600
|
+
};
|
|
601
|
+
const rest = rawEntries[0].numerator === tick0Entry.numerator ? rawEntries.slice(1) : rawEntries;
|
|
602
|
+
return [tick0Entry, ...rest];
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// src/utils/peaksGenerator.ts
|
|
606
|
+
function generatePeaks(samples, samplesPerPixel, bits = 16) {
|
|
607
|
+
const numPeaks = Math.ceil(samples.length / samplesPerPixel);
|
|
608
|
+
const peakArray = bits === 8 ? new Int8Array(numPeaks * 2) : new Int16Array(numPeaks * 2);
|
|
609
|
+
const maxValue = 2 ** (bits - 1);
|
|
610
|
+
for (let i = 0; i < numPeaks; i++) {
|
|
611
|
+
const start = i * samplesPerPixel;
|
|
612
|
+
const end = Math.min(start + samplesPerPixel, samples.length);
|
|
613
|
+
let min = 0;
|
|
614
|
+
let max = 0;
|
|
615
|
+
for (let j = start; j < end; j++) {
|
|
616
|
+
const value = samples[j];
|
|
617
|
+
if (value < min) min = value;
|
|
618
|
+
if (value > max) max = value;
|
|
619
|
+
}
|
|
620
|
+
peakArray[i * 2] = Math.max(-maxValue, Math.floor(min * maxValue));
|
|
621
|
+
peakArray[i * 2 + 1] = Math.min(maxValue - 1, Math.floor(max * maxValue));
|
|
622
|
+
}
|
|
623
|
+
return peakArray;
|
|
624
|
+
}
|
|
625
|
+
function appendPeaks(existingPeaks, newSamples, samplesPerPixel, totalSamplesProcessed, bits = 16) {
|
|
626
|
+
const maxValue = 2 ** (bits - 1);
|
|
627
|
+
const remainder = totalSamplesProcessed % samplesPerPixel;
|
|
628
|
+
let offset = 0;
|
|
629
|
+
if (remainder > 0 && existingPeaks.length > 0) {
|
|
630
|
+
const samplesToComplete = samplesPerPixel - remainder;
|
|
631
|
+
const endIndex = Math.min(samplesToComplete, newSamples.length);
|
|
632
|
+
let min = existingPeaks[existingPeaks.length - 2] / maxValue;
|
|
633
|
+
let max = existingPeaks[existingPeaks.length - 1] / maxValue;
|
|
634
|
+
for (let i = 0; i < endIndex; i++) {
|
|
635
|
+
const value = newSamples[i];
|
|
636
|
+
if (value < min) min = value;
|
|
637
|
+
if (value > max) max = value;
|
|
638
|
+
}
|
|
639
|
+
const updated = new (bits === 8 ? Int8Array : Int16Array)(existingPeaks.length);
|
|
640
|
+
updated.set(existingPeaks);
|
|
641
|
+
updated[existingPeaks.length - 2] = Math.max(-maxValue, Math.floor(min * maxValue));
|
|
642
|
+
updated[existingPeaks.length - 1] = Math.min(maxValue - 1, Math.floor(max * maxValue));
|
|
643
|
+
offset = endIndex;
|
|
644
|
+
const newPeaks2 = generatePeaks(newSamples.slice(offset), samplesPerPixel, bits);
|
|
645
|
+
const result2 = new (bits === 8 ? Int8Array : Int16Array)(updated.length + newPeaks2.length);
|
|
646
|
+
result2.set(updated);
|
|
647
|
+
result2.set(newPeaks2, updated.length);
|
|
648
|
+
return result2;
|
|
649
|
+
}
|
|
650
|
+
const newPeaks = generatePeaks(newSamples.slice(offset), samplesPerPixel, bits);
|
|
651
|
+
const result = new (bits === 8 ? Int8Array : Int16Array)(existingPeaks.length + newPeaks.length);
|
|
652
|
+
result.set(existingPeaks);
|
|
653
|
+
result.set(newPeaks, existingPeaks.length);
|
|
654
|
+
return result;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
// src/utils/audioBufferUtils.ts
|
|
658
|
+
function concatenateAudioData(chunks) {
|
|
659
|
+
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
660
|
+
const result = new Float32Array(totalLength);
|
|
661
|
+
let offset = 0;
|
|
662
|
+
for (const chunk of chunks) {
|
|
663
|
+
result.set(chunk, offset);
|
|
664
|
+
offset += chunk.length;
|
|
665
|
+
}
|
|
666
|
+
return result;
|
|
667
|
+
}
|
|
668
|
+
function createAudioBuffer(audioContext, channelData, sampleRate, channelCount = 1) {
|
|
669
|
+
const channels = channelData instanceof Float32Array ? [channelData] : channelData;
|
|
670
|
+
const length = channels[0]?.length ?? 0;
|
|
671
|
+
const buffer = audioContext.createBuffer(channelCount, length, sampleRate);
|
|
672
|
+
for (let ch = 0; ch < Math.min(channelCount, channels.length); ch++) {
|
|
673
|
+
buffer.copyToChannel(new Float32Array(channels[ch]), ch);
|
|
674
|
+
}
|
|
675
|
+
return buffer;
|
|
676
|
+
}
|
|
677
|
+
function appendToAudioBuffer(audioContext, existingBuffer, newSamples, sampleRate) {
|
|
678
|
+
if (!existingBuffer) {
|
|
679
|
+
return createAudioBuffer(audioContext, [newSamples], sampleRate);
|
|
680
|
+
}
|
|
681
|
+
const existingData = existingBuffer.getChannelData(0);
|
|
682
|
+
const combined = concatenateAudioData([existingData, newSamples]);
|
|
683
|
+
return createAudioBuffer(audioContext, [combined], sampleRate);
|
|
684
|
+
}
|
|
685
|
+
function calculateDuration(sampleCount, sampleRate) {
|
|
686
|
+
return sampleCount / sampleRate;
|
|
687
|
+
}
|
|
688
|
+
|
|
341
689
|
// src/clipTimeHelpers.ts
|
|
342
690
|
function clipStartTime(clip) {
|
|
343
691
|
return clip.startSample / clip.sampleRate;
|
|
@@ -351,6 +699,12 @@ function clipOffsetTime(clip) {
|
|
|
351
699
|
function clipDurationTime(clip) {
|
|
352
700
|
return clip.durationSamples / clip.sampleRate;
|
|
353
701
|
}
|
|
702
|
+
function trackChannelCount(track) {
|
|
703
|
+
return track.clips.reduce(
|
|
704
|
+
(max, clip) => Math.max(max, clip.audioBuffer?.numberOfChannels ?? 1),
|
|
705
|
+
1
|
|
706
|
+
);
|
|
707
|
+
}
|
|
354
708
|
function clipPixelWidth(startSample, durationSamples, samplesPerPixel) {
|
|
355
709
|
return Math.floor((startSample + durationSamples) / samplesPerPixel) - Math.floor(startSample / samplesPerPixel);
|
|
356
710
|
}
|
|
@@ -488,24 +842,35 @@ var getShortcutLabel = (shortcut) => {
|
|
|
488
842
|
0 && (module.exports = {
|
|
489
843
|
InteractionState,
|
|
490
844
|
MAX_CANVAS_WIDTH,
|
|
845
|
+
MIN_PIXELS_PER_UNIT,
|
|
491
846
|
PPQN,
|
|
847
|
+
appendPeaks,
|
|
848
|
+
appendToAudioBuffer,
|
|
492
849
|
applyFadeIn,
|
|
493
850
|
applyFadeOut,
|
|
851
|
+
calculateDuration,
|
|
494
852
|
clipDurationTime,
|
|
495
853
|
clipEndTime,
|
|
496
854
|
clipOffsetTime,
|
|
497
855
|
clipPixelWidth,
|
|
498
856
|
clipStartTime,
|
|
499
857
|
clipsOverlap,
|
|
858
|
+
computeMusicalTicks,
|
|
859
|
+
concatenateAudioData,
|
|
860
|
+
createAudioBuffer,
|
|
500
861
|
createClip,
|
|
501
862
|
createClipFromSeconds,
|
|
863
|
+
createClipFromTicks,
|
|
502
864
|
createTimeline,
|
|
503
865
|
createTrack,
|
|
504
866
|
dBToNormalized,
|
|
867
|
+
detectMeterChanges,
|
|
505
868
|
exponentialCurve,
|
|
506
869
|
findGaps,
|
|
870
|
+
gainToDb,
|
|
507
871
|
gainToNormalized,
|
|
508
872
|
generateCurve,
|
|
873
|
+
generatePeaks,
|
|
509
874
|
getClipsAtSample,
|
|
510
875
|
getClipsInRange,
|
|
511
876
|
getShortcutLabel,
|
|
@@ -521,11 +886,13 @@ var getShortcutLabel = (shortcut) => {
|
|
|
521
886
|
samplesToTicks,
|
|
522
887
|
secondsToPixels,
|
|
523
888
|
secondsToSamples,
|
|
889
|
+
snapTickToGrid,
|
|
524
890
|
snapToGrid,
|
|
891
|
+
snapToTicks,
|
|
525
892
|
sortClipsByTime,
|
|
526
893
|
ticksPerBar,
|
|
527
894
|
ticksPerBeat,
|
|
528
|
-
|
|
529
|
-
|
|
895
|
+
ticksToSamples,
|
|
896
|
+
trackChannelCount
|
|
530
897
|
});
|
|
531
898
|
//# sourceMappingURL=index.js.map
|