@lumavox/react-native 0.9.0 → 0.10.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/README.md CHANGED
@@ -54,7 +54,10 @@ function Talk({ agentId }: { agentId: string }) {
54
54
  - A call, Siri or another app pauses the reply and throws away a recording
55
55
  in progress — `interrupted`, `onAudioInterrupted`, `onAudioResumed`; so
56
56
  does leaving the screen: no background audio in this release.
57
- - Hands-free and background audio come with `react-native-audio-api` 1.0.
57
+ - Hands-free (`agent.listen()`) runs on the web, where the browser cancels
58
+ echo; on a phone it is refused until `react-native-audio-api` 1.0 does —
59
+ `listen({ assumeNoEcho: true })` with headphones on. Background audio
60
+ comes with 1.0 too.
58
61
 
59
62
  The guide, with the bare React Native setup: the docs' **SDKs** tab,
60
63
  *React Native*.
@@ -1,6 +1,8 @@
1
1
  import type { AudioOptions, CreateAudio, DeviceAudio, Output } from "./device-audio.js";
2
2
  /** Hz the microphone records at unless a rate the socket takes is asked. */
3
3
  export declare const RECORD_RATE = 16000;
4
+ /** Why `handsFree` refuses on a phone. */
5
+ export declare const PHONE_ECHO = "Hands-free needs echo cancellation, which comes with react-native-audio-api 1.0. Use push-to-talk, or assumeNoEcho with headphones on.";
4
6
  export declare class PhoneAudio implements DeviceAudio {
5
7
  interrupted: boolean;
6
8
  private context;
@@ -29,6 +31,7 @@ export declare class PhoneAudio implements DeviceAudio {
29
31
  stopCapture(): Promise<void>;
30
32
  resume(): Promise<void>;
31
33
  setOutput(output: Output): void;
34
+ handsFree(): ReturnType<DeviceAudio["handsFree"]>;
32
35
  onInterruption(fn: (interrupted: boolean) => void): () => void;
33
36
  dispose(): void;
34
37
  /** The queue for a reply at `rate`: this one, or a new context's. */
@@ -26,12 +26,17 @@
26
26
  * thrown away (the hook discards the recording on the socket). The audio
27
27
  * comes back on its own when the system says it may, else on `resume()`
28
28
  * or the next press.
29
+ * - **No hands-free**: 0.13.6 cancels no echo, so a microphone left open
30
+ * hears the agent (D12, D16). `handsFree` refuses until 1.0; with
31
+ * headphones on, the app passes `assumeNoEcho`.
29
32
  */
30
33
  import { DEFAULT_SAMPLE_RATE, floatToPcm16, pcm16ToFloat, RECORDING_RATES, resample, } from "@lumavox/client";
31
34
  import { AudioContext, AudioRecorder, } from "react-native-audio-api";
32
35
  import * as session from "./session.js";
33
36
  /** Hz the microphone records at unless a rate the socket takes is asked. */
34
37
  export const RECORD_RATE = 16000;
38
+ /** Why `handsFree` refuses on a phone. */
39
+ export const PHONE_ECHO = "Hands-free needs echo cancellation, which comes with react-native-audio-api 1.0. Use push-to-talk, or assumeNoEcho with headphones on.";
35
40
  /** Seconds of microphone per buffer delivered: the recorder's own latency. */
36
41
  const RECORD_SECONDS = 0.1;
37
42
  export class PhoneAudio {
@@ -156,6 +161,9 @@ export class PhoneAudio {
156
161
  setOutput(output) {
157
162
  session.configure(output);
158
163
  }
164
+ async handsFree() {
165
+ return { ok: false, reason: PHONE_ECHO };
166
+ }
159
167
  onInterruption(fn) {
160
168
  this.interruptions.add(fn);
161
169
  return () => {
@@ -186,7 +194,11 @@ export class PhoneAudio {
186
194
  this.tell(this.drained);
187
195
  }
188
196
  };
189
- queue.start();
197
+ // Never `start()`: 0.13.6's wrapper defaults `offset` to -1 and then
198
+ // throws on its own default (`offset must be a finite non-negative
199
+ // number: -1`), which crashed the first reply on Android's emulator.
200
+ // The queue is empty here, so the native side ignores the 0.
201
+ queue.start(0, 0);
190
202
  // Made while the audio is taken: it waits, as the one it replaced did.
191
203
  if (this.interrupted)
192
204
  this.quiet(context);
package/dist/audio.web.js CHANGED
@@ -29,6 +29,12 @@ class BrowserAudio {
29
29
  return this.web.prime();
30
30
  }
31
31
  setOutput(_output) { }
32
+ handsFree() {
33
+ return this.web.handsFree();
34
+ }
35
+ duck(on) {
36
+ this.web.duck(on);
37
+ }
32
38
  onInterruption(_fn) {
33
39
  return () => { };
34
40
  }
@@ -34,6 +34,17 @@ export interface DeviceAudio extends AgentAudio {
34
34
  /** The loudspeaker or the earpiece, from now on (`AudioOptions.output`). */
35
35
  setOutput(output: Output): void;
36
36
  onInterruption(fn: (interrupted: boolean) => void): () => void;
37
+ /**
38
+ * Whether hands-free may run (`useAgent`'s `listen`): on the web where
39
+ * the browser cancels echo; on a phone not until `react-native-audio-api`
40
+ * 1.0 does — with headphones on, an app passes `assumeNoEcho`.
41
+ */
42
+ handsFree(): Promise<{
43
+ ok: true;
44
+ } | {
45
+ ok: false;
46
+ reason: string;
47
+ }>;
37
48
  }
38
49
  /** `createAudio`'s signature, which each platform's file implements. */
39
50
  export type CreateAudio = (options?: AudioOptions) => DeviceAudio;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lumavox/react-native",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Voice conversations with Lumavox agents in a React Native app, on iOS, Android and the web: the client, the React hooks, and push-to-talk audio on react-native-audio-api.",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -30,8 +30,8 @@
30
30
  "typecheck": "tsc --noEmit && tsc --noEmit -p test"
31
31
  },
32
32
  "dependencies": {
33
- "@lumavox/client": "0.9.0",
34
- "@lumavox/react": "0.9.0"
33
+ "@lumavox/client": "0.10.0",
34
+ "@lumavox/react": "0.10.0"
35
35
  },
36
36
  "peerDependencies": {
37
37
  "react": ">=18",