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