@waveform-playlist/playout 9.5.2 → 10.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.mjs CHANGED
@@ -19,6 +19,15 @@ import {
19
19
  } from "tone";
20
20
 
21
21
  // src/fades.ts
22
+ import {
23
+ linearCurve,
24
+ exponentialCurve,
25
+ sCurveCurve,
26
+ logarithmicCurve,
27
+ generateCurve,
28
+ applyFadeIn,
29
+ applyFadeOut
30
+ } from "@waveform-playlist/core";
22
31
  var hasWarned = false;
23
32
  function getUnderlyingAudioParam(signal) {
24
33
  const param = signal._param;
@@ -30,92 +39,6 @@ function getUnderlyingAudioParam(signal) {
30
39
  }
31
40
  return param;
32
41
  }
33
- function linearCurve(length, fadeIn) {
34
- const curve = new Float32Array(length);
35
- const scale = length - 1;
36
- for (let i = 0; i < length; i++) {
37
- const x = i / scale;
38
- curve[i] = fadeIn ? x : 1 - x;
39
- }
40
- return curve;
41
- }
42
- function exponentialCurve(length, fadeIn) {
43
- const curve = new Float32Array(length);
44
- const scale = length - 1;
45
- for (let i = 0; i < length; i++) {
46
- const x = i / scale;
47
- const index = fadeIn ? i : length - 1 - i;
48
- curve[index] = Math.exp(2 * x - 1) / Math.E;
49
- }
50
- return curve;
51
- }
52
- function sCurveCurve(length, fadeIn) {
53
- const curve = new Float32Array(length);
54
- const phase = fadeIn ? Math.PI / 2 : -Math.PI / 2;
55
- for (let i = 0; i < length; i++) {
56
- curve[i] = Math.sin(Math.PI * i / length - phase) / 2 + 0.5;
57
- }
58
- return curve;
59
- }
60
- function logarithmicCurve(length, fadeIn, base = 10) {
61
- const curve = new Float32Array(length);
62
- for (let i = 0; i < length; i++) {
63
- const index = fadeIn ? i : length - 1 - i;
64
- const x = i / length;
65
- curve[index] = Math.log(1 + base * x) / Math.log(1 + base);
66
- }
67
- return curve;
68
- }
69
- function generateCurve(type, length, fadeIn) {
70
- switch (type) {
71
- case "linear":
72
- return linearCurve(length, fadeIn);
73
- case "exponential":
74
- return exponentialCurve(length, fadeIn);
75
- case "sCurve":
76
- return sCurveCurve(length, fadeIn);
77
- case "logarithmic":
78
- return logarithmicCurve(length, fadeIn);
79
- default:
80
- return linearCurve(length, fadeIn);
81
- }
82
- }
83
- function applyFadeIn(param, startTime, duration, type = "linear", startValue = 0, endValue = 1) {
84
- if (duration <= 0) return;
85
- if (type === "linear") {
86
- param.setValueAtTime(startValue, startTime);
87
- param.linearRampToValueAtTime(endValue, startTime + duration);
88
- } else if (type === "exponential") {
89
- param.setValueAtTime(Math.max(startValue, 1e-3), startTime);
90
- param.exponentialRampToValueAtTime(Math.max(endValue, 1e-3), startTime + duration);
91
- } else {
92
- const curve = generateCurve(type, 1e4, true);
93
- const scaledCurve = new Float32Array(curve.length);
94
- const range = endValue - startValue;
95
- for (let i = 0; i < curve.length; i++) {
96
- scaledCurve[i] = startValue + curve[i] * range;
97
- }
98
- param.setValueCurveAtTime(scaledCurve, startTime, duration);
99
- }
100
- }
101
- function applyFadeOut(param, startTime, duration, type = "linear", startValue = 1, endValue = 0) {
102
- if (duration <= 0) return;
103
- if (type === "linear") {
104
- param.setValueAtTime(startValue, startTime);
105
- param.linearRampToValueAtTime(endValue, startTime + duration);
106
- } else if (type === "exponential") {
107
- param.setValueAtTime(Math.max(startValue, 1e-3), startTime);
108
- param.exponentialRampToValueAtTime(Math.max(endValue, 1e-3), startTime + duration);
109
- } else {
110
- const curve = generateCurve(type, 1e4, false);
111
- const scaledCurve = new Float32Array(curve.length);
112
- const range = startValue - endValue;
113
- for (let i = 0; i < curve.length; i++) {
114
- scaledCurve[i] = endValue + curve[i] * range;
115
- }
116
- param.setValueCurveAtTime(scaledCurve, startTime, duration);
117
- }
118
- }
119
42
 
120
43
  // src/ToneTrack.ts
121
44
  var ToneTrack = class {
@@ -130,7 +53,7 @@ var ToneTrack = class {
130
53
  this._scheduleGuardOffset = 0;
131
54
  this.track = options.track;
132
55
  this.volumeNode = new Volume(this.gainToDb(options.track.gain));
133
- this.panNode = new Panner(options.track.stereoPan);
56
+ this.panNode = new Panner({ pan: options.track.stereoPan, channelCount: 2 });
134
57
  this.muteGain = new Gain(options.track.muted ? 0 : 1);
135
58
  this.volumeNode.chain(this.panNode, this.muteGain);
136
59
  const destination = options.destination || getDestination();
@@ -1536,6 +1459,84 @@ function createToneAdapter(options) {
1536
1459
  let _loopStart = 0;
1537
1460
  let _loopEnd = 0;
1538
1461
  let _audioInitialized = false;
1462
+ function addTrackToPlayout(p, track) {
1463
+ const audioClips = track.clips.filter((c) => c.audioBuffer && !c.midiNotes);
1464
+ const midiClips = track.clips.filter((c) => c.midiNotes && c.midiNotes.length > 0);
1465
+ if (audioClips.length > 0) {
1466
+ const startTime = Math.min(...audioClips.map(clipStartTime));
1467
+ const endTime = Math.max(...audioClips.map(clipEndTime));
1468
+ const trackObj = {
1469
+ id: track.id,
1470
+ name: track.name,
1471
+ gain: track.volume,
1472
+ muted: track.muted,
1473
+ soloed: track.soloed,
1474
+ stereoPan: track.pan,
1475
+ startTime,
1476
+ endTime
1477
+ };
1478
+ const clipInfos = audioClips.map((clip) => ({
1479
+ buffer: clip.audioBuffer,
1480
+ startTime: clipStartTime(clip) - startTime,
1481
+ duration: clipDurationTime(clip),
1482
+ offset: clipOffsetTime(clip),
1483
+ fadeIn: clip.fadeIn,
1484
+ fadeOut: clip.fadeOut,
1485
+ gain: clip.gain
1486
+ }));
1487
+ p.addTrack({
1488
+ clips: clipInfos,
1489
+ track: trackObj,
1490
+ effects: track.effects
1491
+ });
1492
+ }
1493
+ if (midiClips.length > 0) {
1494
+ const startTime = Math.min(...midiClips.map(clipStartTime));
1495
+ const endTime = Math.max(...midiClips.map(clipEndTime));
1496
+ const trackId = audioClips.length > 0 ? `${track.id}:midi` : track.id;
1497
+ const trackObj = {
1498
+ id: trackId,
1499
+ name: track.name,
1500
+ gain: track.volume,
1501
+ muted: track.muted,
1502
+ soloed: track.soloed,
1503
+ stereoPan: track.pan,
1504
+ startTime,
1505
+ endTime
1506
+ };
1507
+ const midiClipInfos = midiClips.map((clip) => ({
1508
+ notes: clip.midiNotes,
1509
+ startTime: clipStartTime(clip) - startTime,
1510
+ duration: clipDurationTime(clip),
1511
+ offset: clipOffsetTime(clip)
1512
+ }));
1513
+ if (options?.soundFontCache?.isLoaded) {
1514
+ const firstClip = midiClips[0];
1515
+ const midiChannel = firstClip.midiChannel;
1516
+ const isPercussion = midiChannel === 9;
1517
+ const programNumber = firstClip.midiProgram ?? 0;
1518
+ p.addSoundFontTrack({
1519
+ clips: midiClipInfos,
1520
+ track: trackObj,
1521
+ soundFontCache: options.soundFontCache,
1522
+ programNumber,
1523
+ isPercussion,
1524
+ effects: track.effects
1525
+ });
1526
+ } else {
1527
+ if (options?.soundFontCache) {
1528
+ console.warn(
1529
+ `[waveform-playlist] SoundFont not loaded for track "${track.name}" \u2014 falling back to PolySynth.`
1530
+ );
1531
+ }
1532
+ p.addMidiTrack({
1533
+ clips: midiClipInfos,
1534
+ track: trackObj,
1535
+ effects: track.effects
1536
+ });
1537
+ }
1538
+ }
1539
+ }
1539
1540
  function buildPlayout(tracks) {
1540
1541
  if (playout) {
1541
1542
  try {
@@ -1560,82 +1561,7 @@ function createToneAdapter(options) {
1560
1561
  });
1561
1562
  }
1562
1563
  for (const track of tracks) {
1563
- const audioClips = track.clips.filter((c) => c.audioBuffer && !c.midiNotes);
1564
- const midiClips = track.clips.filter((c) => c.midiNotes && c.midiNotes.length > 0);
1565
- if (audioClips.length > 0) {
1566
- const startTime = Math.min(...audioClips.map(clipStartTime));
1567
- const endTime = Math.max(...audioClips.map(clipEndTime));
1568
- const trackObj = {
1569
- id: track.id,
1570
- name: track.name,
1571
- gain: track.volume,
1572
- muted: track.muted,
1573
- soloed: track.soloed,
1574
- stereoPan: track.pan,
1575
- startTime,
1576
- endTime
1577
- };
1578
- const clipInfos = audioClips.map((clip) => ({
1579
- buffer: clip.audioBuffer,
1580
- startTime: clipStartTime(clip) - startTime,
1581
- duration: clipDurationTime(clip),
1582
- offset: clipOffsetTime(clip),
1583
- fadeIn: clip.fadeIn,
1584
- fadeOut: clip.fadeOut,
1585
- gain: clip.gain
1586
- }));
1587
- playout.addTrack({
1588
- clips: clipInfos,
1589
- track: trackObj,
1590
- effects: track.effects
1591
- });
1592
- }
1593
- if (midiClips.length > 0) {
1594
- const startTime = Math.min(...midiClips.map(clipStartTime));
1595
- const endTime = Math.max(...midiClips.map(clipEndTime));
1596
- const trackId = audioClips.length > 0 ? `${track.id}:midi` : track.id;
1597
- const trackObj = {
1598
- id: trackId,
1599
- name: track.name,
1600
- gain: track.volume,
1601
- muted: track.muted,
1602
- soloed: track.soloed,
1603
- stereoPan: track.pan,
1604
- startTime,
1605
- endTime
1606
- };
1607
- const midiClipInfos = midiClips.map((clip) => ({
1608
- notes: clip.midiNotes,
1609
- startTime: clipStartTime(clip) - startTime,
1610
- duration: clipDurationTime(clip),
1611
- offset: clipOffsetTime(clip)
1612
- }));
1613
- if (options?.soundFontCache?.isLoaded) {
1614
- const firstClip = midiClips[0];
1615
- const midiChannel = firstClip.midiChannel;
1616
- const isPercussion = midiChannel === 9;
1617
- const programNumber = firstClip.midiProgram ?? 0;
1618
- playout.addSoundFontTrack({
1619
- clips: midiClipInfos,
1620
- track: trackObj,
1621
- soundFontCache: options.soundFontCache,
1622
- programNumber,
1623
- isPercussion,
1624
- effects: track.effects
1625
- });
1626
- } else {
1627
- if (options?.soundFontCache) {
1628
- console.warn(
1629
- `[waveform-playlist] SoundFont not loaded for track "${track.name}" \u2014 falling back to PolySynth.`
1630
- );
1631
- }
1632
- playout.addMidiTrack({
1633
- clips: midiClipInfos,
1634
- track: trackObj,
1635
- effects: track.effects
1636
- });
1637
- }
1638
- }
1564
+ addTrackToPlayout(playout, track);
1639
1565
  }
1640
1566
  playout.applyInitialSoloState();
1641
1567
  playout.setLoop(_loopEnabled, _loopStart, _loopEnd);
@@ -1655,6 +1581,15 @@ function createToneAdapter(options) {
1655
1581
  setTracks(tracks) {
1656
1582
  buildPlayout(tracks);
1657
1583
  },
1584
+ addTrack(track) {
1585
+ if (!playout) {
1586
+ throw new Error(
1587
+ "[waveform-playlist] adapter.addTrack() called but no playout exists. Call setTracks() first to initialize the playout."
1588
+ );
1589
+ }
1590
+ addTrackToPlayout(playout, track);
1591
+ playout.applyInitialSoloState();
1592
+ },
1658
1593
  play(startTime, endTime) {
1659
1594
  if (!playout) {
1660
1595
  console.warn(