@waveform-playlist/playout 12.0.0 → 12.2.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
@@ -1,6 +1,7 @@
1
1
  // src/TonePlayout.ts
2
2
  import {
3
3
  Volume as Volume4,
4
+ Gain as Gain4,
4
5
  getDestination as getDestination4,
5
6
  start,
6
7
  now,
@@ -1047,13 +1048,15 @@ var TonePlayout = class {
1047
1048
  this._loopStart = 0;
1048
1049
  this._loopEnd = 0;
1049
1050
  this.masterVolume = new Volume4(gainToDb4(options.masterGain ?? 1));
1051
+ this._masterTap = new Gain4(1);
1050
1052
  if (options.effects) {
1051
- const cleanup = options.effects(this.masterVolume, getDestination4(), false);
1053
+ const cleanup = options.effects(this.masterVolume, this._masterTap, false);
1052
1054
  if (cleanup) {
1053
1055
  this.effectsCleanup = cleanup;
1054
1056
  }
1057
+ this._masterTap.connect(getDestination4());
1055
1058
  } else {
1056
- this.masterVolume.toDestination();
1059
+ this.masterVolume.chain(this._masterTap, getDestination4());
1057
1060
  }
1058
1061
  if (options.tracks) {
1059
1062
  options.tracks.forEach((track) => {
@@ -1258,6 +1261,12 @@ var TonePlayout = class {
1258
1261
  setMasterGain(gain) {
1259
1262
  this.masterVolume.volume.value = gainToDb4(gain);
1260
1263
  }
1264
+ /** The master output tap node. In the signal chain: masterVolume → tap → destination.
1265
+ * Connect analyzers/effects/recorders here — parallel or serial.
1266
+ * The tap's native GainNode is on the same standardized-audio-context as adapter.audioContext. */
1267
+ get masterOutputNode() {
1268
+ return this._masterTap.input;
1269
+ }
1261
1270
  setSolo(trackId, soloed) {
1262
1271
  const track = this.tracks.get(trackId);
1263
1272
  if (track) {
@@ -1357,10 +1366,15 @@ var TonePlayout = class {
1357
1366
  console.warn("[waveform-playlist] Error during master effects cleanup:", err);
1358
1367
  }
1359
1368
  }
1369
+ try {
1370
+ this._masterTap.dispose();
1371
+ } catch (err) {
1372
+ console.warn("[waveform-playlist] Error disposing master tap: " + String(err));
1373
+ }
1360
1374
  try {
1361
1375
  this.masterVolume.dispose();
1362
1376
  } catch (err) {
1363
- console.warn("[waveform-playlist] Error disposing master volume:", err);
1377
+ console.warn("[waveform-playlist] Error disposing master volume: " + String(err));
1364
1378
  }
1365
1379
  }
1366
1380
  get context() {
@@ -1619,14 +1633,22 @@ import {
1619
1633
  } from "@waveform-playlist/core";
1620
1634
  import { now as now2 } from "tone";
1621
1635
  function createToneAdapter(options) {
1622
- let playout = null;
1636
+ getGlobalContext();
1637
+ let _playoutGeneration = 1;
1638
+ let playout = new TonePlayout({ effects: options?.effects });
1639
+ playout.setOnPlaybackComplete(() => {
1640
+ if (_playoutGeneration === 1) {
1641
+ _isPlaying = false;
1642
+ }
1643
+ });
1623
1644
  let _isPlaying = false;
1624
- let _playoutGeneration = 0;
1625
1645
  let _loopEnabled = false;
1626
1646
  let _loopStart = 0;
1627
1647
  let _loopEnd = 0;
1628
1648
  let _audioInitialized = false;
1629
1649
  let _pendingInit = null;
1650
+ const _ppqn = options?.ppqn ?? 192;
1651
+ let _bpm = 120;
1630
1652
  function addTrackToPlayout(p, track) {
1631
1653
  const audioClips = track.clips.filter((c) => c.audioBuffer && !c.midiNotes);
1632
1654
  const midiClips = track.clips.filter((c) => c.midiNotes && c.midiNotes.length > 0);
@@ -1819,9 +1841,10 @@ function createToneAdapter(options) {
1819
1841
  },
1820
1842
  addTrack(track) {
1821
1843
  if (!playout) {
1822
- throw new Error(
1823
- "[waveform-playlist] adapter.addTrack() called but no playout exists. Call setTracks() first to initialize the playout."
1844
+ console.warn(
1845
+ "[waveform-playlist] adapter.addTrack() called but playout is not available (adapter may have been disposed)."
1824
1846
  );
1847
+ return;
1825
1848
  }
1826
1849
  addTrackToPlayout(playout, track);
1827
1850
  playout.applyInitialSoloState();
@@ -1880,11 +1903,58 @@ function createToneAdapter(options) {
1880
1903
  _loopEnd = end;
1881
1904
  playout?.setLoop(enabled, start2, end);
1882
1905
  },
1906
+ get audioContext() {
1907
+ return getGlobalAudioContext();
1908
+ },
1909
+ get ppqn() {
1910
+ return _ppqn;
1911
+ },
1912
+ setTempo(bpm, atTick) {
1913
+ if (atTick !== void 0) {
1914
+ throw new Error(
1915
+ "Multiple tempo changes not supported by TonePlayoutAdapter. Use NativePlayoutAdapter from @dawcore/transport for multi-tempo support."
1916
+ );
1917
+ }
1918
+ _bpm = bpm;
1919
+ },
1920
+ setMeter(_numerator, _denominator, atTick) {
1921
+ if (atTick !== void 0) {
1922
+ throw new Error(
1923
+ "Multiple meter changes not supported by TonePlayoutAdapter. Use NativePlayoutAdapter from @dawcore/transport for multi-meter support."
1924
+ );
1925
+ }
1926
+ },
1927
+ ticksToSeconds(tick) {
1928
+ return tick * 60 / (_bpm * _ppqn);
1929
+ },
1930
+ secondsToTicks(seconds) {
1931
+ return seconds * _bpm * _ppqn / 60;
1932
+ },
1933
+ // --- Cross-context worklet support ---
1934
+ // Tone.js wraps standardized-audio-context. Native AudioWorkletNode constructor
1935
+ // rejects it ("parameter 1 is not of type 'BaseAudioContext'").
1936
+ // These methods use Tone.js Context wrappers that handle both context types.
1937
+ // Note: addWorkletModule is NOT needed — rawContext.audioWorklet.addModule() works
1938
+ // identically for both native and standardized contexts. Only node/source creation differs.
1939
+ createAudioWorkletNode(name, options2) {
1940
+ return getGlobalContext().createAudioWorkletNode(name, options2);
1941
+ },
1942
+ createMediaStreamSource(stream) {
1943
+ return getGlobalContext().createMediaStreamSource(stream);
1944
+ },
1945
+ get masterOutputNode() {
1946
+ if (!playout) {
1947
+ throw new Error(
1948
+ "[waveform-playlist] adapter.masterOutputNode accessed after dispose. Disconnect your analyzer before disposing the adapter."
1949
+ );
1950
+ }
1951
+ return playout.masterOutputNode;
1952
+ },
1883
1953
  dispose() {
1884
1954
  try {
1885
1955
  playout?.dispose();
1886
1956
  } catch (err) {
1887
- console.warn("[waveform-playlist] Error disposing playout:", err);
1957
+ console.warn("[waveform-playlist] Error disposing playout: " + String(err));
1888
1958
  }
1889
1959
  playout = null;
1890
1960
  _isPlaying = false;