@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.d.mts +7 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +77 -8
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +78 -8
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
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,
|
|
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.
|
|
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:"
|
|
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
|
-
|
|
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
|
-
|
|
1823
|
-
"[waveform-playlist] adapter.addTrack() called but
|
|
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:"
|
|
1957
|
+
console.warn("[waveform-playlist] Error disposing playout: " + String(err));
|
|
1888
1958
|
}
|
|
1889
1959
|
playout = null;
|
|
1890
1960
|
_isPlaying = false;
|