@absolutejs/voice 0.0.22-beta.581 → 0.0.22-beta.582

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.
@@ -22,9 +22,16 @@ type MinimalGainNode = {
22
22
  value: number;
23
23
  };
24
24
  };
25
+ type MinimalAnalyserNode = {
26
+ connect?: (destination: unknown) => void;
27
+ disconnect?: () => void;
28
+ fftSize: number;
29
+ getByteTimeDomainData: (array: Uint8Array) => void;
30
+ };
25
31
  type MinimalAudioContext = {
26
32
  baseLatency?: number;
27
33
  close: () => Promise<void>;
34
+ createAnalyser?: () => MinimalAnalyserNode;
28
35
  createBuffer: (numberOfChannels: number, length: number, sampleRate: number) => MinimalAudioBuffer;
29
36
  createBufferSource: () => MinimalAudioBufferSourceNode;
30
37
  createGain?: () => MinimalGainNode;
@@ -1693,6 +1693,8 @@ var DEFAULT_PLAYBACK_RATE = 1;
1693
1693
  var MIN_PLAYBACK_RATE = 0.5;
1694
1694
  var MAX_PLAYBACK_RATE = 2;
1695
1695
  var STRETCH_BYPASS_EPSILON = 0.01;
1696
+ var ANALYSER_FFT_SIZE = 256;
1697
+ var PCM_BYTE_MIDPOINT = 128;
1696
1698
  var createInitialState3 = () => ({
1697
1699
  activeSourceCount: 0,
1698
1700
  error: null,
@@ -1753,6 +1755,8 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1753
1755
  let state = createInitialState3();
1754
1756
  let audioContext = null;
1755
1757
  let outputNode = null;
1758
+ let analyserNode = null;
1759
+ let analyserBuffer = null;
1756
1760
  let volume = clampVolume(options.volume);
1757
1761
  let playbackRate = clampPlaybackRate(options.playbackRate);
1758
1762
  let stretcher = null;
@@ -1849,6 +1853,12 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1849
1853
  if (audioContext.createGain) {
1850
1854
  outputNode = audioContext.createGain();
1851
1855
  outputNode.connect?.(audioContext.destination);
1856
+ if (audioContext.createAnalyser) {
1857
+ analyserNode = audioContext.createAnalyser();
1858
+ analyserNode.fftSize = ANALYSER_FFT_SIZE;
1859
+ analyserBuffer = new Uint8Array(analyserNode.fftSize);
1860
+ outputNode.connect?.(analyserNode);
1861
+ }
1852
1862
  }
1853
1863
  queueEndTime = audioContext.currentTime;
1854
1864
  return audioContext;
@@ -1973,6 +1983,9 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1973
1983
  audioContext = null;
1974
1984
  outputNode?.disconnect?.();
1975
1985
  outputNode = null;
1986
+ analyserNode?.disconnect?.();
1987
+ analyserNode = null;
1988
+ analyserBuffer = null;
1976
1989
  queueEndTime = 0;
1977
1990
  setState({
1978
1991
  activeSourceCount: 0,
@@ -1983,6 +1996,18 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1983
1996
  get error() {
1984
1997
  return state.error;
1985
1998
  },
1999
+ getOutputLevel: () => {
2000
+ if (!analyserNode || !analyserBuffer) {
2001
+ return 0;
2002
+ }
2003
+ analyserNode.getByteTimeDomainData(analyserBuffer);
2004
+ let sumSquares = 0;
2005
+ for (const sample of analyserBuffer) {
2006
+ const centered = (sample - PCM_BYTE_MIDPOINT) / PCM_BYTE_MIDPOINT;
2007
+ sumSquares += centered * centered;
2008
+ }
2009
+ return Math.sqrt(sumSquares / analyserBuffer.length);
2010
+ },
1986
2011
  getSnapshot: () => state,
1987
2012
  interrupt: async () => {
1988
2013
  const startedAt = Date.now();
@@ -529,6 +529,8 @@ var DEFAULT_PLAYBACK_RATE = 1;
529
529
  var MIN_PLAYBACK_RATE = 0.5;
530
530
  var MAX_PLAYBACK_RATE = 2;
531
531
  var STRETCH_BYPASS_EPSILON = 0.01;
532
+ var ANALYSER_FFT_SIZE = 256;
533
+ var PCM_BYTE_MIDPOINT = 128;
532
534
  var createInitialState = () => ({
533
535
  activeSourceCount: 0,
534
536
  error: null,
@@ -589,6 +591,8 @@ var createVoiceAudioPlayer = (source, options = {}) => {
589
591
  let state = createInitialState();
590
592
  let audioContext = null;
591
593
  let outputNode = null;
594
+ let analyserNode = null;
595
+ let analyserBuffer = null;
592
596
  let volume = clampVolume(options.volume);
593
597
  let playbackRate = clampPlaybackRate(options.playbackRate);
594
598
  let stretcher = null;
@@ -685,6 +689,12 @@ var createVoiceAudioPlayer = (source, options = {}) => {
685
689
  if (audioContext.createGain) {
686
690
  outputNode = audioContext.createGain();
687
691
  outputNode.connect?.(audioContext.destination);
692
+ if (audioContext.createAnalyser) {
693
+ analyserNode = audioContext.createAnalyser();
694
+ analyserNode.fftSize = ANALYSER_FFT_SIZE;
695
+ analyserBuffer = new Uint8Array(analyserNode.fftSize);
696
+ outputNode.connect?.(analyserNode);
697
+ }
688
698
  }
689
699
  queueEndTime = audioContext.currentTime;
690
700
  return audioContext;
@@ -809,6 +819,9 @@ var createVoiceAudioPlayer = (source, options = {}) => {
809
819
  audioContext = null;
810
820
  outputNode?.disconnect?.();
811
821
  outputNode = null;
822
+ analyserNode?.disconnect?.();
823
+ analyserNode = null;
824
+ analyserBuffer = null;
812
825
  queueEndTime = 0;
813
826
  setState({
814
827
  activeSourceCount: 0,
@@ -819,6 +832,18 @@ var createVoiceAudioPlayer = (source, options = {}) => {
819
832
  get error() {
820
833
  return state.error;
821
834
  },
835
+ getOutputLevel: () => {
836
+ if (!analyserNode || !analyserBuffer) {
837
+ return 0;
838
+ }
839
+ analyserNode.getByteTimeDomainData(analyserBuffer);
840
+ let sumSquares = 0;
841
+ for (const sample of analyserBuffer) {
842
+ const centered = (sample - PCM_BYTE_MIDPOINT) / PCM_BYTE_MIDPOINT;
843
+ sumSquares += centered * centered;
844
+ }
845
+ return Math.sqrt(sumSquares / analyserBuffer.length);
846
+ },
822
847
  getSnapshot: () => state,
823
848
  interrupt: async () => {
824
849
  const startedAt = Date.now();
@@ -1331,6 +1331,9 @@ export type VoiceAudioPlayerSource = {
1331
1331
  export type VoiceAudioPlayer = {
1332
1332
  close: () => Promise<void>;
1333
1333
  error: string | null;
1334
+ /** Instantaneous RMS amplitude (0..1) of the assistant's audio output — for
1335
+ * driving a visualizer from the actual voice. 0 when idle / no analyser. */
1336
+ getOutputLevel: () => number;
1334
1337
  getSnapshot: () => VoiceAudioPlayerState;
1335
1338
  activeSourceCount: number;
1336
1339
  isActive: boolean;
@@ -1736,6 +1736,8 @@ var DEFAULT_PLAYBACK_RATE = 1;
1736
1736
  var MIN_PLAYBACK_RATE = 0.5;
1737
1737
  var MAX_PLAYBACK_RATE = 2;
1738
1738
  var STRETCH_BYPASS_EPSILON = 0.01;
1739
+ var ANALYSER_FFT_SIZE = 256;
1740
+ var PCM_BYTE_MIDPOINT = 128;
1739
1741
  var createInitialState = () => ({
1740
1742
  activeSourceCount: 0,
1741
1743
  error: null,
@@ -1796,6 +1798,8 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1796
1798
  let state = createInitialState();
1797
1799
  let audioContext = null;
1798
1800
  let outputNode = null;
1801
+ let analyserNode = null;
1802
+ let analyserBuffer = null;
1799
1803
  let volume = clampVolume(options.volume);
1800
1804
  let playbackRate = clampPlaybackRate(options.playbackRate);
1801
1805
  let stretcher = null;
@@ -1892,6 +1896,12 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1892
1896
  if (audioContext.createGain) {
1893
1897
  outputNode = audioContext.createGain();
1894
1898
  outputNode.connect?.(audioContext.destination);
1899
+ if (audioContext.createAnalyser) {
1900
+ analyserNode = audioContext.createAnalyser();
1901
+ analyserNode.fftSize = ANALYSER_FFT_SIZE;
1902
+ analyserBuffer = new Uint8Array(analyserNode.fftSize);
1903
+ outputNode.connect?.(analyserNode);
1904
+ }
1895
1905
  }
1896
1906
  queueEndTime = audioContext.currentTime;
1897
1907
  return audioContext;
@@ -2016,6 +2026,9 @@ var createVoiceAudioPlayer = (source, options = {}) => {
2016
2026
  audioContext = null;
2017
2027
  outputNode?.disconnect?.();
2018
2028
  outputNode = null;
2029
+ analyserNode?.disconnect?.();
2030
+ analyserNode = null;
2031
+ analyserBuffer = null;
2019
2032
  queueEndTime = 0;
2020
2033
  setState({
2021
2034
  activeSourceCount: 0,
@@ -2026,6 +2039,18 @@ var createVoiceAudioPlayer = (source, options = {}) => {
2026
2039
  get error() {
2027
2040
  return state.error;
2028
2041
  },
2042
+ getOutputLevel: () => {
2043
+ if (!analyserNode || !analyserBuffer) {
2044
+ return 0;
2045
+ }
2046
+ analyserNode.getByteTimeDomainData(analyserBuffer);
2047
+ let sumSquares = 0;
2048
+ for (const sample of analyserBuffer) {
2049
+ const centered = (sample - PCM_BYTE_MIDPOINT) / PCM_BYTE_MIDPOINT;
2050
+ sumSquares += centered * centered;
2051
+ }
2052
+ return Math.sqrt(sumSquares / analyserBuffer.length);
2053
+ },
2029
2054
  getSnapshot: () => state,
2030
2055
  interrupt: async () => {
2031
2056
  const startedAt = Date.now();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.581",
3
+ "version": "0.0.22-beta.582",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",