@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.
- package/README.md +85 -5
- package/app.plugin.js +40 -0
- 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
|
|
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
|
-
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
```sh
|
|
7
|
+
npm install @live-assistant/audio react-native-audio-api
|
|
8
|
+
```
|
|
8
9
|
|
|
9
|
-
|
|
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.
|
|
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.
|
|
48
|
+
"@live-assistant/core": "0.3.0"
|
|
44
49
|
},
|
|
45
50
|
"peerDependencies": {
|
|
46
51
|
"react-native": ">=0.76",
|