@waveform-playlist/playout 12.2.0 → 12.4.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 CHANGED
@@ -317,6 +317,16 @@ declare class SoundFontCache {
317
317
  * construct before user interaction (avoids Firefox autoplay warnings).
318
318
  */
319
319
  constructor(context?: BaseAudioContext);
320
+ /**
321
+ * Fetch and parse an SF2 file, resolving only once it's ready to play.
322
+ * Prefer this over `new SoundFontCache()` + `load()` — the returned cache
323
+ * is always loaded, so it can't hit the "unloaded cache → PolySynth
324
+ * fallback" path in createToneAdapter / setSoundFontCache.
325
+ */
326
+ static fromUrl(url: string, options?: {
327
+ context?: BaseAudioContext;
328
+ signal?: AbortSignal;
329
+ }): Promise<SoundFontCache>;
320
330
  /**
321
331
  * Load and parse an SF2 file from a URL.
322
332
  */
@@ -610,6 +620,20 @@ interface ToneAdapterOptions {
610
620
  /** Pulses per quarter note. Defaults to 192 (Tone.js native). */
611
621
  ppqn?: number;
612
622
  }
613
- declare function createToneAdapter(options?: ToneAdapterOptions): PlayoutAdapter;
623
+ interface ToneAdapter extends PlayoutAdapter {
624
+ /**
625
+ * Provide or swap the SoundFont after creation. Rebuilds only the MIDI
626
+ * tracks whose routing changes; audio tracks keep playing untouched.
627
+ * Pass undefined to revert MIDI tracks to PolySynth synthesis.
628
+ */
629
+ setSoundFontCache(cache: SoundFontCache | undefined): void;
630
+ }
631
+ /**
632
+ * Capability check for Tone-specific adapter features. Structural, not
633
+ * instanceof — any adapter implementing setSoundFontCache passes. Narrows a
634
+ * generic PlayoutAdapter so soundfont calls typecheck without casts.
635
+ */
636
+ declare function isToneAdapter(adapter: PlayoutAdapter | null | undefined): adapter is ToneAdapter;
637
+ declare function createToneAdapter(options?: ToneAdapterOptions): ToneAdapter;
614
638
 
615
- export { type AudioContextOptions, type EffectsFunction, type LoopAndEnvelopeParams, type MidiClipInfo, MidiToneTrack, type MidiToneTrackOptions, type PlayableTrack, type PlaybackRateParams, SoundFontCache, type SoundFontSample, SoundFontToneTrack, type SoundFontToneTrackOptions, type ToneAdapterOptions, TonePlayout, type TonePlayoutOptions, ToneTrack, type ToneTrackOptions, type TrackEffectsFunction, calculatePlaybackRate, closeGlobalAudioContext, configureGlobalContext, createToneAdapter, extractLoopAndEnvelope, getGeneratorValue, getGlobalAudioContext, getGlobalAudioContextState, getGlobalContext, getGlobalToneContext, getMediaStreamSource, getUnderlyingAudioParam, hasMediaStreamSource, int16ToFloat32, releaseMediaStreamSource, resumeGlobalAudioContext, timecentsToSeconds };
639
+ export { type AudioContextOptions, type EffectsFunction, type LoopAndEnvelopeParams, type MidiClipInfo, MidiToneTrack, type MidiToneTrackOptions, type PlayableTrack, type PlaybackRateParams, SoundFontCache, type SoundFontSample, SoundFontToneTrack, type SoundFontToneTrackOptions, type ToneAdapter, type ToneAdapterOptions, TonePlayout, type TonePlayoutOptions, ToneTrack, type ToneTrackOptions, type TrackEffectsFunction, calculatePlaybackRate, closeGlobalAudioContext, configureGlobalContext, createToneAdapter, extractLoopAndEnvelope, getGeneratorValue, getGlobalAudioContext, getGlobalAudioContextState, getGlobalContext, getGlobalToneContext, getMediaStreamSource, getUnderlyingAudioParam, hasMediaStreamSource, int16ToFloat32, isToneAdapter, releaseMediaStreamSource, resumeGlobalAudioContext, timecentsToSeconds };
package/dist/index.d.ts CHANGED
@@ -317,6 +317,16 @@ declare class SoundFontCache {
317
317
  * construct before user interaction (avoids Firefox autoplay warnings).
318
318
  */
319
319
  constructor(context?: BaseAudioContext);
320
+ /**
321
+ * Fetch and parse an SF2 file, resolving only once it's ready to play.
322
+ * Prefer this over `new SoundFontCache()` + `load()` — the returned cache
323
+ * is always loaded, so it can't hit the "unloaded cache → PolySynth
324
+ * fallback" path in createToneAdapter / setSoundFontCache.
325
+ */
326
+ static fromUrl(url: string, options?: {
327
+ context?: BaseAudioContext;
328
+ signal?: AbortSignal;
329
+ }): Promise<SoundFontCache>;
320
330
  /**
321
331
  * Load and parse an SF2 file from a URL.
322
332
  */
@@ -610,6 +620,20 @@ interface ToneAdapterOptions {
610
620
  /** Pulses per quarter note. Defaults to 192 (Tone.js native). */
611
621
  ppqn?: number;
612
622
  }
613
- declare function createToneAdapter(options?: ToneAdapterOptions): PlayoutAdapter;
623
+ interface ToneAdapter extends PlayoutAdapter {
624
+ /**
625
+ * Provide or swap the SoundFont after creation. Rebuilds only the MIDI
626
+ * tracks whose routing changes; audio tracks keep playing untouched.
627
+ * Pass undefined to revert MIDI tracks to PolySynth synthesis.
628
+ */
629
+ setSoundFontCache(cache: SoundFontCache | undefined): void;
630
+ }
631
+ /**
632
+ * Capability check for Tone-specific adapter features. Structural, not
633
+ * instanceof — any adapter implementing setSoundFontCache passes. Narrows a
634
+ * generic PlayoutAdapter so soundfont calls typecheck without casts.
635
+ */
636
+ declare function isToneAdapter(adapter: PlayoutAdapter | null | undefined): adapter is ToneAdapter;
637
+ declare function createToneAdapter(options?: ToneAdapterOptions): ToneAdapter;
614
638
 
615
- export { type AudioContextOptions, type EffectsFunction, type LoopAndEnvelopeParams, type MidiClipInfo, MidiToneTrack, type MidiToneTrackOptions, type PlayableTrack, type PlaybackRateParams, SoundFontCache, type SoundFontSample, SoundFontToneTrack, type SoundFontToneTrackOptions, type ToneAdapterOptions, TonePlayout, type TonePlayoutOptions, ToneTrack, type ToneTrackOptions, type TrackEffectsFunction, calculatePlaybackRate, closeGlobalAudioContext, configureGlobalContext, createToneAdapter, extractLoopAndEnvelope, getGeneratorValue, getGlobalAudioContext, getGlobalAudioContextState, getGlobalContext, getGlobalToneContext, getMediaStreamSource, getUnderlyingAudioParam, hasMediaStreamSource, int16ToFloat32, releaseMediaStreamSource, resumeGlobalAudioContext, timecentsToSeconds };
639
+ export { type AudioContextOptions, type EffectsFunction, type LoopAndEnvelopeParams, type MidiClipInfo, MidiToneTrack, type MidiToneTrackOptions, type PlayableTrack, type PlaybackRateParams, SoundFontCache, type SoundFontSample, SoundFontToneTrack, type SoundFontToneTrackOptions, type ToneAdapter, type ToneAdapterOptions, TonePlayout, type TonePlayoutOptions, ToneTrack, type ToneTrackOptions, type TrackEffectsFunction, calculatePlaybackRate, closeGlobalAudioContext, configureGlobalContext, createToneAdapter, extractLoopAndEnvelope, getGeneratorValue, getGlobalAudioContext, getGlobalAudioContextState, getGlobalContext, getGlobalToneContext, getMediaStreamSource, getUnderlyingAudioParam, hasMediaStreamSource, int16ToFloat32, isToneAdapter, releaseMediaStreamSource, resumeGlobalAudioContext, timecentsToSeconds };
package/dist/index.js CHANGED
@@ -39,6 +39,7 @@ __export(index_exports, {
39
39
  getUnderlyingAudioParam: () => getUnderlyingAudioParam,
40
40
  hasMediaStreamSource: () => hasMediaStreamSource,
41
41
  int16ToFloat32: () => int16ToFloat32,
42
+ isToneAdapter: () => isToneAdapter,
42
43
  releaseMediaStreamSource: () => releaseMediaStreamSource,
43
44
  resumeGlobalAudioContext: () => resumeGlobalAudioContext,
44
45
  timecentsToSeconds: () => timecentsToSeconds
@@ -1455,7 +1456,7 @@ function extractLoopAndEnvelope(params) {
1455
1456
  releaseVolEnv
1456
1457
  };
1457
1458
  }
1458
- var SoundFontCache = class {
1459
+ var SoundFontCache = class _SoundFontCache {
1459
1460
  /**
1460
1461
  * @param context Optional AudioContext for createBuffer(). If omitted, uses
1461
1462
  * an OfflineAudioContext which doesn't require user gesture — safe to
@@ -1466,6 +1467,17 @@ var SoundFontCache = class {
1466
1467
  this.audioBufferCache = /* @__PURE__ */ new Map();
1467
1468
  this.context = context ?? new OfflineAudioContext(1, 1, 44100);
1468
1469
  }
1470
+ /**
1471
+ * Fetch and parse an SF2 file, resolving only once it's ready to play.
1472
+ * Prefer this over `new SoundFontCache()` + `load()` — the returned cache
1473
+ * is always loaded, so it can't hit the "unloaded cache → PolySynth
1474
+ * fallback" path in createToneAdapter / setSoundFontCache.
1475
+ */
1476
+ static async fromUrl(url, options) {
1477
+ const cache = new _SoundFontCache(options?.context);
1478
+ await cache.load(url, options?.signal);
1479
+ return cache;
1480
+ }
1469
1481
  /**
1470
1482
  * Load and parse an SF2 file from a URL.
1471
1483
  */
@@ -1638,6 +1650,9 @@ function hasMediaStreamSource(stream) {
1638
1650
  // src/TonePlayoutAdapter.ts
1639
1651
  var import_core6 = require("@waveform-playlist/core");
1640
1652
  var import_tone7 = require("tone");
1653
+ function isToneAdapter(adapter) {
1654
+ return typeof adapter?.setSoundFontCache === "function";
1655
+ }
1641
1656
  function createToneAdapter(options) {
1642
1657
  getGlobalContext();
1643
1658
  let _playoutGeneration = 1;
@@ -1655,85 +1670,98 @@ function createToneAdapter(options) {
1655
1670
  let _pendingInit = null;
1656
1671
  const _ppqn = options?.ppqn ?? 192;
1657
1672
  let _bpm = 120;
1658
- function addTrackToPlayout(p, track) {
1673
+ let _soundFontCache = options?.soundFontCache;
1674
+ const _currentTracks = /* @__PURE__ */ new Map();
1675
+ const _midiTrackBuild = /* @__PURE__ */ new Map();
1676
+ function midiPlayoutTrackId(track) {
1677
+ const hasAudio = track.clips.some((c) => c.audioBuffer && !c.midiNotes);
1678
+ return hasAudio ? `${track.id}:midi` : track.id;
1679
+ }
1680
+ function addAudioTrackToPlayout(p, track) {
1659
1681
  const audioClips = track.clips.filter((c) => c.audioBuffer && !c.midiNotes);
1682
+ if (audioClips.length === 0) return;
1683
+ const startTime = Math.min(...audioClips.map(import_core6.clipStartTime));
1684
+ const endTime = Math.max(...audioClips.map(import_core6.clipEndTime));
1685
+ const trackObj = {
1686
+ id: track.id,
1687
+ name: track.name,
1688
+ gain: track.volume,
1689
+ muted: track.muted,
1690
+ soloed: track.soloed,
1691
+ stereoPan: track.pan,
1692
+ startTime,
1693
+ endTime
1694
+ };
1695
+ const clipInfos = audioClips.map((clip) => ({
1696
+ buffer: clip.audioBuffer,
1697
+ startTime: (0, import_core6.clipStartTime)(clip) - startTime,
1698
+ duration: (0, import_core6.clipDurationTime)(clip),
1699
+ offset: (0, import_core6.clipOffsetTime)(clip),
1700
+ fadeIn: clip.fadeIn,
1701
+ fadeOut: clip.fadeOut,
1702
+ gain: clip.gain
1703
+ }));
1704
+ p.addTrack({
1705
+ clips: clipInfos,
1706
+ track: trackObj,
1707
+ effects: track.effects,
1708
+ channelCount: (0, import_core6.trackChannelCount)(track)
1709
+ });
1710
+ }
1711
+ function addMidiTrackToPlayout(p, track) {
1660
1712
  const midiClips = track.clips.filter((c) => c.midiNotes && c.midiNotes.length > 0);
1661
- if (audioClips.length > 0) {
1662
- const startTime = Math.min(...audioClips.map(import_core6.clipStartTime));
1663
- const endTime = Math.max(...audioClips.map(import_core6.clipEndTime));
1664
- const trackObj = {
1665
- id: track.id,
1666
- name: track.name,
1667
- gain: track.volume,
1668
- muted: track.muted,
1669
- soloed: track.soloed,
1670
- stereoPan: track.pan,
1671
- startTime,
1672
- endTime
1673
- };
1674
- const clipInfos = audioClips.map((clip) => ({
1675
- buffer: clip.audioBuffer,
1676
- startTime: (0, import_core6.clipStartTime)(clip) - startTime,
1677
- duration: (0, import_core6.clipDurationTime)(clip),
1678
- offset: (0, import_core6.clipOffsetTime)(clip),
1679
- fadeIn: clip.fadeIn,
1680
- fadeOut: clip.fadeOut,
1681
- gain: clip.gain
1682
- }));
1683
- p.addTrack({
1684
- clips: clipInfos,
1713
+ if (midiClips.length === 0) return;
1714
+ const startTime = Math.min(...midiClips.map(import_core6.clipStartTime));
1715
+ const endTime = Math.max(...midiClips.map(import_core6.clipEndTime));
1716
+ const trackId = midiPlayoutTrackId(track);
1717
+ const trackObj = {
1718
+ id: trackId,
1719
+ name: track.name,
1720
+ gain: track.volume,
1721
+ muted: track.muted,
1722
+ soloed: track.soloed,
1723
+ stereoPan: track.pan,
1724
+ startTime,
1725
+ endTime
1726
+ };
1727
+ const midiClipInfos = midiClips.map((clip) => ({
1728
+ notes: clip.midiNotes,
1729
+ startTime: (0, import_core6.clipStartTime)(clip) - startTime,
1730
+ duration: (0, import_core6.clipDurationTime)(clip),
1731
+ offset: (0, import_core6.clipOffsetTime)(clip)
1732
+ }));
1733
+ if (_soundFontCache?.isLoaded) {
1734
+ const firstClip = midiClips[0];
1735
+ const midiChannel = firstClip.midiChannel;
1736
+ const isPercussion = midiChannel === 9;
1737
+ const programNumber = firstClip.midiProgram ?? 0;
1738
+ p.addSoundFontTrack({
1739
+ clips: midiClipInfos,
1685
1740
  track: trackObj,
1686
- effects: track.effects,
1687
- channelCount: (0, import_core6.trackChannelCount)(track)
1741
+ soundFontCache: _soundFontCache,
1742
+ programNumber,
1743
+ isPercussion,
1744
+ effects: track.effects
1688
1745
  });
1689
- }
1690
- if (midiClips.length > 0) {
1691
- const startTime = Math.min(...midiClips.map(import_core6.clipStartTime));
1692
- const endTime = Math.max(...midiClips.map(import_core6.clipEndTime));
1693
- const trackId = audioClips.length > 0 ? `${track.id}:midi` : track.id;
1694
- const trackObj = {
1695
- id: trackId,
1696
- name: track.name,
1697
- gain: track.volume,
1698
- muted: track.muted,
1699
- soloed: track.soloed,
1700
- stereoPan: track.pan,
1701
- startTime,
1702
- endTime
1703
- };
1704
- const midiClipInfos = midiClips.map((clip) => ({
1705
- notes: clip.midiNotes,
1706
- startTime: (0, import_core6.clipStartTime)(clip) - startTime,
1707
- duration: (0, import_core6.clipDurationTime)(clip),
1708
- offset: (0, import_core6.clipOffsetTime)(clip)
1709
- }));
1710
- if (options?.soundFontCache?.isLoaded) {
1711
- const firstClip = midiClips[0];
1712
- const midiChannel = firstClip.midiChannel;
1713
- const isPercussion = midiChannel === 9;
1714
- const programNumber = firstClip.midiProgram ?? 0;
1715
- p.addSoundFontTrack({
1716
- clips: midiClipInfos,
1717
- track: trackObj,
1718
- soundFontCache: options.soundFontCache,
1719
- programNumber,
1720
- isPercussion,
1721
- effects: track.effects
1722
- });
1723
- } else {
1724
- if (options?.soundFontCache) {
1725
- console.warn(
1726
- `[waveform-playlist] SoundFont not loaded for track "${track.name}" \u2014 falling back to PolySynth.`
1727
- );
1728
- }
1729
- p.addMidiTrack({
1730
- clips: midiClipInfos,
1731
- track: trackObj,
1732
- effects: track.effects
1733
- });
1746
+ _midiTrackBuild.set(trackId, _soundFontCache);
1747
+ } else {
1748
+ if (_soundFontCache) {
1749
+ console.warn(
1750
+ `[waveform-playlist] SoundFont not loaded for track "${track.name}" \u2014 falling back to PolySynth.`
1751
+ );
1734
1752
  }
1753
+ p.addMidiTrack({
1754
+ clips: midiClipInfos,
1755
+ track: trackObj,
1756
+ effects: track.effects
1757
+ });
1758
+ _midiTrackBuild.set(trackId, null);
1735
1759
  }
1736
1760
  }
1761
+ function addTrackToPlayout(p, track) {
1762
+ addAudioTrackToPlayout(p, track);
1763
+ addMidiTrackToPlayout(p, track);
1764
+ }
1737
1765
  function buildPlayout(tracks) {
1738
1766
  if (playout) {
1739
1767
  try {
@@ -1781,6 +1809,11 @@ function createToneAdapter(options) {
1781
1809
  }
1782
1810
  },
1783
1811
  setTracks(tracks) {
1812
+ _currentTracks.clear();
1813
+ _midiTrackBuild.clear();
1814
+ for (const track of tracks) {
1815
+ _currentTracks.set(track.id, track);
1816
+ }
1784
1817
  if (!playout) {
1785
1818
  buildPlayout(tracks);
1786
1819
  return;
@@ -1808,6 +1841,7 @@ function createToneAdapter(options) {
1808
1841
  }
1809
1842
  },
1810
1843
  updateTrack(trackId, track) {
1844
+ _currentTracks.set(trackId, track);
1811
1845
  if (!playout) return;
1812
1846
  const audioClips = track.clips.filter((c) => c.audioBuffer && !c.midiNotes);
1813
1847
  if (audioClips.length > 0) {
@@ -1826,7 +1860,7 @@ function createToneAdapter(options) {
1826
1860
  if (midiClips.length > 0) {
1827
1861
  const midiTrackId = trackId + ":midi";
1828
1862
  playout.removeTrack(midiTrackId);
1829
- addTrackToPlayout(playout, track);
1863
+ addMidiTrackToPlayout(playout, track);
1830
1864
  if (_isPlaying) {
1831
1865
  playout.resumeTrackMidPlayback(midiTrackId);
1832
1866
  }
@@ -1838,6 +1872,8 @@ function createToneAdapter(options) {
1838
1872
  }
1839
1873
  playout.removeTrack(trackId);
1840
1874
  playout.removeTrack(trackId + ":midi");
1875
+ _midiTrackBuild.delete(trackId);
1876
+ _midiTrackBuild.delete(trackId + ":midi");
1841
1877
  addTrackToPlayout(playout, track);
1842
1878
  playout.applyInitialSoloState();
1843
1879
  if (_isPlaying) {
@@ -1846,6 +1882,7 @@ function createToneAdapter(options) {
1846
1882
  }
1847
1883
  },
1848
1884
  addTrack(track) {
1885
+ _currentTracks.set(track.id, track);
1849
1886
  if (!playout) {
1850
1887
  console.warn(
1851
1888
  "[waveform-playlist] adapter.addTrack() called but playout is not available (adapter may have been disposed)."
@@ -1856,8 +1893,12 @@ function createToneAdapter(options) {
1856
1893
  playout.applyInitialSoloState();
1857
1894
  },
1858
1895
  removeTrack(trackId) {
1896
+ _currentTracks.delete(trackId);
1897
+ _midiTrackBuild.delete(trackId);
1898
+ _midiTrackBuild.delete(trackId + ":midi");
1859
1899
  if (!playout) return;
1860
1900
  playout.removeTrack(trackId);
1901
+ playout.removeTrack(trackId + ":midi");
1861
1902
  playout.applyInitialSoloState();
1862
1903
  },
1863
1904
  play(startTime, endTime) {
@@ -1892,15 +1933,23 @@ function createToneAdapter(options) {
1892
1933
  playout?.setMasterGain(volume);
1893
1934
  },
1894
1935
  setTrackVolume(trackId, volume) {
1936
+ const existing = _currentTracks.get(trackId);
1937
+ if (existing) _currentTracks.set(trackId, { ...existing, volume });
1895
1938
  playout?.getTrack(trackId)?.setVolume(volume);
1896
1939
  },
1897
1940
  setTrackMute(trackId, muted) {
1941
+ const existing = _currentTracks.get(trackId);
1942
+ if (existing) _currentTracks.set(trackId, { ...existing, muted });
1898
1943
  playout?.setMute(trackId, muted);
1899
1944
  },
1900
1945
  setTrackSolo(trackId, soloed) {
1946
+ const existing = _currentTracks.get(trackId);
1947
+ if (existing) _currentTracks.set(trackId, { ...existing, soloed });
1901
1948
  playout?.setSolo(trackId, soloed);
1902
1949
  },
1903
1950
  setTrackPan(trackId, pan) {
1951
+ const existing = _currentTracks.get(trackId);
1952
+ if (existing) _currentTracks.set(trackId, { ...existing, pan });
1904
1953
  playout?.getTrack(trackId)?.setPan(pan);
1905
1954
  },
1906
1955
  setLoop(enabled, start2, end) {
@@ -1912,6 +1961,9 @@ function createToneAdapter(options) {
1912
1961
  get audioContext() {
1913
1962
  return getGlobalAudioContext();
1914
1963
  },
1964
+ get lookAhead() {
1965
+ return getGlobalContext().lookAhead ?? 0;
1966
+ },
1915
1967
  get ppqn() {
1916
1968
  return _ppqn;
1917
1969
  },
@@ -1956,6 +2008,38 @@ function createToneAdapter(options) {
1956
2008
  }
1957
2009
  return playout.masterOutputNode;
1958
2010
  },
2011
+ setSoundFontCache(cache) {
2012
+ _soundFontCache = cache;
2013
+ if (cache && !cache.isLoaded) {
2014
+ console.warn(
2015
+ "[waveform-playlist] setSoundFontCache called with a SoundFontCache that is not loaded \u2014 MIDI tracks remain on PolySynth. Await cache.load() and call again."
2016
+ );
2017
+ }
2018
+ if (!playout) return;
2019
+ const effective = cache?.isLoaded ? cache : null;
2020
+ let changed = false;
2021
+ for (const track of _currentTracks.values()) {
2022
+ const hasMidi = track.clips.some((c) => c.midiNotes && c.midiNotes.length > 0);
2023
+ if (!hasMidi) continue;
2024
+ const midiTrackId = midiPlayoutTrackId(track);
2025
+ if (_midiTrackBuild.get(midiTrackId) === effective) continue;
2026
+ try {
2027
+ playout.removeTrack(midiTrackId);
2028
+ addMidiTrackToPlayout(playout, track);
2029
+ if (_isPlaying) {
2030
+ playout.resumeTrackMidPlayback(midiTrackId);
2031
+ }
2032
+ changed = true;
2033
+ } catch (err) {
2034
+ console.warn(
2035
+ `[waveform-playlist] SoundFont swap failed for track "${track.name}": ` + String(err)
2036
+ );
2037
+ }
2038
+ }
2039
+ if (changed) {
2040
+ playout.applyInitialSoloState();
2041
+ }
2042
+ },
1959
2043
  dispose() {
1960
2044
  try {
1961
2045
  playout?.dispose();
@@ -1964,6 +2048,8 @@ function createToneAdapter(options) {
1964
2048
  }
1965
2049
  playout = null;
1966
2050
  _isPlaying = false;
2051
+ _currentTracks.clear();
2052
+ _midiTrackBuild.clear();
1967
2053
  }
1968
2054
  };
1969
2055
  }
@@ -1988,6 +2074,7 @@ function createToneAdapter(options) {
1988
2074
  getUnderlyingAudioParam,
1989
2075
  hasMediaStreamSource,
1990
2076
  int16ToFloat32,
2077
+ isToneAdapter,
1991
2078
  releaseMediaStreamSource,
1992
2079
  resumeGlobalAudioContext,
1993
2080
  timecentsToSeconds