@live-assistant/audio 0.1.0 → 0.3.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.
Files changed (3) hide show
  1. package/README.md +85 -5
  2. package/app.plugin.js +40 -0
  3. package/package.json +7 -2
package/README.md CHANGED
@@ -1,9 +1,89 @@
1
1
  # @live-assistant/audio
2
2
 
3
- `Microphone` and `PcmPlayer` for iOS, Android (`react-native-audio-api`, a peer dependency) and the web (Web Audio). The platform is picked through file extensions.
3
+ `Microphone` and `PcmPlayer` for iOS, Android and the web. The platform is picked
4
+ by file extension, so you import one name and get the right implementation.
4
5
 
5
- - Frames are resampled to the rate the session asks for, not the rate the recorder claims to deliver.
6
- - `level()` on both classes feeds animations. The player's level follows the playhead, and `remainingSeconds()` says how much audio has not been heard yet.
7
- - On iOS the session runs in `voiceChat` mode, so the app's own output is cancelled. Android has no echo cancellation here (`cancelsEcho` is false), so the controller holds the microphone shut while the assistant is audible.
6
+ ```sh
7
+ npm install @live-assistant/audio react-native-audio-api
8
+ ```
8
9
 
9
- See the [overview](../README.md).
10
+ ## Native setup — one line
11
+
12
+ The library ships an Expo config plugin, so this is the whole of it:
13
+
14
+ ```json
15
+ ["@live-assistant/audio", { "microphonePermission": "Acme uses your microphone so you can talk to the assistant." }]
16
+ ```
17
+
18
+ (If you installed `@live-assistant/react-native`, name that instead — same
19
+ plugin.)
20
+
21
+ It writes what a voice assistant needs, checked by running `expo prebuild` and
22
+ reading the **generated** files rather than the config: the microphone usage
23
+ description set to your sentence, **no** `UIBackgroundModes`,
24
+ `android.permission.RECORD_AUDIO` present, and no foreground service.
25
+
26
+ Left to `react-native-audio-api`'s own plugin defaults you would get
27
+ `UIBackgroundModes: ["audio"]` — background audio nothing here plays, which App
28
+ Review rejects under guideline 2.5.4 — and **no** microphone usage description,
29
+ so iOS terminates the app at the first request. That is why this plugin exists.
30
+ **Do not list `react-native-audio-api` in `plugins` as well**: its plugin runs
31
+ once, and whichever is listed first wins.
32
+
33
+ Two more things the first run depends on:
34
+
35
+ - **Expo Go cannot load this.** `react-native-audio-api` is a native module.
36
+ Build a development build (`npx expo prebuild && npx expo run:ios`) or use EAS.
37
+ - **Bare React Native**: add `NSMicrophoneUsageDescription` to
38
+ `ios/<App>/Info.plist` yourself; `RECORD_AUDIO` arrives through the module's
39
+ manifest merge.
40
+
41
+ ## On the web, with or without React Native
42
+
43
+ Nothing native is involved: the `.web` halves use Web Audio.
44
+
45
+ - In an **Expo / React Native Web** app, Metro picks them by file extension.
46
+ - In a **plain React** app — Vite, Next, webpack, esbuild — the package's
47
+ `browser` field points at them, so a browser build never reaches the native
48
+ module. Checked by bundling an installed copy for `platform=browser`:
49
+ `react-native-audio-api` does not appear in the output.
50
+
51
+ Either way `getUserMedia` needs a **secure context**, so serve from `https://` or
52
+ `localhost`. Anything else answers `microphone_unavailable`.
53
+
54
+ **Start the session from a user gesture.** A browser leaves
55
+ `AudioContext.resume()` pending until the page has been interacted with, so
56
+ `PcmPlayer.prepare()` called on page load never settles — and neither does
57
+ `start()`. Pressing the orb, or any button, is the gesture. Verified by running
58
+ the web halves in a plain browser build: from a click,
59
+ `prepare(24000)` returns ok, a second of audio reports `remainingSeconds=1.00`
60
+ and a real `level()`, and `flush()` takes it back to zero.
61
+
62
+ ## What it does that a recorder does not
63
+
64
+ - **It promises a sample rate rather than requesting one.** Platform recorders
65
+ treat the rate as a preference and hand back whatever the hardware runs at;
66
+ frames labelled with a rate they are not transcribe as fast noise instead of
67
+ failing. Resampling to the promised rate happens here, so no caller repeats it.
68
+ - **`level()` on both classes feeds animations**, 0–1, pulled on an animation
69
+ clock rather than pushed. The player's level follows the **playhead**, so a
70
+ glow moves with what is heard, not with what has arrived.
71
+ - **`remainingSeconds()`** says how much audio has not been heard yet — the
72
+ number the echo gate and the interruption flush are built on.
73
+ - **Echo differs by platform.** iOS runs the session in `voiceChat` mode, which
74
+ cancels the app's own output, so `cancelsEcho` is true and the user can
75
+ interrupt freely. Android has none, so `cancelsEcho` is false and
76
+ `AssistantController` holds the microphone shut while the assistant is
77
+ audible. Do not send audio yourself during `speaking`.
78
+
79
+ ## Failures
80
+
81
+ `microphone_denied` (the user said no, or iOS never asked because the usage
82
+ description is missing) · `microphone_unavailable` (no native module, or not a
83
+ secure context on the web) · `player_unavailable`.
84
+
85
+ Codes, never sentences: map them to words in your own app.
86
+
87
+ See the [overview](https://github.com/Recipely-Team/live-assistant#readme) for how this fits together.
88
+
89
+ A working app that puts this together: [`examples/expo-app`](https://github.com/Recipely-Team/live-assistant/tree/main/examples/expo-app).
package/app.plugin.js ADDED
@@ -0,0 +1,40 @@
1
+ /**
2
+ * The native configuration this library needs, so an app does not have to know
3
+ * it.
4
+ *
5
+ * @remarks
6
+ * - **Why this exists.** `react-native-audio-api` ships a config plugin whose
7
+ * defaults are wrong for a voice assistant, and both mistakes are invisible
8
+ * until late: it declares `UIBackgroundModes: ["audio"]`, which App Review
9
+ * rejects under guideline 2.5.4 when nothing plays in the background, and it
10
+ * writes no `NSMicrophoneUsageDescription`, so iOS terminates the app the
11
+ * moment it asks for the microphone. Every integrator would have had to
12
+ * discover both.
13
+ * - **What it produces**, verified by running `expo prebuild` and reading the
14
+ * generated files: `NSMicrophoneUsageDescription` set to your sentence, no
15
+ * `UIBackgroundModes`, `android.permission.RECORD_AUDIO` present, and no
16
+ * foreground service.
17
+ * - **Use this INSTEAD of listing `react-native-audio-api` yourself.** That
18
+ * plugin runs once; if the app lists it first, this one is skipped and the
19
+ * defaults win.
20
+ * - An app that genuinely plays audio while backgrounded should configure
21
+ * `react-native-audio-api` directly and not use this plugin — and be ready to
22
+ * justify the background mode to App Review.
23
+ */
24
+ // `react-native-audio-api/app.plugin` re-exports a transpiled ES module, so the
25
+ // function is under `default`. Taking the module itself hands Expo an object and
26
+ // prebuild dies with "withAudioAPI is not a function".
27
+ const audioApiPlugin = require('react-native-audio-api/app.plugin');
28
+ const withAudioAPI = audioApiPlugin.default || audioApiPlugin;
29
+
30
+ const DEFAULT_MICROPHONE_PERMISSION =
31
+ 'This app uses your microphone so you can talk to the assistant.';
32
+
33
+ module.exports = (config, props) =>
34
+ withAudioAPI(config, {
35
+ iosMicrophonePermission:
36
+ (props && props.microphonePermission) || DEFAULT_MICROPHONE_PERMISSION,
37
+ iosBackgroundMode: false,
38
+ androidForegroundService: false,
39
+ androidPermissions: [],
40
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@live-assistant/audio",
3
- "version": "0.1.0",
3
+ "version": "0.3.0",
4
4
  "description": "Microphone capture and streaming PCM playback for @live-assistant/core, on iOS, Android and the web, with levels an animation can read every frame.",
5
5
  "license": "MIT",
6
6
  "author": "Recep Tayyip Ekşi",
@@ -23,11 +23,16 @@
23
23
  ],
24
24
  "main": "dist/index.js",
25
25
  "types": "dist/index.d.ts",
26
+ "browser": {
27
+ "./dist/microphone.js": "./dist/microphone.web.js",
28
+ "./dist/pcm-player.js": "./dist/pcm-player.web.js"
29
+ },
26
30
  "files": [
27
31
  "dist",
28
32
  "src",
29
33
  "README.md",
30
34
  "LICENSE",
35
+ "app.plugin.js",
31
36
  "!src/**/__tests__",
32
37
  "!src/**/__fixtures__"
33
38
  ],
@@ -40,7 +45,7 @@
40
45
  "access": "public"
41
46
  },
42
47
  "dependencies": {
43
- "@live-assistant/core": "0.1.0"
48
+ "@live-assistant/core": "0.3.0"
44
49
  },
45
50
  "peerDependencies": {
46
51
  "react-native": ">=0.76",