mirror of
https://github.com/techieyann/frame-voice.git
synced 2026-10-18 05:01:26 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8715e16819 | ||
|
|
95e09fb571 | ||
|
|
5d4c2b0110 | ||
|
|
5dee3eb583 | ||
|
|
a2163df4d0 | ||
|
|
1ad2604a74 | ||
|
|
f3fbfb4c5e | ||
|
|
5439c4ec6d |
No files matched your search
Generated
+1
-1
@@ -66,7 +66,7 @@ checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d"
|
||||
|
||||
[[package]]
|
||||
name = "frame-voice"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cc",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "frame-voice"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
rust-version = "1.85"
|
||||
description = "Controller-triggered Steam Frame dictation"
|
||||
|
||||
@@ -8,8 +8,10 @@
|
||||
Voice dictation for Steam Frame. Speak into a text field using your controllers,
|
||||
without a keyboard or an always-on microphone.
|
||||
|
||||
Works in the Desktop app and standalone desktop apps while Steam is in gamepad
|
||||
mode. Dictation is unavailable in the Steam interface and VR games.
|
||||
**Desktop** is the Frame's built-in desktop session. **Standalone apps**, such as
|
||||
Firefox launched directly from Steam, run outside Desktop. Frame Voice types
|
||||
into both; accented and non-Latin text currently requires a standalone app.
|
||||
Dictation is unavailable inside Steam menus and VR games.
|
||||
|
||||
## Install on your Frame
|
||||
|
||||
@@ -20,75 +22,52 @@ mode. Dictation is unavailable in the Steam interface and VR games.
|
||||
curl -fsSL https://raw.githubusercontent.com/techieyann/frame-voice/main/get.sh | sh
|
||||
```
|
||||
|
||||
3. Follow setup to choose how your speech is transcribed. If asked, enter your
|
||||
SteamOS administrator password to allow text input. If you have never set
|
||||
3. The GUI installer opens in Desktop. Choose how your speech is transcribed.
|
||||
If asked, enter your SteamOS administrator password to allow text input. If you have never set
|
||||
that password, run `passwd` in Konsole first.
|
||||
4. Find the Frame Voice microphone icon in the Desktop tray.
|
||||
|
||||
## Choose transcription and language
|
||||
|
||||
| Option | What you need | Why choose it |
|
||||
| --- | --- | --- |
|
||||
| **Groq · Cloud** | Internet and a [Groq API key](https://console.groq.com/keys) | Speech is sent to Groq for transcription; no local model download |
|
||||
| **Local · Fast** | A model download of about 142 MiB | Transcription runs on your Frame and works offline after setup |
|
||||
| **Local · Balanced** | A model download of about 466 MiB | Favors accuracy over speed and works offline after setup |
|
||||
| Model | Runs on | Speed | Accuracy | Internet needed | Requirements |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| Whisper Base | Local | Medium | Iffy | Initial download only | ~142MiB |
|
||||
| Whisper Small | Local | Slow | Good | Initial download only | ~466MiB |
|
||||
| Whisper Large v3 Turbo | Groq | Fast | Best | Every dictation | [API key](https://console.groq.com/keys) |
|
||||
|
||||
Whisper models were created by [OpenAI](https://github.com/openai/whisper). Local transcription sends no audio to OpenAI or Groq; the models run on your Frame using [whisper.cpp](https://github.com/ggml-org/whisper.cpp).
|
||||
|
||||
Choose **Language** in the Transcription tab, or **Automatic detection** if you
|
||||
use more than one language. Local setup downloads the appropriate English or
|
||||
multilingual model. Groq needs no language-specific download.
|
||||
|
||||
For accented characters or non-Latin scripts, use a standalone app such as
|
||||
Firefox. These characters are not yet supported inside the nested Desktop.
|
||||
|
||||
## Try your first dictation
|
||||
|
||||
1. Open an app and focus the text field you want to fill.
|
||||
<img src="assets/icons/dictation-flow.svg" width="720" alt="Tap, hold, speak, release, processing, text appears" />
|
||||
|
||||
1. Open Firefox, start a new tab, and click its address bar.
|
||||
2. Briefly touch either joystick cap, lift your thumb, then touch it again and hold.
|
||||
3. Wait for the start tone and microphone badge, then speak.
|
||||
4. Lift your thumb to finish. Wait for the transcription to appear.
|
||||
|
||||
Click the joystick during recording to cancel. After text appears, **A** submits
|
||||
and releasing **B** clears; the left D-pad right/left do the same. Submit and clear are
|
||||
available briefly after dictation and stop when you change focus. Nothing submits
|
||||
automatically.
|
||||
and **X** clears. On the left hand, **D-pad Right** submits and **D-pad Left** clears.
|
||||
These actions are available briefly after dictation and stop when you change
|
||||
focus. Nothing submits automatically.
|
||||
|
||||
Open **Configure…** in the tray to customize Frame Voice. See the
|
||||
[settings guide](docs/SETTINGS.md) for the General, Transcription, and Controllers tabs.
|
||||
|
||||
## Updates
|
||||

|
||||
|
||||
Use **Configure… → General → Check for updates**. Updates preserve saved settings,
|
||||
API keys, and downloaded models. See [updating Frame Voice](docs/SETTINGS.md#updates).
|
||||
*The configuration window: choose a transcription path and language, then save.*
|
||||
|
||||
## If something does not work
|
||||
|
||||
- **No tone or microphone badge:** focus an app's text field, check that dictation
|
||||
is running in the tray, and try Restart dictation. A plain hold does not trigger
|
||||
recording; it must be tap, release, then hold.
|
||||
- **Groq produces no text:** check your internet connection and API key.
|
||||
- **Local setup fails:** check free storage and internet access for the first download.
|
||||
- **Need more detail:** choose View recent logs in the tray, or see
|
||||
[troubleshooting and configuration](docs/CONFIGURATION.md).
|
||||
Check that dictation is running in the tray and that an app's text field is
|
||||
focused. Use **View recent logs** in the tray for more detail, or follow the
|
||||
[troubleshooting guide](docs/TROUBLESHOOTING.md).
|
||||
|
||||
## For developers and agents
|
||||
|
||||
The installer provides `./install-gui.sh` for GUI setup, `--gui` for Desktop
|
||||
setup, and `--tui` for a terminal menu. Agents can use explicit flags without
|
||||
interactive dialogs:
|
||||
|
||||
```sh
|
||||
./install.sh ./bin/frame-voice --tray-binary ./bin/frame-voice-tray \
|
||||
--non-interactive --backend local-fast --language auto
|
||||
```
|
||||
|
||||
For Groq, use `--backend groq --language en --groq-key-file /path/to/key`.
|
||||
Noninteractive setup requires input permissions to be configured already.
|
||||
|
||||
`frame-voice --version` shows the version and build revision.
|
||||
`frame-voice --check-updates --json` returns update status; use
|
||||
`~/.local/share/frame-voice/update.sh --tui` for a guided terminal update.
|
||||
See the [configuration reference](docs/CONFIGURATION.md) for advanced settings.
|
||||
|
||||
[Installation and removal](docs/INSTALLATION.md) ·
|
||||
[Settings by tab](docs/SETTINGS.md) ·
|
||||
[Builds and release channels](docs/DISTRIBUTION.md) · [MIT license](LICENSE)
|
||||
[Settings](docs/SETTINGS.md) · [Troubleshooting](docs/TROUBLESHOOTING.md) ·
|
||||
[Release notes](docs/RELEASE-NOTES.md) · [Developer guide](docs/DEVELOPMENT.md) ·
|
||||
[License](LICENSE)
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
# Security policy
|
||||
|
||||
Frame Voice injects keystrokes into the focused application and, when Groq
|
||||
transcription is selected, stores an API key in `~/.config/frame-voice/env`.
|
||||
Reports about either of those paths are especially welcome.
|
||||
|
||||
## Reporting a vulnerability
|
||||
|
||||
Please do not open a public issue for security problems. Use GitHub's private
|
||||
reporting at <https://github.com/techieyann/frame-voice/security/advisories/new>.
|
||||
|
||||
Include the Frame Voice version (`frame-voice --version`), the transcription
|
||||
path in use, and steps to reproduce. You should hear back within a week.
|
||||
|
||||
## Supported versions
|
||||
|
||||
Only the latest stable release receives fixes. Experimental prereleases are
|
||||
not supported once a newer build exists.
|
||||
@@ -12,7 +12,7 @@
|
||||
{ "inputs": { "click": { "output": "/actions/voicedict/in/submit" } },
|
||||
"mode": "button", "path": "/user/hand/right/input/a" },
|
||||
{ "inputs": { "click": { "output": "/actions/voicedict/in/clear" } },
|
||||
"mode": "button", "path": "/user/hand/right/input/b" },
|
||||
"mode": "button", "path": "/user/hand/right/input/x" },
|
||||
{ "inputs": { "click": { "output": "/actions/voicedict/in/submit" } },
|
||||
"mode": "button", "path": "/user/hand/left/input/dpad_right" },
|
||||
{ "inputs": { "click": { "output": "/actions/voicedict/in/clear" } },
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 49 KiB |
@@ -0,0 +1,41 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 700 130" width="2100" height="390">
|
||||
<defs>
|
||||
<g id="stick" fill="#FFFFFF">
|
||||
<rect x="-22" y="33" width="44" height="5" rx="2.5"/>
|
||||
<rect x="-5" y="15" width="10" height="19"/>
|
||||
<ellipse cx="0" cy="13" rx="16" ry="6"/><path d="M-9.5 12.2 Q0 8.3 9.5 12.2" fill="none" stroke="#161B22" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</g>
|
||||
<!-- side-view thumb entering from the left; tip at origin -->
|
||||
<g id="thumb">
|
||||
<path fill="#FFFFFF" d="M-90.5 -37.5 L-24.8 -31.8 Q-18.8 -31.3 -12.4 -24.3 Q-9.5 -17.4 -4.5 -14.6 L-5.1 -13.3 L4.1 -4.1 Q4.9 4.9 -5.7 5.7 Q-19.0 -0.8 -26.3 -13.3 Q-56.9 -8.6 -92.5 -14.6 Z"/>
|
||||
<path d="M-18.5 -28.0 L-20.4 -24.1 M-12.6 -24.2 L-15.4 -21.3 M-10.5 -20.6 L-13.1 -18.0" fill="none" stroke="#161B22" stroke-width="1.8" stroke-linecap="round"/>
|
||||
<path d="M-5.1 -13.3 Q-3.8 -6.1 3.9 1.1" fill="none" stroke="#161B22" stroke-width="1.8" stroke-linecap="round"/>
|
||||
</g>
|
||||
<clipPath id="c"><rect x="17" y="17" width="83" height="83"/></clipPath>
|
||||
</defs>
|
||||
<rect width="700" height="130" rx="18" fill="#161B22"/>
|
||||
<g transform="translate(20,15)">
|
||||
<path d="M10 92V10H92" fill="none" stroke="#FFFFFF" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g clip-path="url(#c)"><g transform="translate(60,58)"><use href="#stick"/></g><g transform="translate(58,53)"><use href="#thumb"/></g><path d="M48 61 l-4 4 M60 63 v6 M72 61 l4 4" fill="none" stroke="#161B22" stroke-width="7" stroke-linecap="round"/><path d="M48 61 l-4 4 M60 63 v6 M72 61 l4 4" fill="none" stroke="#1A9FFF" stroke-width="3" stroke-linecap="round"/><path fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M30 50 v16 M24 60 l6 6 6-6"/><path fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M88 66 v-16 M82 56 l6-6 6 6"/></g>
|
||||
</g>
|
||||
<g transform="translate(130,15)">
|
||||
<path d="M10 92V10H92" fill="none" stroke="#FFFFFF" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g clip-path="url(#c)"><g transform="translate(60,58)"><use href="#stick"/></g><g transform="translate(56,64)" stroke="#161B22" stroke-width="2.4" stroke-linejoin="round"><use href="#thumb"/></g><path fill="none" stroke="#1A9FFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M60 46 A24 24 0 1 1 43 87"/></g>
|
||||
</g>
|
||||
<g transform="translate(240,15)">
|
||||
<path d="M10 92V10H92" fill="none" stroke="#FFFFFF" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g clip-path="url(#c)"><g transform="translate(-8,-10) scale(1.35)"><path d="M8 76 V58 C2 48 2 30 12 22 C22 12 42 13 50 22 C55 28 56 36 54 44 L58 50 L53 53 C56 55 56 58 53 60 C55 63 51 67 46 67 L37 67 L37 76 Z" fill="#FFFFFF"/><path d="M6 37 C18 34 30 33 44 34" fill="none" stroke="#161B22" stroke-width="16"/><path d="M6 37 C18 34 30 33 44 34" fill="none" stroke="#FFFFFF" stroke-width="11"/><rect x="40" y="22" width="26" height="24" rx="5" fill="#FFFFFF" stroke="#161B22" stroke-width="2.2"/><g transform="translate(53,34) scale(0.55)"><rect x="-4" y="-11" width="8" height="13" rx="4" fill="#1A9FFF"/><path d="M-8 -2 a8 8 0 0 0 16 0 M0 6 v5 M-4 11 h8" fill="none" stroke="#1A9FFF" stroke-width="3.2" stroke-linecap="round" stroke-linejoin="round"/></g></g><path fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M77 61 q5 6 0 12"/><path fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M84 55 q10 12 0 24"/><path fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M91 49 q14 18 0 36"/></g>
|
||||
</g>
|
||||
<g transform="translate(350,15)">
|
||||
<path d="M10 92V10H92" fill="none" stroke="#FFFFFF" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g clip-path="url(#c)"><g transform="translate(60,58)"><use href="#stick"/></g><g transform="translate(58,45)"><use href="#thumb"/></g><path fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M88 66 v-16 M82 56 l6-6 6 6"/></g>
|
||||
</g>
|
||||
<g transform="translate(460,15)">
|
||||
<path d="M10 92V10H92" fill="none" stroke="#FFFFFF" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g clip-path="url(#c)"><g transform="translate(57,57) scale(0.9) rotate(22.5)"><g fill="#1A9FFF"><rect x="-5" y="-26" width="10" height="52" transform="rotate(0)"/><rect x="-5" y="-26" width="10" height="52" transform="rotate(45)"/><rect x="-5" y="-26" width="10" height="52" transform="rotate(90)"/><rect x="-5" y="-26" width="10" height="52" transform="rotate(135)"/><circle r="18"/></g><circle r="8" fill="#161B22"/></g><path fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M24.4 41.8 A36 36 0 0 1 84.6 33.9 M78.0 31.5 L84.6 33.9 L83.4 27.0"/><path fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="M89.6 72.2 A36 36 0 0 1 29.4 80.1 M36.0 82.5 L29.4 80.1 L30.6 87.0"/></g>
|
||||
</g>
|
||||
<g transform="translate(570,15)">
|
||||
<path d="M10 92V10H92" fill="none" stroke="#FFFFFF" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
<g clip-path="url(#c)"><rect x="22" y="32" width="64" height="40" rx="3" fill="none" stroke="#FFFFFF" stroke-width="4"/><rect x="32" y="43" width="22" height="7" fill="#FFFFFF"/><rect x="56" y="43" width="14" height="7" fill="#FFFFFF"/><rect x="32" y="55" width="16" height="7" fill="#FFFFFF"/><rect x="53" y="53" width="3" height="11" fill="#1A9FFF"/></g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
Binary file not shown.
Binary file not shown.
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72" color="#FFFFFF"><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M14 58V14H58" stroke="#FFFFFF" stroke-width="4"/><g transform="translate(40 40) rotate(-45) scale(1.45) translate(-43 -43)" stroke-width="2.8"><rect x="39" y="31" width="8" height="14" rx="4"/><path d="M34 39v3a9 9 0 0 0 18 0v-3M43 51v5M39 56h8"/></g></g></svg>
|
||||
|
After Width: | Height: | Size: 431 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72" color="#FFFFFF"><defs><mask id="cut"><rect width="72" height="72" fill="white"/><path d="M22.6 57.4L57.4 22.6" fill="none" stroke="black" stroke-width="9.425" stroke-linecap="round"/></mask></defs><g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"><path d="M14 58V14H58" stroke="#FFFFFF" stroke-width="4"/><g mask="url(#cut)"><g transform="translate(40 40) rotate(-45) scale(1.45) translate(-43 -43)" stroke-width="2.8"><rect x="39" y="31" width="8" height="14" rx="4"/><path d="M34 39v3a9 9 0 0 0 18 0v-3M43 51v5M39 56h8"/></g></g><path d="M22.6 57.4L57.4 22.6" stroke-width="4.35"/></g></svg>
|
||||
|
After Width: | Height: | Size: 688 B |
+19
-115
@@ -1,71 +1,41 @@
|
||||
# Configuration reference
|
||||
# Advanced configuration reference
|
||||
|
||||
For the configuration window, read [Settings by tab](SETTINGS.md). This reference
|
||||
covers terminal configuration, advanced options, and diagnostics.
|
||||
For the GUI, read [Settings by tab](SETTINGS.md). This page is for developers and
|
||||
agents editing `~/.config/frame-voice/env`. Use literal `KEY=VALUE` entries, not
|
||||
shell commands; keep configuration/key files mode 600. Restart dictation after
|
||||
editing the file.
|
||||
|
||||
You can also edit
|
||||
`~/.config/frame-voice/env`. Settings are literal `KEY=VALUE`, never shell commands.
|
||||
Restart `frame-voice` after changing them. Configuration and key files should be
|
||||
mode 600.
|
||||
|
||||
## Backend
|
||||
|
||||
First-run setup offers three choices:
|
||||
|
||||
| Choice | Use | What setup does |
|
||||
| --- | --- | --- |
|
||||
| Groq — Cloud | Fast online transcription | Requests your [Groq API key](https://console.groq.com/keys) |
|
||||
| Local — Fast | Offline transcription, favoring speed | Downloads and verifies `base.en` or multilingual `base` (~142 MiB) |
|
||||
| Local — Balanced | Offline transcription, more detail with more processing | Downloads and verifies `small.en` or multilingual `small` (~466 MiB) |
|
||||
|
||||
Local choices need no Groq key and work offline after the download. The Whisper
|
||||
engine is included. Existing verified models are reused. The key is saved only
|
||||
in your mode-600 config and is never printed. Updates preserve your current choice.
|
||||
Switch in the Transcription tab, or rerun the installer with `--backend groq`, `--backend local-fast` or
|
||||
`--backend local-balanced`. `--groq-key-file PATH` reads a key without placing it
|
||||
in command arguments. Local English presets select `VOICE_LANG=en`.
|
||||
|
||||
The searchable Language menu offers automatic detection and 99 named languages.
|
||||
English uses the `.en` local model; other languages and automatic detection use
|
||||
multilingual models. Set `--language CODE` during installation or `VOICE_LANG`
|
||||
in configuration (`en`, `es`, `fr`, `ja`, etc.; `auto` for detection). Groq omits
|
||||
its optional language parameter for automatic detection; whisper-cli receives
|
||||
`-l auto`. Cleanup is instructed to preserve the original language and script.
|
||||
[Groq language parameters](https://console.groq.com/docs/speech-to-text) ·
|
||||
[Whisper model variants and checksums](https://github.com/ggml-org/whisper.cpp/blob/v1.9.4/models/README.md).
|
||||
|
||||
Non-ASCII transcripts use clipboard paste automatically in standalone apps with
|
||||
a known window class. Nested Desktop's classless target still cannot safely
|
||||
receive Unicode through keyboard injection; that path refuses Unicode instead
|
||||
of typing corrupted text. Spoken action aliases remain English; A/B and D-pad
|
||||
controls work independently of transcription language. Models for both variants
|
||||
appear separately in Downloaded models and can be deleted when unused.
|
||||
|
||||
Configuration loads from the compatible `~/.config/voice-prompt/env`, then
|
||||
Values load from the compatible `~/.config/voice-prompt/env`, then
|
||||
`~/.config/frame-voice/env`, then process environment; later values win.
|
||||
|
||||
## Controls
|
||||
|
||||
Tap a joystick cap, release, then touch it again and hold to record. Release ends
|
||||
recording. A plain hold does nothing. Click the stick during recording to cancel.
|
||||
The blue microphone grows during startup; the spinning gear indicates processing;
|
||||
In the headset, the blue microphone grows during startup; the spinning gear indicates processing;
|
||||
the red crossed-out mic indicates cancellation or insufficient speech.
|
||||
|
||||
After successful dictation, A submits and B clears after the button is released; the left D-pad right/left
|
||||
After successful dictation, A submits and X clears after the button is released; the left D-pad right/left
|
||||
provide submit/clear. These actions expire after ten seconds or a focus change.
|
||||
Unknown targets refuse clear rather than guessing. Speech commands match exact
|
||||
whole utterances: "submit", "send", "enter"; "scratch that", "clear", "undo";
|
||||
and "cancel" or "never mind". Ordinary phrases remain text.
|
||||
|
||||
|
||||
The Desktop tray is separate: white microphone when running, white crossed-out
|
||||
microphone when disabled.
|
||||
## Common settings
|
||||
|
||||
| Setting | Default | Purpose |
|
||||
| --- | --- | --- |
|
||||
| `VOICE_BACKEND` | `groq` | `groq` or `local`; setup selects the local model |
|
||||
| `VOICE_LANG` | `en` | Language code or `auto` for automatic detection |
|
||||
| `VOICE_MODEL` | Set by local setup | Verified local model file selected by the installer |
|
||||
| `VOICE_INJECT` | `type` | `type` or clipboard `paste` |
|
||||
| `VOICE_PASTE_CHUNK` | `0` | Maximum characters per paste; 0 disables splitting |
|
||||
| `VOICE_TRAILING` | `space` | Separator after dictation (`space` or `none`) |
|
||||
| `VOICE_BEEP` | `1` | Feedback tones |
|
||||
| `VOICE_NOTIFY` | `1` | Headset badge and usage warnings |
|
||||
| `VOICE_NOTIFY` | `1` | Headset microphone badges |
|
||||
| `VOICE_CLEANUP` | `1` | Optional Groq text cleanup |
|
||||
| `VOICE_SLASH` | `0` | Optional interpretation of spoken "slash" as `/` during cleanup |
|
||||
| `GROQ_USAGE_NOTIFY` | `1` | Groq usage/limit notifications; separate from microphone badges |
|
||||
@@ -87,74 +57,8 @@ Per-hand mappings: `VRBTN_DICTATE_LEFT/RIGHT`, `VRBTN_SUBMIT_LEFT/RIGHT`,
|
||||
the controller binding on service restart. More settings and validation are in
|
||||
`src/config.rs` and the installer-generated config template.
|
||||
|
||||
## Groq prompt customization
|
||||
|
||||
**Edit cleanup prompt…** under Groq opens the built-in editor. Apply your draft,
|
||||
then use the shared Save settings button to write
|
||||
`~/.config/frame-voice/groq-prompt.txt`. Reset to default restores the shipped
|
||||
text in the editor; canceling discards the editor draft. The shipped template is
|
||||
[`assets/groq-prompt.txt`](../assets/groq-prompt.txt): neutral dictation cleanup,
|
||||
original-language preservation, and no invented content. It is loaded for each
|
||||
cleanup request, so edits apply to the next dictation. `GROQ_PROMPT_FILE` can
|
||||
override its location. An absent file uses the neutral embedded default.
|
||||
|
||||
Feedback keeps one PulseAudio-compatible speaker stream warm for the current
|
||||
dictation job, instead of opening a player per cue. It releases the stream when
|
||||
the job and final cue finish; the microphone is still gesture-controlled.
|
||||
The small activation badge waits for capture and speaker startup before growing.
|
||||
Its growth and start cue then share a clock, accounting for a cold first stream
|
||||
without adding a fixed delay to already-warm recordings. With tones disabled,
|
||||
only capture readiness is required.
|
||||
`PAPLAY_BIN` retains the external-player override/fallback behavior.
|
||||
|
||||
Silence detection combines an adaptive volume floor, cue-tone rejection, and
|
||||
WebRTC voice activity detection from SteamOS's existing
|
||||
`libwebrtc-audio-processing-2` library. Voice and volume evidence must agree on
|
||||
the same audio for `VOICE_MIN_SPEECH_MS`; a cue followed by unrelated noise cannot
|
||||
satisfy the two checks independently. No extra speech model is downloaded.
|
||||
If the optional system library is unavailable, the portable volume/cue gate remains
|
||||
active. `VRBTN_DEBUG=1` logs the gate decision and VAD result without transcripts.
|
||||
No ordinary phrase such as “Yeah,” “you,” or “Thank you” is blocked by text.
|
||||
Groq's [detailed response](https://console.groq.com/docs/speech-to-text) provides
|
||||
`avg_logprob` and `no_speech_prob`. After the local gate, Frame Voice cancels if
|
||||
every returned segment has a decoding score below `GROQ_ASR_MIN_LOGPROB` or a
|
||||
no-speech probability above `GROQ_ASR_MAX_NO_SPEECH`. These are diagnostic scores,
|
||||
not calibrated accuracy percentages. Missing scores retain the local audio gate.
|
||||
Debug logging includes numeric scores and decisions, never transcript text.
|
||||
|
||||
Desktop clear counts the trailing separator too. For right-hand B with the default
|
||||
Steam layout, it also counts the space B itself types into Desktop. Other bindings
|
||||
and spoken Clear do not add that character. Set `VRBTN_CLEAR_B_SPACE=0` if your
|
||||
custom Steam B mapping does not type a space. Its count belongs to the
|
||||
running daemon, so a service restart resets that history. Test clear using a
|
||||
fresh dictation after changing settings or restarting the service.
|
||||
|
||||
Spoken-slash conversion is an optional toggle and is off by default; personal
|
||||
preferences belong in user configuration. ASR hints are separate and remain empty
|
||||
by default (`VOICE_PROMPT_TEXT`). API keys, personalized prompt files, and model
|
||||
downloads are excluded from release packaging.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
```sh
|
||||
~/.local/bin/frame-voice --check
|
||||
systemctl --user status frame-voice frame-voice-tray-session
|
||||
journalctl --user -u frame-voice -n 50 --no-pager
|
||||
```
|
||||
|
||||
`--check` validates configuration and compiled OpenVR support, not credentials,
|
||||
network access or headset health. Focus changes cancel pending results; text
|
||||
already delivered cannot be recalled. Silence makes no transcription request.
|
||||
|
||||
## Version and updates
|
||||
|
||||
`frame-voice --version` and `--build-info` report the package version, source
|
||||
revision, dirty-source marker, and compiled OpenVR capability. Release metadata
|
||||
is trusted only when its source commit matches the binary. Unpackaged/local
|
||||
builds are shown as development versions.
|
||||
|
||||
Use General → Check for updates, or `frame-voice --check-updates --json`.
|
||||
Checks compare stable release versions; they neither select experimental releases
|
||||
nor downgrade newer versions. The GUI's Update action opens the transactional
|
||||
installer after settings are saved. `~/.local/share/frame-voice/update.sh --tui`
|
||||
provides the terminal path. No background polling or automatic update is enabled.
|
||||
Backend flags are documented in the [developer guide](DEVELOPMENT.md).
|
||||
For signal gating, prompt handling, and text delivery internals, see
|
||||
[design notes](DESIGN.md). Diagnostics are maintained in
|
||||
[troubleshooting](TROUBLESHOOTING.md#diagnostics).
|
||||
@@ -0,0 +1,70 @@
|
||||
# Frame Voice design notes
|
||||
|
||||
This is an implementation reference. End users should start with
|
||||
[settings](SETTINGS.md) or [troubleshooting](TROUBLESHOOTING.md).
|
||||
|
||||
## Capture and feedback
|
||||
|
||||
The accepted leading tap prearms microphone capture and a PulseAudio-compatible
|
||||
speaker stream. The activation badge waits for capture/output readiness before
|
||||
spending its growth interval. The start cue and animation use that clock, with a
|
||||
playback lead. Speaker writes provide pacing; an additional per-write sleep would
|
||||
cause underruns. The stream closes when the job and final cue finish.
|
||||
`PAPLAY_BIN` retains the external-player override/fallback.
|
||||
|
||||
Headset feedback uses a growing blue microphone, a processing gear, and a red
|
||||
crossed-out microphone for cancellation or insufficient speech. The Desktop tray
|
||||
uses separate white running/disabled vectors, with the Frame corner mark.
|
||||
|
||||
## Speech evidence and ASR confidence
|
||||
|
||||
The local gate combines DC-free energy, an adaptive ambient floor, cue-frequency
|
||||
rejection, and optional WebRTC VAD from SteamOS's
|
||||
`libwebrtc-audio-processing-2`. Volume and VAD must agree on the same audio run
|
||||
for `VOICE_MIN_SPEECH_MS`. A cue followed by unrelated noise cannot supply the
|
||||
two kinds of evidence independently. No additional VAD model is downloaded.
|
||||
Without the optional system library, the portable energy/cue gate remains.
|
||||
|
||||
Groq's [verbose response](https://console.groq.com/docs/speech-to-text) exposes
|
||||
`avg_logprob` and `no_speech_prob`. A result is cancelled when every segment
|
||||
reports a decoding score below `GROQ_ASR_MIN_LOGPROB` or no-speech probability
|
||||
above `GROQ_ASR_MAX_NO_SPEECH`. These scores are not calibrated accuracy
|
||||
percentages. Missing metadata retains the local audio gate. Debug logs include
|
||||
numeric scores and decisions, never transcripts or keys.
|
||||
|
||||
No ordinary phrase, including “Yeah,” “you,” or “Thank you,” is banned by text.
|
||||
Detection and transcription can still mistake environmental sounds for voice.
|
||||
|
||||
## Prompt handling
|
||||
|
||||
The managed custom prompt is `~/.config/frame-voice/groq-prompt.txt`; the shipped
|
||||
neutral template is [assets/groq-prompt.txt](../assets/groq-prompt.txt).
|
||||
`GROQ_PROMPT_FILE` can override its location. An absent file uses the embedded
|
||||
neutral default. Cleanup loads the prompt for each request and preserves the
|
||||
original language/script. ASR hints are separate (`VOICE_PROMPT_TEXT`).
|
||||
User keys, custom prompts, and model downloads are excluded from releases.
|
||||
|
||||
## Context and delivery
|
||||
|
||||
Context observes the outer gamescope/X11 app focus. The built-in Desktop is a
|
||||
nested Plasma session; an inner app/field change may not change that outer
|
||||
context. ASCII text follows keyboard focus through uinput. Unicode uses a
|
||||
clipboard helper for supported standalone apps with a known window class;
|
||||
unsupported Desktop Unicode is refused rather than corrupted.
|
||||
|
||||
Desktop Clear backspaces the tracked insertion, including its separator.
|
||||
Default right X adds no controller character. If right B is explicitly selected,
|
||||
optional native-space accounting is scoped to its action origin and Desktop.
|
||||
Controller Clear waits for release and a short settle interval. Changed context,
|
||||
unavailable input, and expired pending actions cancel it.
|
||||
|
||||
The count belongs to the current process and resets at restart. Cursor movement
|
||||
or unobserved inner-field edits can invalidate ownership. It is not a persistent
|
||||
text-selection or accessibility model.
|
||||
|
||||
## Installer architecture
|
||||
|
||||
See [installer internals](INSTALLER.md) for transactional replacement, input
|
||||
permissions, helper migration, and rollback. Packaging uses an explicit allowlist
|
||||
and records checksums/source commit; release versions are documented in
|
||||
[distribution](DISTRIBUTION.md).
|
||||
@@ -0,0 +1,67 @@
|
||||
# Developer and agent guide
|
||||
|
||||
End-user setup is in the [README](../README.md). GUI settings and common failures
|
||||
are covered by [Settings](SETTINGS.md) and [Troubleshooting](TROUBLESHOOTING.md).
|
||||
|
||||
## Guided and automated installation
|
||||
|
||||
The default downloader selects the GUI when Desktop is open, including a
|
||||
Desktop session discovered from SSH. Without Desktop it opens the terminal menu.
|
||||
Use `--tui` to explicitly select a terminal menu. Explicit backend flags use the
|
||||
scripted path; `--non-interactive` forbids prompts.
|
||||
|
||||
From an extracted bundle or native build:
|
||||
|
||||
```sh
|
||||
./install-gui.sh
|
||||
./install.sh ./bin/frame-voice --tray-binary ./bin/frame-voice-tray --gui
|
||||
./install.sh ./bin/frame-voice --tray-binary ./bin/frame-voice-tray --tui
|
||||
./install.sh ./bin/frame-voice --tray-binary ./bin/frame-voice-tray \
|
||||
--non-interactive --backend local-fast --language auto
|
||||
```
|
||||
|
||||
For **Groq · Cloud**, use `--backend groq --language en --groq-key-file /path/to/key`.
|
||||
The local flags are `local-fast` (**Local · Fast**) and `local-balanced`
|
||||
(**Local · Balanced**). Configure input permissions interactively before unattended
|
||||
setup. Noninteractive mode never opens administrator/key-selection dialogs.
|
||||
Models need their initial download; verified cached files are reused.
|
||||
|
||||
## Version and updates
|
||||
|
||||
```sh
|
||||
frame-voice --version
|
||||
frame-voice --check-updates --json
|
||||
~/.local/share/frame-voice/update.sh --tui
|
||||
```
|
||||
|
||||
Build info includes package version, source revision, dirty marker, and compiled
|
||||
capability. Installed release metadata is trusted only when its commit matches
|
||||
the binary. Unpackaged builds are displayed as development versions.
|
||||
|
||||
## Terminal rollback and removal
|
||||
|
||||
From a checkout or extracted release:
|
||||
|
||||
```sh
|
||||
./rollback.sh
|
||||
./uninstall.sh
|
||||
./uninstall.sh --remove-config --remove-models
|
||||
```
|
||||
|
||||
Removal keeps configuration/models by default; `--remove-config` and
|
||||
`--remove-models` control them separately. GUI removal is documented in
|
||||
[Uninstall](UNINSTALL.md). Rollback restores the last managed snapshot and service
|
||||
state, preserving files modified after installation.
|
||||
|
||||
## Build and architecture references
|
||||
|
||||
- [Installer internals and legacy migration](INSTALLER.md)
|
||||
- [Advanced configuration variables](CONFIGURATION.md)
|
||||
- [Capture, speech gating, prompts, and delivery](DESIGN.md)
|
||||
- [ARM64 builds and release channels](DISTRIBUTION.md)
|
||||
- [Canonical diagnostic commands](TROUBLESHOOTING.md#diagnostics)
|
||||
|
||||
Use current stable Rust and native ARM64 Linux with C/C++, CMake, pkg-config,
|
||||
libpulse headers, scdoc, and the pinned OpenVR headers. `./build-local.sh` builds
|
||||
the application/tray. `./tools/build-runtime.sh` builds bundled helpers from pinned
|
||||
sources under `.build/`. The distribution workflow creates a glibc bundle.
|
||||
+4
-123
@@ -1,124 +1,5 @@
|
||||
# Installation
|
||||
# Installation documentation
|
||||
|
||||
The release bundle includes Frame Voice, its tray and clipboard helper, ydotool/
|
||||
ydotoold, an audio-only FFmpeg build, the Whisper engine, and microphone setup.
|
||||
SteamOS supplies the SteamVR/PipeWire/ALSA runtime, Python, playback tools and
|
||||
Desktop dialogs. Users do not install compilers or fetch these helpers separately.
|
||||
|
||||
## First install
|
||||
|
||||
```sh
|
||||
curl -fsSL https://raw.githubusercontent.com/techieyann/frame-voice/main/get.sh | sh
|
||||
```
|
||||
|
||||
Setup offers [Groq — Cloud, Local — Fast or Local — Balanced](CONFIGURATION.md#backend).
|
||||
Groq requires a key from [Groq's console](https://console.groq.com/keys). Local
|
||||
choices download and verify their models automatically. Input-device permissions
|
||||
may require an administrator password once. Setup then starts dictation and the
|
||||
Desktop tray. Run the installer as your normal user, not with sudo.
|
||||
|
||||
### GUI, terminal, and automated setup
|
||||
|
||||
The installer selects the GUI when Desktop is available and a terminal menu
|
||||
otherwise. Explicit `--backend` flags keep the scripted setup path. For the new
|
||||
release, choose a frontend explicitly:
|
||||
|
||||
```sh
|
||||
./install-gui.sh
|
||||
./install.sh ./bin/frame-voice --tray-binary ./bin/frame-voice-tray --gui
|
||||
./install.sh ./bin/frame-voice --tray-binary ./bin/frame-voice-tray --tui
|
||||
./install.sh ./bin/frame-voice --tray-binary ./bin/frame-voice-tray \
|
||||
--non-interactive --backend local-fast --language auto
|
||||
```
|
||||
|
||||
The GUI and TUI offer transcription, language, general settings, and controller
|
||||
mappings. For cloud automation, use `--backend groq --language en
|
||||
--groq-key-file /path/to/key`. Noninteractive setup never opens selection/key
|
||||
dialogs or administrator prompts; configure input permissions interactively first.
|
||||
Canceling interactive setup restores the prior installation and service state.
|
||||
Models downloaded before a failed/canceled setup may remain cached.
|
||||
|
||||
These frontend and language options are included in stable v0.1.0 and later.
|
||||
|
||||
Installation verifies archive and file checksums, channel and architecture before
|
||||
replacement. Helpers live under `~/.local/share/frame-voice/bin`; existing shared
|
||||
ydotool/Whisper installations and their services are untouched. Updates preserve
|
||||
configuration and models, snapshot all managed files, and restore service state.
|
||||
New snapshots never copy an existing config containing your API key.
|
||||
|
||||
When upgrading the old voice setup, add `--replace-legacy-helpers` to stop and
|
||||
disable `ydotoold.service` and `voice-mixer.service` in favor of the bundled
|
||||
helpers. Use this only if other applications no longer need those services.
|
||||
Their files remain, and rollback or uninstall restores their previous service
|
||||
state. For an extracted release:
|
||||
|
||||
```sh
|
||||
./install.sh ./bin/frame-voice --tray-binary ./bin/frame-voice-tray --setup --enable-tray --replace-legacy-helpers
|
||||
```
|
||||
|
||||
The keyboard-only input service clears `DISPLAY` because ydotoold's optional
|
||||
[X11 setup](https://github.com/ReimuNotMoe/ydotool/blob/v1.0.4/Daemon/ydotoold.c#L333-L349)
|
||||
only adjusts mouse acceleration. This avoids xinput/Xwayland warnings without
|
||||
changing keyboard injection.
|
||||
|
||||
## Channels and noninteractive setup
|
||||
|
||||
```sh
|
||||
curl -fsSL https://raw.githubusercontent.com/techieyann/frame-voice/main/get.sh | sh -s -- --channel experimental
|
||||
```
|
||||
|
||||
For unattended use, add `--backend local-fast`, `--backend local-balanced`, or
|
||||
`--backend groq --groq-key-file /path/to/key`. Models must download on the first
|
||||
local installation; cached verified models need no connection. A terminal or
|
||||
Desktop dialog is required if no backend/key is supplied.
|
||||
|
||||
## Tray
|
||||
|
||||
Open Desktop for status, Pause/Resume, Restart, configuration and logs. The tray
|
||||
runs on Plasma's private bus; service controls use the outer user bus. Hiding the
|
||||
tray leaves dictation running. Configuration changes restart dictation.
|
||||
|
||||
## Rollback and removal
|
||||
|
||||
From a source checkout or extracted release directory:
|
||||
|
||||
```sh
|
||||
./rollback.sh
|
||||
./uninstall.sh
|
||||
# To also remove Frame Voice settings and API keys:
|
||||
./uninstall.sh --remove-config
|
||||
# Also remove downloaded models (a full removal):
|
||||
./uninstall.sh --remove-config --remove-models
|
||||
```
|
||||
|
||||
Rollback restores the complete last snapshot and prior service state. Modified
|
||||
managed files are not overwritten. Uninstall removes application files, units,
|
||||
app data, and installation snapshots. Configuration/keys and model downloads are
|
||||
retained by default, controlled independently by `--remove-config` and
|
||||
`--remove-models`. Models downloaded by this tool are under
|
||||
`~/.local/share/frame-voice/models`; older shared Whisper models are not deleted.
|
||||
|
||||
The tray's **Configure… → General → Uninstall…** action provides confirmation and two retention
|
||||
checkboxes, both on by default. Unchecking both also removes
|
||||
`~/.config/frame-voice`, downloaded models, and this installer's input permission
|
||||
and module-loading rules if present. Removing administrator files requires
|
||||
administrator authentication; cancellation leaves the app installed. Modified
|
||||
administrator rules require manual removal. Older voice tools and their files
|
||||
remain untouched.
|
||||
|
||||
## Building
|
||||
|
||||
Use the native ARM64 [distribution workflow](DISTRIBUTION.md) for a complete
|
||||
bundle. Development builds need current stable Rust, C/C++, CMake, pkg-config,
|
||||
libpulse headers, scdoc and matching OpenVR headers. The workflow checks out
|
||||
pinned helper sources under `.build/`; run `./build-local.sh` and
|
||||
`./tools/build-runtime.sh`, then `./install.sh --setup`. Production builds use
|
||||
glibc. The installer can read built helpers from `.build/runtime/bin`.
|
||||
|
||||
## Diagnostics
|
||||
|
||||
```sh
|
||||
~/.local/bin/frame-voice --check
|
||||
systemctl --user status frame-voice frame-voice-input frame-voice-mixer frame-voice-tray-session
|
||||
journalctl --user -u frame-voice -n 50 --no-pager
|
||||
```
|
||||
For user installation, see the [README](../README.md#install-on-your-frame).
|
||||
For removal, see [Uninstall Frame Voice](UNINSTALL.md).
|
||||
Developer details moved to [Installer internals](INSTALLER.md).
|
||||
@@ -0,0 +1,65 @@
|
||||
# Installer internals
|
||||
|
||||
The release bundle includes Frame Voice, its tray and clipboard helper, ydotool/
|
||||
ydotoold, an audio-only FFmpeg build, the Whisper engine, and microphone setup.
|
||||
SteamOS supplies the SteamVR/PipeWire/ALSA runtime, Python, playback tools and
|
||||
Desktop dialogs. Users do not install compilers or fetch these helpers separately.
|
||||
|
||||
## Entry points
|
||||
|
||||
User installation is documented in the [README](../README.md); GUI/terminal/agent
|
||||
entry points are in [Development](DEVELOPMENT.md). The default downloader opens
|
||||
the GUI when Desktop is available and the terminal menu otherwise. Explicit
|
||||
backend flags retain the scripted setup path.
|
||||
|
||||
Canceling interactive setup restores the previous installation/service state.
|
||||
Models downloaded before a failed/canceled setup may remain cached.
|
||||
|
||||
Installation verifies archive and file checksums, channel and architecture before
|
||||
replacement. Helpers live under `~/.local/share/frame-voice/bin`; existing shared
|
||||
ydotool/Whisper installations and their services are untouched. Updates preserve
|
||||
configuration and models, snapshot all managed files, and restore service state.
|
||||
New snapshots never copy an existing config containing your API key.
|
||||
|
||||
When upgrading the old voice setup, add `--replace-legacy-helpers` to stop and
|
||||
disable `ydotoold.service` and `voice-mixer.service` in favor of the bundled
|
||||
helpers. Use this only if other applications no longer need those services.
|
||||
Their files remain, and rollback or uninstall restores their previous service
|
||||
state. For an extracted release:
|
||||
|
||||
```sh
|
||||
./install.sh ./bin/frame-voice --tray-binary ./bin/frame-voice-tray --setup --enable-tray --replace-legacy-helpers
|
||||
```
|
||||
|
||||
The keyboard-only input service clears `DISPLAY` because ydotoold's optional
|
||||
[X11 setup](https://github.com/ReimuNotMoe/ydotool/blob/v1.0.4/Daemon/ydotoold.c#L333-L349)
|
||||
only adjusts mouse acceleration. This avoids xinput/Xwayland warnings without
|
||||
changing keyboard injection.
|
||||
|
||||
## Tray
|
||||
|
||||
Open Desktop for status, Pause/Resume, Restart, configuration and logs. The tray
|
||||
runs on Plasma's private bus; service controls use the outer user bus. Hiding the
|
||||
tray leaves dictation running. Configuration changes restart dictation.
|
||||
|
||||
## Rollback and removal
|
||||
|
||||
User-facing retention choices are in [Uninstall](UNINSTALL.md); terminal flags
|
||||
are in [Development](DEVELOPMENT.md#terminal-rollback-and-removal).
|
||||
Rollback restores the last snapshot and previous service state while preserving
|
||||
modified files. Removal deletes managed application data, units, and snapshots.
|
||||
Configuration and managed model downloads are independently retained by default.
|
||||
Older/shared tools and their model directories are not removed.
|
||||
|
||||
## Building
|
||||
|
||||
Use the native ARM64 [distribution workflow](DISTRIBUTION.md) for a complete
|
||||
bundle. Development builds need current stable Rust, C/C++, CMake, pkg-config,
|
||||
libpulse headers, scdoc and matching OpenVR headers. The workflow checks out
|
||||
pinned helper sources under `.build/`; run `./build-local.sh` and
|
||||
`./tools/build-runtime.sh`, then `./install.sh --setup`. Production builds use
|
||||
glibc. The installer can read built helpers from `.build/runtime/bin`.
|
||||
|
||||
## Diagnostics
|
||||
|
||||
Use the [canonical diagnostic commands](TROUBLESHOOTING.md#diagnostics).
|
||||
@@ -0,0 +1,11 @@
|
||||
# Release notes
|
||||
|
||||
Current defaults are **right X** for Clear and a **white tray microphone**
|
||||
(white crossed-out microphone when disabled). Blue/red headset feedback is
|
||||
separate from tray status.
|
||||
|
||||
- [v0.1.1: white tray icons and X Clear default](releases/v0.1.1.md)
|
||||
- [v0.1.0: initial stable release](releases/v0.1.0.md)
|
||||
|
||||
Installers and update checks select the latest stable release. Existing saved
|
||||
button choices are preserved; use Controllers to change an older B binding to X.
|
||||
+16
-13
@@ -16,7 +16,7 @@ settings and restarts dictation, so finish any recording first.
|
||||
| **Separator after dictation** | Space keeps consecutive dictations from running together. None adds no separator. |
|
||||
| **Maximum recording seconds** | Limits each recording; the default is 30 seconds. |
|
||||
|
||||
The tray shows a blue microphone while dictation runs and a red crossed-out
|
||||
The tray shows a white microphone while dictation runs and a white crossed-out
|
||||
microphone when paused. Use the tray's start/resume or pause options to control it.
|
||||
In the headset, the small microphone waits for audio startup, then grows toward
|
||||
the start tone. Wait for the tone before speaking. Release the joystick cap to
|
||||
@@ -53,11 +53,13 @@ installed voice tools and their models are left alone.
|
||||
|
||||
### Transcription path
|
||||
|
||||
| Choice | What you need | What to expect |
|
||||
| --- | --- | --- |
|
||||
| **Groq · Cloud** | Internet and a [Groq API key](https://console.groq.com/keys). | Speech is sent to Groq; no local model download is needed. |
|
||||
| **Local · Fast** | About 142 MiB for a model download. | Runs on your Frame and favors speed. |
|
||||
| **Local · Balanced** | About 466 MiB for a model download. | Runs on your Frame and favors accuracy over speed. |
|
||||
| Model | Runs on | Speed | Accuracy | Internet needed | Requirements |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| Whisper Base | Local | Medium | Iffy | Initial download only | ~142MiB |
|
||||
| Whisper Small | Local | Slow | Good | Initial download only | ~466MiB |
|
||||
| Whisper Large v3 Turbo | Groq | Fast | Best | Every dictation | [API key](https://console.groq.com/keys) |
|
||||
|
||||
Whisper models were created by [OpenAI](https://github.com/openai/whisper). Local transcription sends no audio to OpenAI or Groq; the models run on your Frame using [whisper.cpp](https://github.com/ggml-org/whisper.cpp).
|
||||
|
||||
Select a path, then **Save settings**. Local transcription works offline after
|
||||
its initial download. Switching to local retains your saved Groq API key so you
|
||||
@@ -74,7 +76,7 @@ other languages and automatic detection use a multilingual model. Changing this
|
||||
selection may require another download.
|
||||
|
||||
For accented characters and non-Latin scripts, use a clipboard-compatible
|
||||
standalone app such as Firefox. Nested Desktop typing currently supports ASCII.
|
||||
standalone app such as Firefox. Typing inside Desktop currently supports ASCII.
|
||||
Controller buttons work independently of the transcription language; spoken
|
||||
submit/clear command aliases remain English.
|
||||
|
||||
@@ -121,15 +123,16 @@ Choose **None** to disable a particular action. Save to apply new bindings.
|
||||
| **Either joystick cap** | Touch briefly, release, then touch and hold to record. |
|
||||
| **Joystick click during recording** | Cancel the recording. |
|
||||
| **Right A / left D-pad Right** | Submit after text appears. |
|
||||
| **Right B / left D-pad Left** | Clear after text appears, when the button is released. |
|
||||
| **Right X / left D-pad Left** | Clear after text appears, when the button is released. |
|
||||
|
||||
Submit and clear are available for ten seconds after successful dictation and
|
||||
stop when focus changes. A plain joystick hold does not start recording.
|
||||
|
||||
### B's Desktop space
|
||||
|
||||
In Steam's default Desktop layout, B also types a space. Leave **B also types a
|
||||
space in Desktop** enabled to include that extra character when clearing.
|
||||
The default Clear button is X, which does not type an extra character. If you
|
||||
choose B instead, Steam's default Desktop layout also types a space. Leave
|
||||
**B also types a space in Desktop** enabled to include that extra character when clearing.
|
||||
Disable it if your custom Steam B mapping does not type a space. This adjustment
|
||||
applies to right-hand B in Desktop; spoken Clear and other bindings keep their
|
||||
own behavior.
|
||||
@@ -141,6 +144,6 @@ editing the text yourself can also invalidate the tracked count.
|
||||
|
||||
## More detail
|
||||
|
||||
See [installation and removal](INSTALLATION.md) for installer and rollback
|
||||
instructions, or the [configuration reference](CONFIGURATION.md) for terminal
|
||||
commands, advanced settings, and troubleshooting.
|
||||
See [troubleshooting](TROUBLESHOOTING.md) if dictation is not working, or
|
||||
[uninstalling Frame Voice](UNINSTALL.md) for removal and retention choices.
|
||||
Advanced configuration belongs in the [developer guide](DEVELOPMENT.md).
|
||||
@@ -0,0 +1,78 @@
|
||||
# Troubleshooting
|
||||
|
||||
Start in Desktop and focus an app's text field. A new Firefox tab's address bar
|
||||
is a useful first test.
|
||||
|
||||
## No microphone badge or tone
|
||||
|
||||
1. Check the tray: a white microphone means running; a white crossed-out
|
||||
microphone means disabled. Choose **Start / resume dictation** if needed.
|
||||
2. Touch the joystick cap briefly, release, then touch it again and hold.
|
||||
A plain hold does not start recording.
|
||||
3. Wait for the growing **blue headset badge** and start tone before speaking.
|
||||
If tones are disabled in General, the badge still indicates readiness.
|
||||
4. Try **Restart dictation** in the tray, then repeat in a focused text field.
|
||||
|
||||
Frame Voice does not dictate into Steam menus or VR games.
|
||||
|
||||
## Groq produces no text
|
||||
|
||||
Check internet access and the key under Transcription → Groq. The
|
||||
[API-key page](https://console.groq.com/keys) lets you create a key. Check Groq's
|
||||
usage/limit notifications and **View recent logs** for failed requests.
|
||||
Quiet or uncertain recordings can cancel instead of inserting text. Try a clear
|
||||
sentence in a quieter room. Environmental sounds can still be misidentified.
|
||||
|
||||
## Local setup fails
|
||||
|
||||
Check free storage and the internet connection needed for the first download.
|
||||
Setup verifies downloads and retains the current transcription path if
|
||||
preparation fails. Reopen Configure and try saving again.
|
||||
Delete unused models in Transcription to recover storage; first switch and save
|
||||
another path to unlock deletion of an active model.
|
||||
|
||||
## Clear leaves text or removes the wrong text
|
||||
|
||||
Default Clear is **right X** or **left D-pad Left**, after text appears. Release
|
||||
the button to clear. Updates preserve existing button choices; use Controllers
|
||||
to switch an older B binding to X.
|
||||
|
||||
Start with an empty field and use a fresh dictation after an update, settings
|
||||
save, or service restart. Desktop tracks text inserted by the running process;
|
||||
restarting resets that history. Cursor movement and manual edits can invalidate
|
||||
that count. If using B, see [B's Desktop space](SETTINGS.md#bs-desktop-space).
|
||||
|
||||
## Accented or non-Latin text does not appear
|
||||
|
||||
Use a standalone app launched directly from Steam. Typing inside Desktop
|
||||
currently supports ASCII; supported standalone apps use clipboard paste for
|
||||
other scripts.
|
||||
|
||||
## Update check cannot find a release
|
||||
|
||||
Check internet access and try again. **Open releases** shows the release page.
|
||||
The automatic check reads public stable releases; a private repository or an
|
||||
unpublished release will not appear. Experimental versions are not selected.
|
||||
|
||||
## Diagnostics
|
||||
|
||||
First use **View recent logs** in the tray. If you need to inspect services,
|
||||
open Konsole and run:
|
||||
|
||||
```sh
|
||||
~/.local/bin/frame-voice --check
|
||||
systemctl --user status frame-voice frame-voice-input frame-voice-mixer frame-voice-tray-session
|
||||
journalctl --user -u frame-voice -n 50 --no-pager
|
||||
```
|
||||
|
||||
The four services handle dictation, keyboard input, microphone setup, and the
|
||||
Desktop tray. `--check` validates settings and compiled support; it does not test
|
||||
your API key, internet connection, or headset health.
|
||||
|
||||
When reporting a problem, include the app/text field, transcription option,
|
||||
selected language, installed version, and relevant errors. Do not include API keys.
|
||||
|
||||
## Remove Frame Voice
|
||||
|
||||
See [uninstall instructions](UNINSTALL.md). You can retain settings and models
|
||||
independently, or remove both.
|
||||
@@ -0,0 +1,20 @@
|
||||
# Uninstall Frame Voice
|
||||
|
||||
1. Open the tray icon and choose **Configure…**.
|
||||
2. Select **General** and click **Uninstall…** at the bottom.
|
||||
3. Choose what to retain in the confirmation, then confirm uninstall.
|
||||
|
||||
| Option | Keep checked to retain | Uncheck to remove |
|
||||
| --- | --- | --- |
|
||||
| **Keep current configuration** | Settings, saved API keys, and the managed custom prompt. | Those saved values and files. |
|
||||
| **Keep downloaded models** | Models downloaded by Frame Voice. | Those model downloads. |
|
||||
|
||||
Both options are checked initially. Uncheck both for a full removal of Frame
|
||||
Voice's settings, keys, models, app data, and installation backups.
|
||||
Separately installed voice tools and their models are not removed.
|
||||
Removing administrator-installed input rules can require your SteamOS password.
|
||||
Cancelling authentication leaves the app installed.
|
||||
|
||||
To recover model space while keeping the app, see
|
||||
[Downloaded models](SETTINGS.md#downloaded-models-and-storage).
|
||||
For terminal removal or rollback, use the [developer guide](DEVELOPMENT.md).
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
@@ -1,5 +1,8 @@
|
||||
# Frame Voice v0.1.0
|
||||
|
||||
> These notes describe v0.1.0. Current versions default to X for Clear and white
|
||||
> tray icons; see [v0.1.1](v0.1.1.md). Existing saved bindings are preserved.
|
||||
|
||||
First stable release of controller-driven dictation for Steam Frame.
|
||||
|
||||
- Tap, release, then hold a joystick cap to dictate into Desktop or standalone apps. Click during recording to cancel; use A to submit and release B to clear.
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
# Frame Voice v0.1.1
|
||||
|
||||
- The tray uses a white microphone with the Frame corner mark. Disabled dictation uses the same white icon with its existing cross. Headset recording/cancellation colours are unchanged.
|
||||
- New installations use right-controller **X** for Clear, matching left D-pad Left. This avoids B's native Desktop space. Existing configured button choices are retained; select X in Controllers to change them.
|
||||
- Default X clears exactly the tracked dictation and its separator, without an extra controller Backspace. Optional native-space handling applies only when B is explicitly selected.
|
||||
- User docs now separate settings, troubleshooting, removal, and developer references, with a configuration screenshot and a concise transcription comparison.
|
||||
|
||||
Updates preserve saved settings, API keys, and downloaded models. Open Configure → General → Check for updates.
|
||||
+14
-4
@@ -95,7 +95,7 @@ struct PendingClear {
|
||||
fn controller_clear_chars(c: &Config, hand: Option<usize>, target: &Snapshot) -> usize {
|
||||
usize::from(
|
||||
hand == Some(1)
|
||||
&& c.get("VRBTN_CLEAR_RIGHT", "b")
|
||||
&& c.get("VRBTN_CLEAR_RIGHT", "x")
|
||||
.trim()
|
||||
.eq_ignore_ascii_case("b")
|
||||
&& c.boolean("VRBTN_CLEAR_B_SPACE", true).unwrap_or(true)
|
||||
@@ -263,7 +263,7 @@ fn write_controller_binding(c: &Config, manifest: &str) -> Result<()> {
|
||||
if let Some(name) = pick("VRBTN_SUBMIT_RIGHT", "a", &right_buttons) {
|
||||
sources.push(button("right", &name, "/actions/voicedict/in/submit"));
|
||||
}
|
||||
if let Some(name) = pick("VRBTN_CLEAR_RIGHT", "b", &right_buttons) {
|
||||
if let Some(name) = pick("VRBTN_CLEAR_RIGHT", "x", &right_buttons) {
|
||||
sources.push(button("right", &name, "/actions/voicedict/in/clear"));
|
||||
}
|
||||
let doc = serde_json::json!({
|
||||
@@ -391,7 +391,7 @@ fn daemon(c: Config) -> Result<()> {
|
||||
thread::sleep(Duration::from_millis(500));
|
||||
}
|
||||
eprintln!("OpenVR controller bindings active");
|
||||
eprintln!("frame-voice ready: cap tap then hold; A submit; B clear");
|
||||
eprintln!("frame-voice ready: cap tap then hold; A submit; X clear");
|
||||
while !SHUTDOWN.load(Ordering::SeqCst) {
|
||||
retired.retain(|j| !j.worker.is_finished());
|
||||
// Deliver usage warnings through OpenVR so they appear in the headset /
|
||||
@@ -713,7 +713,15 @@ fn daemon(c: Config) -> Result<()> {
|
||||
// The same B press reaches the Desktop too. Inject
|
||||
// only after release so its first Backspace isn't
|
||||
// consumed by controller/keyboard focus switching.
|
||||
let hand = vr.clear_hand();
|
||||
let hand = if c
|
||||
.get("VRBTN_CLEAR_RIGHT", "x")
|
||||
.trim()
|
||||
.eq_ignore_ascii_case("b")
|
||||
{
|
||||
vr.clear_hand()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let controller_chars = controller_clear_chars(&c, hand, &now);
|
||||
if debug {
|
||||
eprintln!("controller clear prepared: hand={hand:?}; native_chars={controller_chars}");
|
||||
@@ -1065,6 +1073,8 @@ mod tests {
|
||||
class: String::new(),
|
||||
}),
|
||||
};
|
||||
assert_eq!(controller_clear_chars(&config, Some(1), &target), 0);
|
||||
config.values.insert("VRBTN_CLEAR_RIGHT".into(), "b".into());
|
||||
assert_eq!(controller_clear_chars(&config, Some(1), &target), 1);
|
||||
assert_eq!(controller_clear_chars(&config, Some(0), &target), 0);
|
||||
assert_eq!(controller_clear_chars(&config, None, &target), 0);
|
||||
|
||||
+9
-1
@@ -236,10 +236,18 @@ fn desktop_clear_counts_the_appended_separator() {
|
||||
assert!(fs::read_to_string(&arguments)
|
||||
.unwrap()
|
||||
.ends_with("hello \n"));
|
||||
inject::send(&c, &Output::Clear, Some(&focus)).unwrap();
|
||||
inject::send_controller_clear_guarded(&c, Some(&focus), 0, || true).unwrap();
|
||||
let keys = fs::read_to_string(&arguments).unwrap();
|
||||
assert_eq!(keys.lines().filter(|&line| line == "14:1").count(), 6);
|
||||
assert_eq!(keys.lines().filter(|&line| line == "14:0").count(), 6);
|
||||
// With no separator, default X must remove exactly the five typed letters.
|
||||
c.values.insert("VOICE_TRAILING".into(), "none".into());
|
||||
inject::send(&c, &Output::Text("hello".into()), Some(&focus)).unwrap();
|
||||
inject::send_controller_clear_guarded(&c, Some(&focus), 0, || true).unwrap();
|
||||
let keys = fs::read_to_string(&arguments).unwrap();
|
||||
assert_eq!(keys.lines().filter(|&line| line == "14:1").count(), 5);
|
||||
assert_eq!(keys.lines().filter(|&line| line == "14:0").count(), 5);
|
||||
c.values.remove("VOICE_TRAILING");
|
||||
// Default Desktop B inserts an additional space before triggering Clear.
|
||||
inject::send(&c, &Output::Text("hello".into()), Some(&focus)).unwrap();
|
||||
inject::send_controller_clear_guarded(&c, Some(&focus), 1, || true).unwrap();
|
||||
|
||||
@@ -147,6 +147,10 @@ class ConfigApp(Adw.Application):
|
||||
for widget in self.fields:
|
||||
signal = 'notify::active' if widget.kind == 'bool' else 'notify::selected' if widget.kind == 'enum' else 'notify::text'
|
||||
widget.connect(signal, self.update_dirty)
|
||||
controls = {widget.key: widget for widget in self.fields}
|
||||
if 'VRBTN_CLEAR_RIGHT' in controls:
|
||||
controls['VRBTN_CLEAR_RIGHT'].connect('notify::selected', self.update_controller_options)
|
||||
self.update_controller_options()
|
||||
for widget in (self.backend_choice, self.language_choice):
|
||||
widget.connect('notify::selected', self.update_dirty)
|
||||
self.update_dirty()
|
||||
@@ -177,6 +181,13 @@ class ConfigApp(Adw.Application):
|
||||
self.fields.append(widget)
|
||||
return widget
|
||||
|
||||
def update_controller_options(self, *_):
|
||||
controls = {widget.key: widget for widget in self.fields}
|
||||
choice = controls.get('VRBTN_CLEAR_RIGHT')
|
||||
row = controls.get('VRBTN_CLEAR_B_SPACE')
|
||||
if choice is not None and row is not None:
|
||||
row.set_visible(choice.options[choice.get_selected()] == 'b')
|
||||
|
||||
def transcription_page(self, page):
|
||||
choices = Adw.PreferencesGroup(title='Transcription', description='Choose where your speech is transcribed.')
|
||||
self.backend_modes = ['groq', 'local-fast', 'local-balanced']
|
||||
|
||||
@@ -62,7 +62,7 @@ GROQ_API_KEY=
|
||||
# VOICE_WARN_COOLDOWN_SEC=3600
|
||||
#
|
||||
# == Local ASR (VOICE_BACKEND=local) ==
|
||||
# VOICE_MODEL=/home/steamos/.local/share/whisper/models/ggml-base.en.bin
|
||||
# VOICE_MODEL=~/.local/share/whisper/models/ggml-base.en.bin
|
||||
# WHISPER_BIN=whisper-cli
|
||||
# VOICE_THREADS=4
|
||||
'''
|
||||
@@ -143,8 +143,8 @@ def payload(project, binary, tray, include_tray, home):
|
||||
add(project / 'tools/config-gui.py', '.local/share/frame-voice/config-gui.py', 0o755)
|
||||
add(project / 'tools/config-gui-zenity.sh', '.local/share/frame-voice/config-gui-zenity.sh', 0o755)
|
||||
add(project / 'systemd/frame-voice-tray-session.service', '.config/systemd/user/frame-voice-tray-session.service')
|
||||
for name, state in [('microphone.svg', 'active'), ('cancelled-microphone.svg', 'stopped'),
|
||||
('gear.svg', 'processing'), ('cancelled-microphone.svg', 'failed')]:
|
||||
for name, state in [('tray-microphone.svg', 'active'), ('tray-muted-microphone.svg', 'stopped'),
|
||||
('tray-microphone.svg', 'processing'), ('tray-muted-microphone.svg', 'failed')]:
|
||||
add(project / 'assets/icons' / name, f'.local/share/frame-voice/tray-icons/frame-voice-{state}.svg')
|
||||
if not (home / CONFIG).exists():
|
||||
files[CONFIG] = (EXAMPLE, 0o600)
|
||||
|
||||
@@ -37,7 +37,7 @@ def files(project, binaries, tray, sdk_license, runtime=None):
|
||||
source['tools/' + name] = project / 'tools' / name
|
||||
for name in ('actions.json', 'frame_controller_binding.json', 'groq-prompt.txt'):
|
||||
source['assets/' + name] = project / 'assets' / name
|
||||
for name in ('microphone.svg', 'cancelled-microphone.svg', 'gear.svg'):
|
||||
for name in ('microphone.svg', 'cancelled-microphone.svg', 'gear.svg', 'tray-microphone.svg', 'tray-muted-microphone.svg'):
|
||||
source['assets/icons/' + name] = project / 'assets/icons' / name
|
||||
for name in ('frame-voice.service', 'frame-voice-tray-session.service', 'frame-voice-input.service', 'frame-voice-mixer.service'):
|
||||
source['systemd/' + name] = project / 'systemd' / name
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Developer helper: render existing tray vectors as embedded 64px ARGB pixmaps."""
|
||||
from pathlib import Path
|
||||
import gi
|
||||
|
||||
gi.require_version('GdkPixbuf', '2.0')
|
||||
from gi.repository import GdkPixbuf
|
||||
|
||||
root = Path(__file__).resolve().parent.parent/'assets/icons'
|
||||
for source, target in (('tray-microphone.svg', 'frame-voice-active.argb'),
|
||||
('tray-muted-microphone.svg', 'frame-voice-stopped.argb')):
|
||||
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(str(root/source), 64, 64)
|
||||
assert pixbuf.get_width() == pixbuf.get_height() == 64
|
||||
rgba = pixbuf.get_pixels()
|
||||
stride, channels = pixbuf.get_rowstride(), pixbuf.get_n_channels()
|
||||
pixels = bytearray()
|
||||
for y in range(64):
|
||||
for x in range(64):
|
||||
i = y*stride + x*channels
|
||||
r, g, b = rgba[i:i+3]
|
||||
a = rgba[i+3] if channels == 4 else 255
|
||||
pixels.extend((a, r, g, b))
|
||||
(root/target).write_bytes(pixels)
|
||||
@@ -134,7 +134,7 @@ SETTINGS = {
|
||||
'controllers': [
|
||||
('VRBTN_DICTATE_RIGHT', 'Right hand dictation', 'bool', None, True),
|
||||
('VRBTN_SUBMIT_RIGHT', 'Right submit button', 'enum', ['a', 'b', 'x', 'y', 'none'], 'a'),
|
||||
('VRBTN_CLEAR_RIGHT', 'Right clear button', 'enum', ['a', 'b', 'x', 'y', 'none'], 'b'),
|
||||
('VRBTN_CLEAR_RIGHT', 'Right clear button', 'enum', ['a', 'b', 'x', 'y', 'none'], 'x'),
|
||||
('VRBTN_CLEAR_B_SPACE', 'B also types a space in Desktop', 'bool', None, True),
|
||||
('VRBTN_DICTATE_LEFT', 'Left hand dictation', 'bool', None, True),
|
||||
('VRBTN_SUBMIT_LEFT', 'Left submit button', 'enum', ['dpad_up', 'dpad_down', 'dpad_left', 'dpad_right', 'none'], 'dpad_right'),
|
||||
|
||||
Generated
+1
-1
@@ -137,7 +137,7 @@ checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
|
||||
|
||||
[[package]]
|
||||
name = "frame-voice-tray"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"ksni",
|
||||
]
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "frame-voice-tray"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2021"
|
||||
rust-version = "1.85"
|
||||
description = "Nested SteamOS Desktop status and controls for Frame Voice"
|
||||
|
||||
+2
-3
@@ -64,9 +64,8 @@ mod desktop {
|
||||
}
|
||||
fn icon_pixmap(&self) -> Vec<ksni::Icon> {
|
||||
let data: &[u8] = match self.state.active.as_str() {
|
||||
"active" => include_bytes!("../../assets/icons/frame-voice-active.argb"),
|
||||
"activating" | "reloading" => {
|
||||
include_bytes!("../../assets/icons/frame-voice-processing.argb")
|
||||
"active" | "activating" | "reloading" => {
|
||||
include_bytes!("../../assets/icons/frame-voice-active.argb")
|
||||
}
|
||||
_ => include_bytes!("../../assets/icons/frame-voice-stopped.argb"),
|
||||
};
|
||||
|
||||
Reference in new issue
Block a user