@absolutejs/voice 0.0.22-beta.566 → 0.0.22-beta.568

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/LICENSE ADDED
@@ -0,0 +1,88 @@
1
+ # Business Source License 1.1
2
+
3
+ **Licensor:** Alex Kahn
4
+
5
+ **Licensed Work:** @absolutejs/voice (https://github.com/absolutejs/voice)
6
+
7
+ **Change Date:** May 29, 2030
8
+
9
+ **Change License:** Apache License, Version 2.0
10
+
11
+ ---
12
+
13
+ ## Terms
14
+
15
+ The Licensor hereby grants you the right to copy, modify, create derivative
16
+ works, redistribute, and make non-production use of the Licensed Work. The
17
+ Licensor may make an Additional Use Grant, permitting limited production use.
18
+
19
+ ### Additional Use Grant
20
+
21
+ You may use the Licensed Work in production, provided your use does not include
22
+ any of the following:
23
+
24
+ 1. **Offering a Competing Service.** You may not offer the Licensed Work, or
25
+ any derivative or substantial portion of it, to third parties as a hosted or
26
+ managed service that competes with a hosted voice AI, voice agent, conversational voice, or voice-as-a-service platform (including, but not limited to, services like Vapi, Retell, Bland, Phonely, Air, or LiveKit Agents). This includes any
27
+ product whose primary value to its users is the functionality the Licensed
28
+ Work provides.
29
+
30
+ 2. **Resale or Redistribution as a Standalone Product.** You may not sell,
31
+ license, or distribute the Licensed Work, or any derivative or fork of it,
32
+ as a standalone commercial product.
33
+
34
+ 3. **Removal of Attribution.** Any derivative work, fork, or redistribution of
35
+ the Licensed Work must prominently credit AbsoluteJS and include a link to
36
+ the original project repository (https://github.com/absolutejs/voice).
37
+
38
+ For clarity, the following uses are expressly permitted:
39
+
40
+ - Using the Licensed Work to build and operate your own applications, websites,
41
+ internal tools, or SaaS products (whether commercial or non-commercial), so
42
+ long as the Licensed Work itself is not the primary product you are selling.
43
+ - Using the Licensed Work as a dependency in commercial software you build and
44
+ sell, as long as the software is not itself a competing managed service of
45
+ the kind described in clause 1.
46
+ - Providing consulting, development, or professional services to clients using
47
+ the Licensed Work.
48
+ - Forking and modifying the Licensed Work for your own internal use, provided
49
+ attribution is maintained.
50
+
51
+ ### Change Date and Change License
52
+
53
+ On the Change Date specified above, or on such other date as the Licensor may
54
+ specify by written notice, the Licensed Work will be made available under the
55
+ Change License (Apache License, Version 2.0). Until the Change Date, the terms
56
+ of this Business Source License 1.1 apply.
57
+
58
+ ### Trademark
59
+
60
+ This license does not grant you any rights to use the "AbsoluteJS" or
61
+ "@absolutejs" name, logo, or any related trademarks. Forks and derivative works
62
+ must not be named or branded in a manner that suggests endorsement by or
63
+ affiliation with AbsoluteJS or the Licensor.
64
+
65
+ ### Notices
66
+
67
+ You must not remove or obscure any licensing, copyright, or other notices
68
+ included in the Licensed Work.
69
+
70
+ ### No Warranty
71
+
72
+ THE LICENSED WORK IS PROVIDED "AS IS". THE LICENSOR HEREBY DISCLAIMS ALL
73
+ WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF
74
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO
75
+ EVENT SHALL THE LICENSOR BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY,
76
+ WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR
77
+ IN CONNECTION WITH THE LICENSED WORK OR THE USE OR OTHER DEALINGS IN THE
78
+ LICENSED WORK.
79
+
80
+ ---
81
+
82
+ ## Contact
83
+
84
+ For commercial licensing inquiries or additional permissions, contact:
85
+
86
+ - **Alex Kahn**
87
+ - alexkahndev@gmail.com
88
+ - alexkahndev.github.io
@@ -1529,6 +1529,7 @@ var createVoiceController = (path, options = {}) => {
1529
1529
 
1530
1530
  // src/client/audioPlayer.ts
1531
1531
  var DEFAULT_LOOKAHEAD_MS = 15;
1532
+ var DEFAULT_VOLUME = 1;
1532
1533
  var createInitialState3 = () => ({
1533
1534
  activeSourceCount: 0,
1534
1535
  error: null,
@@ -1545,6 +1546,12 @@ var getAudioContextCtor = () => {
1545
1546
  }
1546
1547
  return window.AudioContext ?? window.webkitAudioContext;
1547
1548
  };
1549
+ var clampVolume = (volume) => {
1550
+ if (typeof volume !== "number" || !Number.isFinite(volume)) {
1551
+ return DEFAULT_VOLUME;
1552
+ }
1553
+ return Math.min(1, Math.max(0, volume));
1554
+ };
1548
1555
  var decodePCM16LEChunk = (audioContext, chunk) => {
1549
1556
  const { format } = chunk;
1550
1557
  if (format.container !== "raw" || format.encoding !== "pcm_s16le") {
@@ -1577,6 +1584,7 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1577
1584
  let state = createInitialState3();
1578
1585
  let audioContext = null;
1579
1586
  let outputNode = null;
1587
+ let volume = clampVolume(options.volume);
1580
1588
  let queueEndTime = 0;
1581
1589
  let syncPromise = Promise.resolve();
1582
1590
  let interruptStartedAt = null;
@@ -1625,11 +1633,11 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1625
1633
  }
1626
1634
  return Math.max(0, ((context.baseLatency ?? 0) + (context.outputLatency ?? 0)) * 1000);
1627
1635
  };
1628
- const restoreOutputGain = (context) => {
1636
+ const applyOutputGain = (context) => {
1629
1637
  if (!outputNode) {
1630
1638
  return;
1631
1639
  }
1632
- const gainValue = 1;
1640
+ const gainValue = volume;
1633
1641
  if (outputNode.gain.setValueAtTime) {
1634
1642
  outputNode.gain.setValueAtTime(gainValue, context?.currentTime ?? 0);
1635
1643
  return;
@@ -1840,11 +1848,15 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1840
1848
  get queuedChunkCount() {
1841
1849
  return state.queuedChunkCount;
1842
1850
  },
1851
+ setVolume: (nextVolume) => {
1852
+ volume = clampVolume(nextVolume);
1853
+ applyOutputGain(audioContext);
1854
+ },
1843
1855
  start: async () => {
1844
1856
  try {
1845
1857
  clearError();
1846
1858
  const context = await ensureAudioContext();
1847
- restoreOutputGain(context);
1859
+ applyOutputGain(context);
1848
1860
  if (context.state === "suspended") {
1849
1861
  await context.resume();
1850
1862
  }
@@ -1868,6 +1880,9 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1868
1880
  return () => {
1869
1881
  subscribers.delete(subscriber);
1870
1882
  };
1883
+ },
1884
+ get volume() {
1885
+ return volume;
1871
1886
  }
1872
1887
  };
1873
1888
  return player;
@@ -372,6 +372,7 @@ var createVoiceConnection = (path, options = {}) => {
372
372
  };
373
373
  // src/client/audioPlayer.ts
374
374
  var DEFAULT_LOOKAHEAD_MS = 15;
375
+ var DEFAULT_VOLUME = 1;
375
376
  var createInitialState = () => ({
376
377
  activeSourceCount: 0,
377
378
  error: null,
@@ -388,6 +389,12 @@ var getAudioContextCtor = () => {
388
389
  }
389
390
  return window.AudioContext ?? window.webkitAudioContext;
390
391
  };
392
+ var clampVolume = (volume) => {
393
+ if (typeof volume !== "number" || !Number.isFinite(volume)) {
394
+ return DEFAULT_VOLUME;
395
+ }
396
+ return Math.min(1, Math.max(0, volume));
397
+ };
391
398
  var decodePCM16LEChunk = (audioContext, chunk) => {
392
399
  const { format } = chunk;
393
400
  if (format.container !== "raw" || format.encoding !== "pcm_s16le") {
@@ -420,6 +427,7 @@ var createVoiceAudioPlayer = (source, options = {}) => {
420
427
  let state = createInitialState();
421
428
  let audioContext = null;
422
429
  let outputNode = null;
430
+ let volume = clampVolume(options.volume);
423
431
  let queueEndTime = 0;
424
432
  let syncPromise = Promise.resolve();
425
433
  let interruptStartedAt = null;
@@ -468,11 +476,11 @@ var createVoiceAudioPlayer = (source, options = {}) => {
468
476
  }
469
477
  return Math.max(0, ((context.baseLatency ?? 0) + (context.outputLatency ?? 0)) * 1000);
470
478
  };
471
- const restoreOutputGain = (context) => {
479
+ const applyOutputGain = (context) => {
472
480
  if (!outputNode) {
473
481
  return;
474
482
  }
475
- const gainValue = 1;
483
+ const gainValue = volume;
476
484
  if (outputNode.gain.setValueAtTime) {
477
485
  outputNode.gain.setValueAtTime(gainValue, context?.currentTime ?? 0);
478
486
  return;
@@ -683,11 +691,15 @@ var createVoiceAudioPlayer = (source, options = {}) => {
683
691
  get queuedChunkCount() {
684
692
  return state.queuedChunkCount;
685
693
  },
694
+ setVolume: (nextVolume) => {
695
+ volume = clampVolume(nextVolume);
696
+ applyOutputGain(audioContext);
697
+ },
686
698
  start: async () => {
687
699
  try {
688
700
  clearError();
689
701
  const context = await ensureAudioContext();
690
- restoreOutputGain(context);
702
+ applyOutputGain(context);
691
703
  if (context.state === "suspended") {
692
704
  await context.resume();
693
705
  }
@@ -711,6 +723,9 @@ var createVoiceAudioPlayer = (source, options = {}) => {
711
723
  return () => {
712
724
  subscribers.delete(subscriber);
713
725
  };
726
+ },
727
+ get volume() {
728
+ return volume;
714
729
  }
715
730
  };
716
731
  return player;
@@ -1183,6 +1183,7 @@ export type VoiceAudioPlayerOptions = {
1183
1183
  autoStart?: boolean;
1184
1184
  createAudioContext?: () => AudioContext;
1185
1185
  lookaheadMs?: number;
1186
+ volume?: number;
1186
1187
  };
1187
1188
  export type VoiceDuplexControllerOptions = VoiceControllerOptions & {
1188
1189
  audioPlayer?: VoiceAudioPlayerOptions;
@@ -1314,8 +1315,10 @@ export type VoiceAudioPlayer = {
1314
1315
  pause: () => Promise<void>;
1315
1316
  processedChunkCount: number;
1316
1317
  queuedChunkCount: number;
1318
+ setVolume: (volume: number) => void;
1317
1319
  start: () => Promise<void>;
1318
1320
  subscribe: (subscriber: () => void) => () => void;
1321
+ volume: number;
1319
1322
  };
1320
1323
  export type VoiceBargeInBinding = {
1321
1324
  close: () => void;
@@ -1579,6 +1579,7 @@ var buildSessionCorrectionAudit = (raw, generic, experimental, benchmarkSeeded,
1579
1579
  };
1580
1580
  // src/client/audioPlayer.ts
1581
1581
  var DEFAULT_LOOKAHEAD_MS = 15;
1582
+ var DEFAULT_VOLUME = 1;
1582
1583
  var createInitialState = () => ({
1583
1584
  activeSourceCount: 0,
1584
1585
  error: null,
@@ -1595,6 +1596,12 @@ var getAudioContextCtor = () => {
1595
1596
  }
1596
1597
  return window.AudioContext ?? window.webkitAudioContext;
1597
1598
  };
1599
+ var clampVolume = (volume) => {
1600
+ if (typeof volume !== "number" || !Number.isFinite(volume)) {
1601
+ return DEFAULT_VOLUME;
1602
+ }
1603
+ return Math.min(1, Math.max(0, volume));
1604
+ };
1598
1605
  var decodePCM16LEChunk = (audioContext, chunk) => {
1599
1606
  const { format } = chunk;
1600
1607
  if (format.container !== "raw" || format.encoding !== "pcm_s16le") {
@@ -1627,6 +1634,7 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1627
1634
  let state = createInitialState();
1628
1635
  let audioContext = null;
1629
1636
  let outputNode = null;
1637
+ let volume = clampVolume(options.volume);
1630
1638
  let queueEndTime = 0;
1631
1639
  let syncPromise = Promise.resolve();
1632
1640
  let interruptStartedAt = null;
@@ -1675,11 +1683,11 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1675
1683
  }
1676
1684
  return Math.max(0, ((context.baseLatency ?? 0) + (context.outputLatency ?? 0)) * 1000);
1677
1685
  };
1678
- const restoreOutputGain = (context) => {
1686
+ const applyOutputGain = (context) => {
1679
1687
  if (!outputNode) {
1680
1688
  return;
1681
1689
  }
1682
- const gainValue = 1;
1690
+ const gainValue = volume;
1683
1691
  if (outputNode.gain.setValueAtTime) {
1684
1692
  outputNode.gain.setValueAtTime(gainValue, context?.currentTime ?? 0);
1685
1693
  return;
@@ -1890,11 +1898,15 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1890
1898
  get queuedChunkCount() {
1891
1899
  return state.queuedChunkCount;
1892
1900
  },
1901
+ setVolume: (nextVolume) => {
1902
+ volume = clampVolume(nextVolume);
1903
+ applyOutputGain(audioContext);
1904
+ },
1893
1905
  start: async () => {
1894
1906
  try {
1895
1907
  clearError();
1896
1908
  const context = await ensureAudioContext();
1897
- restoreOutputGain(context);
1909
+ applyOutputGain(context);
1898
1910
  if (context.state === "suspended") {
1899
1911
  await context.resume();
1900
1912
  }
@@ -1918,6 +1930,9 @@ var createVoiceAudioPlayer = (source, options = {}) => {
1918
1930
  return () => {
1919
1931
  subscribers.delete(subscriber);
1920
1932
  };
1933
+ },
1934
+ get volume() {
1935
+ return volume;
1921
1936
  }
1922
1937
  };
1923
1938
  return player;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/voice",
3
- "version": "0.0.22-beta.566",
3
+ "version": "0.0.22-beta.568",
4
4
  "description": "Voice primitives and Elysia plugin for AbsoluteJS",
5
5
  "repository": {
6
6
  "type": "git",
@@ -12,7 +12,7 @@
12
12
  ],
13
13
  "main": "./dist/index.js",
14
14
  "types": "./dist/index.d.ts",
15
- "license": "CC BY-NC 4.0",
15
+ "license": "BSL-1.1",
16
16
  "author": "Alex Kahn",
17
17
  "scripts": {
18
18
  "build": "rm -rf dist && bun build ./src/index.ts ./src/client/index.ts ./src/react/index.ts ./src/vue/index.ts ./src/svelte/index.ts ./src/angular/index.ts ./src/testing/index.ts ./src/drizzle/index.ts --outdir dist --target bun --external elysia --external react --external vue --external @angular/core --external @absolutejs/absolute --external @absolutejs/ai --external @absolutejs/media --external drizzle-orm && bun build ./src/client/htmxBootstrap.ts --outdir dist/client --target browser --format esm && bun build ./src/embed/index.ts --outfile dist/embed/voice-widget.js --target browser --format iife --minify && bun build ./src/embed/index.ts --outdir dist/embed --target browser --format esm && tsc --emitDeclarationOnly --project tsconfig.json",