python-voiceio 1.4.0__tar.gz → 1.6.0__tar.gz
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.
- python_voiceio-1.6.0/PKG-INFO +138 -0
- python_voiceio-1.6.0/README.md +85 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/pyproject.toml +1 -1
- python_voiceio-1.6.0/python_voiceio.egg-info/PKG-INFO +138 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/python_voiceio.egg-info/SOURCES.txt +5 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_clipboard_read.py +32 -0
- python_voiceio-1.6.0/tests/test_clipboard_typer.py +204 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_learn_pipeline.py +102 -0
- python_voiceio-1.6.0/tests/test_media.py +170 -0
- python_voiceio-1.6.0/tests/test_modifier_guard.py +204 -0
- python_voiceio-1.6.0/voiceio/__init__.py +1 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/app.py +8 -2
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/learn.py +32 -12
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/clipboard_read.py +33 -3
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/config.py +18 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/health.py +5 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/train.py +11 -1
- python_voiceio-1.6.0/voiceio/media.py +89 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/__init__.py +13 -1
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/base.py +25 -1
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/clipboard.py +107 -19
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/manager.py +7 -5
- python_voiceio-1.6.0/voiceio/typers/modifier_guard.py +97 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/pynput_type.py +4 -7
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/wtype.py +4 -7
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/xdotool.py +4 -7
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/ydotool.py +4 -7
- python_voiceio-1.4.0/PKG-INFO +0 -320
- python_voiceio-1.4.0/README.md +0 -267
- python_voiceio-1.4.0/python_voiceio.egg-info/PKG-INFO +0 -320
- python_voiceio-1.4.0/voiceio/__init__.py +0 -1
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/LICENSE +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/python_voiceio.egg-info/dependency_links.txt +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/python_voiceio.egg-info/entry_points.txt +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/python_voiceio.egg-info/requires.txt +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/python_voiceio.egg-info/top_level.txt +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/setup.cfg +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_app_wiring.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_audio_quality.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_audio_source.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_backend_probes.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_cli.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_commands.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_concurrency_lockdown.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_config.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_corrections.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_decoders.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_engine.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_evdev_uaccess.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_fallback.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_fcitx_bridge.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_health.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_hints.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_history.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_ibus_engine_limits.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_ibus_engine_listener.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_ibus_pending.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_ibus_pids.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_ibus_ping.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_ibus_session.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_ibus_textlimit.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_ibus_typer.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_learn_clean.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_learn_import.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_learn_metrics.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_learn_quiet.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_learn_review.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_learn_targets.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_llm.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_llm_api.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_numbers.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_pipeline.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_platform.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_postcorrect.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_postprocess.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_prebuffer.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_prompt.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_recorder_integration.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_retention.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_robustness.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_security_hardening.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_server.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_service.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_setup.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_streaming.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_suggest.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_tokens.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_transcriber.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_tts.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_typer_manager.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_vad.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_vocabulary.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_wordfreq.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/tests/test_worker_decode.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/__main__.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/audio_source.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/backends.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/__init__.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/configcmd.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/corrections.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/doctor.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/history.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/models.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/servicecmd.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/uninstall.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/cli/vocab.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/commands.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/consent.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/__init__.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/bias.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/decoders/__init__.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/decoders/base.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/decoders/catalog.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/decoders/faster_whisper/__init__.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/decoders/faster_whisper/worker.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/decoders/lanes.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/decoders/sherpa.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/decoders/whispercpp.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/engine.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/core/pipeline.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/corrections.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/data/60-voiceio-uaccess.rules +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/data/__init__.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/demo.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/display.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/feedback.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/hints.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/history.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/hotkeys/__init__.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/hotkeys/base.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/hotkeys/chain.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/hotkeys/evdev.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/hotkeys/pynput_backend.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/hotkeys/socket_backend.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/ibus/__init__.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/ibus/engine.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/ibus/install.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/ibus/pending.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/ibus/session.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/ibus/textlimit.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/__init__.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/augment.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/clean.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/evaluate.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/importer.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/metrics.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/quiet.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/review.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/store.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/learn/teacher.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/llm.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/llm_api.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/models/__init__.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/models/silero_vad.onnx +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/numbers.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/pidlock.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/platform.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/postcorrect.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/postprocess.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/privacy.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/prompt.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/recorder.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/retention.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/server/__init__.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/server/__main__.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/server/app.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/server/config.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/server/runtime.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/service.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/__init__.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/configfile.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/extras.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/hotkey.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/noninteractive.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/speech.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/steps.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/system.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/setup/ui.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/sounds/__init__.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/sounds/commit.wav +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/sounds/start.wav +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/sounds/stop.wav +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/streaming.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/suggest.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tokens.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tray/__init__.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tray/_icons.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tray/_indicator.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tray/_pystray.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tts/__init__.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tts/base.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tts/chain.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tts/edge_engine.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tts/espeak.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tts/piper_engine.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/tts/player.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/chain.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/collecting.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/fcitx_bridge.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/typers/ibus.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/ui.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/vad.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/vocab_stats.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/vocabulary.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/watchdog.py +0 -0
- {python_voiceio-1.4.0 → python_voiceio-1.6.0}/voiceio/wordfreq.py +0 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-voiceio
|
|
3
|
+
Version: 1.6.0
|
|
4
|
+
Summary: Voice dictation for Linux. Speak → text, locally, instantly.
|
|
5
|
+
Author: Hugo Montenegro
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://voiceio.dev/
|
|
8
|
+
Project-URL: Repository, https://github.com/Hugo0/voiceio
|
|
9
|
+
Project-URL: Issues, https://github.com/Hugo0/voiceio/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/Hugo0/voiceio/releases
|
|
11
|
+
Keywords: voice,speech-to-text,whisper,linux,dictation,wayland,ibus
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: X11 Applications
|
|
14
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: faster-whisper>=1.0.0
|
|
22
|
+
Requires-Dist: numpy>=1.24.0
|
|
23
|
+
Requires-Dist: onnxruntime>=1.16.0
|
|
24
|
+
Requires-Dist: wordfreq>=3.0
|
|
25
|
+
Provides-Extra: desktop
|
|
26
|
+
Requires-Dist: sounddevice>=0.4.6; extra == "desktop"
|
|
27
|
+
Requires-Dist: evdev>=1.6.0; sys_platform == "linux" and extra == "desktop"
|
|
28
|
+
Requires-Dist: pynput>=1.7.6; extra == "desktop"
|
|
29
|
+
Requires-Dist: pystray>=0.19; extra == "desktop"
|
|
30
|
+
Requires-Dist: Pillow>=10.0; extra == "desktop"
|
|
31
|
+
Provides-Extra: linux
|
|
32
|
+
Requires-Dist: sounddevice>=0.4.6; extra == "linux"
|
|
33
|
+
Requires-Dist: evdev>=1.6.0; sys_platform == "linux" and extra == "linux"
|
|
34
|
+
Requires-Dist: pynput>=1.7.6; extra == "linux"
|
|
35
|
+
Requires-Dist: pystray>=0.19; extra == "linux"
|
|
36
|
+
Requires-Dist: Pillow>=10.0; extra == "linux"
|
|
37
|
+
Provides-Extra: server
|
|
38
|
+
Requires-Dist: aiohttp>=3.9; extra == "server"
|
|
39
|
+
Provides-Extra: tts
|
|
40
|
+
Requires-Dist: piper-tts>=1.3.0; extra == "tts"
|
|
41
|
+
Requires-Dist: edge-tts>=6.1.0; extra == "tts"
|
|
42
|
+
Provides-Extra: train
|
|
43
|
+
Requires-Dist: torch>=2.2; extra == "train"
|
|
44
|
+
Requires-Dist: transformers>=4.40; extra == "train"
|
|
45
|
+
Requires-Dist: peft>=0.10; extra == "train"
|
|
46
|
+
Provides-Extra: sherpa
|
|
47
|
+
Requires-Dist: sherpa-onnx>=1.12; extra == "sherpa"
|
|
48
|
+
Provides-Extra: dev
|
|
49
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
50
|
+
Requires-Dist: pytest-mock; extra == "dev"
|
|
51
|
+
Requires-Dist: ruff<0.18,>=0.15; extra == "dev"
|
|
52
|
+
Dynamic: license-file
|
|
53
|
+
|
|
54
|
+
# voiceio
|
|
55
|
+
|
|
56
|
+
[](https://github.com/Hugo0/voiceio/actions/workflows/ci.yml)
|
|
57
|
+
[](https://pypi.org/project/python-voiceio/)
|
|
58
|
+
[](https://pypi.org/project/python-voiceio/)
|
|
59
|
+
[](LICENSE)
|
|
60
|
+
|
|
61
|
+
**Voice dictation for Linux that learns you.** Local, private, open source, and it fine-tunes itself on your own voice.
|
|
62
|
+
|
|
63
|
+
[](https://voiceio.dev)
|
|
64
|
+
|
|
65
|
+
**[Try it in your browser at voiceio.dev](https://voiceio.dev)**: no install, the model runs in the tab.
|
|
66
|
+
|
|
67
|
+
- **Types anywhere.** Press your hotkey, speak, and the words stream into the focused app with a live underlined preview (IBus), on Wayland and X11: GNOME, KDE, Hyprland, sway, i3.
|
|
68
|
+
- **Runs on your machine.** Whisper decodes your speech locally. No account, no server, no telemetry; logs never contain your words.
|
|
69
|
+
- **Learns your words.** Keep your recordings (opt-in, on your disk) and voiceio fine-tunes its model on them while the machine is idle. It switches only when the new model wins on clips it never trained on. On the author's voice: 12.1% → 4.9% word errors after one night on a laptop CPU.
|
|
70
|
+
|
|
71
|
+
## Install
|
|
72
|
+
|
|
73
|
+
**With a coding agent.** Paste this into Claude Code, Codex or any agent with a shell:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
Install voiceio on this machine following https://voiceio.dev/llms.txt. Ask me which hotkey I want and whether to keep my recordings on this computer so voiceio can learn my voice. Finish with voiceio doctor and fix whatever it reports.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**By hand:**
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
sudo apt install pipx build-essential python3-dev portaudio19-dev ibus gir1.2-ibus-1.0 python3-gi # Debian/Ubuntu
|
|
83
|
+
sudo dnf install pipx gcc gcc-c++ make python3-devel portaudio-devel ibus ibus-libs python3-gobject # Fedora
|
|
84
|
+
sudo pacman -S python-pipx base-devel portaudio ibus python-gobject # Arch
|
|
85
|
+
|
|
86
|
+
pipx install 'python-voiceio[desktop]'
|
|
87
|
+
voiceio setup # model, hotkey, autostart
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Then press your hotkey, speak, and press it again. `voiceio doctor` shows what works and `voiceio doctor --fix` repairs what it can. NixOS, other extras and installing from source: [docs/linux.md](docs/linux.md). Agent runbook: [INSTALL.md](INSTALL.md).
|
|
91
|
+
|
|
92
|
+
## Make it yours
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
voiceio vocab add Kalshi # a name, recognized from the next dictation
|
|
96
|
+
voiceio corrections add kalchi Kalshi # fix whatever still comes out wrong
|
|
97
|
+
voiceio learn schedule on # learn from your recordings while the machine is idle
|
|
98
|
+
voiceio learn status # what it learned and how each model scores
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Scheduled learning runs daily or weekly at a time you pick, only on an idle machine on mains power, with a notification you can stop. It never switches models unless the new one wins, and `voiceio learn rollback` undoes a switch. Details: [docs/learning.md](docs/learning.md).
|
|
102
|
+
|
|
103
|
+
## Configure
|
|
104
|
+
|
|
105
|
+
Everything lives in `~/.config/voiceio/config.toml`; [config.example.toml](config.example.toml) documents every option. `voiceio config set section.key value` edits it in place.
|
|
106
|
+
|
|
107
|
+
## Docs
|
|
108
|
+
|
|
109
|
+
| | |
|
|
110
|
+
|---|---|
|
|
111
|
+
| [Learning your voice](docs/learning.md) | Vocabulary, corrections, labeling, fine-tuning, evaluation, schedule |
|
|
112
|
+
| [Choosing a model](docs/models.md) | Whisper sizes, Parakeet, whisper.cpp servers, licenses |
|
|
113
|
+
| [Commands](docs/commands.md) | Every `voiceio` command |
|
|
114
|
+
| [Your data](docs/data.md) | What is stored where, and what is off by default |
|
|
115
|
+
| [Phone dictation](docs/phone.md) | Run voiceio headless on a home server |
|
|
116
|
+
| [Linux support and troubleshooting](docs/linux.md) | Desktops, backends, NixOS, fixes |
|
|
117
|
+
| [Comparisons](https://voiceio.dev/vs/) | voiceio vs Voxtype, Wispr Flow, Superwhisper, Dragon and others |
|
|
118
|
+
|
|
119
|
+
## Roadmap
|
|
120
|
+
|
|
121
|
+
- **Unbounded vocabulary**: a decoder whose biasing has no token cap (see [model choice](CONTRIBUTING.md#model-choice-why-whisper-and-what-would-change-it)).
|
|
122
|
+
- **GPU on AMD and Intel**: let setup run a Vulkan whisper.cpp server for you.
|
|
123
|
+
- **Typing without IBus on GNOME and KDE**: a libei backend.
|
|
124
|
+
- **Distro packages**: AUR, .deb and .rpm.
|
|
125
|
+
- **One model everywhere**: sync your fine-tune across your machines.
|
|
126
|
+
|
|
127
|
+
## Contributing
|
|
128
|
+
|
|
129
|
+
Open an issue before a large PR. [CONTRIBUTING.md](CONTRIBUTING.md) covers the architecture, conventions and the reasoning behind the model choice. Install reports and ideas are welcome on the [feedback board](https://voiceio.dev/#feedback).
|
|
130
|
+
|
|
131
|
+
## Credits
|
|
132
|
+
|
|
133
|
+
- Speech recognition: [faster-whisper](https://github.com/SYSTRAN/faster-whisper) and [CTranslate2](https://github.com/OpenNMT/CTranslate2), with OpenAI's [Whisper](https://github.com/openai/whisper) models. The in-browser demo runs [Moonshine](https://github.com/usefulsensors/moonshine) through [transformers.js](https://github.com/huggingface/transformers.js).
|
|
134
|
+
- Ideas borrowed with thanks: modifier release, paste keys and media pausing from [Voxtype](https://github.com/peteonrails/voxtype); the landing page's look from Romario Kavin's [KOLlateral](https://github.com/RomarioKavin1/kollateral).
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
MIT. Model weights are not part of voiceio and carry their own licenses (see [docs/models.md](docs/models.md)).
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# voiceio
|
|
2
|
+
|
|
3
|
+
[](https://github.com/Hugo0/voiceio/actions/workflows/ci.yml)
|
|
4
|
+
[](https://pypi.org/project/python-voiceio/)
|
|
5
|
+
[](https://pypi.org/project/python-voiceio/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
|
|
8
|
+
**Voice dictation for Linux that learns you.** Local, private, open source, and it fine-tunes itself on your own voice.
|
|
9
|
+
|
|
10
|
+
[](https://voiceio.dev)
|
|
11
|
+
|
|
12
|
+
**[Try it in your browser at voiceio.dev](https://voiceio.dev)**: no install, the model runs in the tab.
|
|
13
|
+
|
|
14
|
+
- **Types anywhere.** Press your hotkey, speak, and the words stream into the focused app with a live underlined preview (IBus), on Wayland and X11: GNOME, KDE, Hyprland, sway, i3.
|
|
15
|
+
- **Runs on your machine.** Whisper decodes your speech locally. No account, no server, no telemetry; logs never contain your words.
|
|
16
|
+
- **Learns your words.** Keep your recordings (opt-in, on your disk) and voiceio fine-tunes its model on them while the machine is idle. It switches only when the new model wins on clips it never trained on. On the author's voice: 12.1% → 4.9% word errors after one night on a laptop CPU.
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
**With a coding agent.** Paste this into Claude Code, Codex or any agent with a shell:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
Install voiceio on this machine following https://voiceio.dev/llms.txt. Ask me which hotkey I want and whether to keep my recordings on this computer so voiceio can learn my voice. Finish with voiceio doctor and fix whatever it reports.
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**By hand:**
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
sudo apt install pipx build-essential python3-dev portaudio19-dev ibus gir1.2-ibus-1.0 python3-gi # Debian/Ubuntu
|
|
30
|
+
sudo dnf install pipx gcc gcc-c++ make python3-devel portaudio-devel ibus ibus-libs python3-gobject # Fedora
|
|
31
|
+
sudo pacman -S python-pipx base-devel portaudio ibus python-gobject # Arch
|
|
32
|
+
|
|
33
|
+
pipx install 'python-voiceio[desktop]'
|
|
34
|
+
voiceio setup # model, hotkey, autostart
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Then press your hotkey, speak, and press it again. `voiceio doctor` shows what works and `voiceio doctor --fix` repairs what it can. NixOS, other extras and installing from source: [docs/linux.md](docs/linux.md). Agent runbook: [INSTALL.md](INSTALL.md).
|
|
38
|
+
|
|
39
|
+
## Make it yours
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
voiceio vocab add Kalshi # a name, recognized from the next dictation
|
|
43
|
+
voiceio corrections add kalchi Kalshi # fix whatever still comes out wrong
|
|
44
|
+
voiceio learn schedule on # learn from your recordings while the machine is idle
|
|
45
|
+
voiceio learn status # what it learned and how each model scores
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Scheduled learning runs daily or weekly at a time you pick, only on an idle machine on mains power, with a notification you can stop. It never switches models unless the new one wins, and `voiceio learn rollback` undoes a switch. Details: [docs/learning.md](docs/learning.md).
|
|
49
|
+
|
|
50
|
+
## Configure
|
|
51
|
+
|
|
52
|
+
Everything lives in `~/.config/voiceio/config.toml`; [config.example.toml](config.example.toml) documents every option. `voiceio config set section.key value` edits it in place.
|
|
53
|
+
|
|
54
|
+
## Docs
|
|
55
|
+
|
|
56
|
+
| | |
|
|
57
|
+
|---|---|
|
|
58
|
+
| [Learning your voice](docs/learning.md) | Vocabulary, corrections, labeling, fine-tuning, evaluation, schedule |
|
|
59
|
+
| [Choosing a model](docs/models.md) | Whisper sizes, Parakeet, whisper.cpp servers, licenses |
|
|
60
|
+
| [Commands](docs/commands.md) | Every `voiceio` command |
|
|
61
|
+
| [Your data](docs/data.md) | What is stored where, and what is off by default |
|
|
62
|
+
| [Phone dictation](docs/phone.md) | Run voiceio headless on a home server |
|
|
63
|
+
| [Linux support and troubleshooting](docs/linux.md) | Desktops, backends, NixOS, fixes |
|
|
64
|
+
| [Comparisons](https://voiceio.dev/vs/) | voiceio vs Voxtype, Wispr Flow, Superwhisper, Dragon and others |
|
|
65
|
+
|
|
66
|
+
## Roadmap
|
|
67
|
+
|
|
68
|
+
- **Unbounded vocabulary**: a decoder whose biasing has no token cap (see [model choice](CONTRIBUTING.md#model-choice-why-whisper-and-what-would-change-it)).
|
|
69
|
+
- **GPU on AMD and Intel**: let setup run a Vulkan whisper.cpp server for you.
|
|
70
|
+
- **Typing without IBus on GNOME and KDE**: a libei backend.
|
|
71
|
+
- **Distro packages**: AUR, .deb and .rpm.
|
|
72
|
+
- **One model everywhere**: sync your fine-tune across your machines.
|
|
73
|
+
|
|
74
|
+
## Contributing
|
|
75
|
+
|
|
76
|
+
Open an issue before a large PR. [CONTRIBUTING.md](CONTRIBUTING.md) covers the architecture, conventions and the reasoning behind the model choice. Install reports and ideas are welcome on the [feedback board](https://voiceio.dev/#feedback).
|
|
77
|
+
|
|
78
|
+
## Credits
|
|
79
|
+
|
|
80
|
+
- Speech recognition: [faster-whisper](https://github.com/SYSTRAN/faster-whisper) and [CTranslate2](https://github.com/OpenNMT/CTranslate2), with OpenAI's [Whisper](https://github.com/openai/whisper) models. The in-browser demo runs [Moonshine](https://github.com/usefulsensors/moonshine) through [transformers.js](https://github.com/huggingface/transformers.js).
|
|
81
|
+
- Ideas borrowed with thanks: modifier release, paste keys and media pausing from [Voxtype](https://github.com/peteonrails/voxtype); the landing page's look from Romario Kavin's [KOLlateral](https://github.com/RomarioKavin1/kollateral).
|
|
82
|
+
|
|
83
|
+
## License
|
|
84
|
+
|
|
85
|
+
MIT. Model weights are not part of voiceio and carry their own licenses (see [docs/models.md](docs/models.md)).
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-voiceio
|
|
3
|
+
Version: 1.6.0
|
|
4
|
+
Summary: Voice dictation for Linux. Speak → text, locally, instantly.
|
|
5
|
+
Author: Hugo Montenegro
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://voiceio.dev/
|
|
8
|
+
Project-URL: Repository, https://github.com/Hugo0/voiceio
|
|
9
|
+
Project-URL: Issues, https://github.com/Hugo0/voiceio/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/Hugo0/voiceio/releases
|
|
11
|
+
Keywords: voice,speech-to-text,whisper,linux,dictation,wayland,ibus
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: X11 Applications
|
|
14
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
15
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: faster-whisper>=1.0.0
|
|
22
|
+
Requires-Dist: numpy>=1.24.0
|
|
23
|
+
Requires-Dist: onnxruntime>=1.16.0
|
|
24
|
+
Requires-Dist: wordfreq>=3.0
|
|
25
|
+
Provides-Extra: desktop
|
|
26
|
+
Requires-Dist: sounddevice>=0.4.6; extra == "desktop"
|
|
27
|
+
Requires-Dist: evdev>=1.6.0; sys_platform == "linux" and extra == "desktop"
|
|
28
|
+
Requires-Dist: pynput>=1.7.6; extra == "desktop"
|
|
29
|
+
Requires-Dist: pystray>=0.19; extra == "desktop"
|
|
30
|
+
Requires-Dist: Pillow>=10.0; extra == "desktop"
|
|
31
|
+
Provides-Extra: linux
|
|
32
|
+
Requires-Dist: sounddevice>=0.4.6; extra == "linux"
|
|
33
|
+
Requires-Dist: evdev>=1.6.0; sys_platform == "linux" and extra == "linux"
|
|
34
|
+
Requires-Dist: pynput>=1.7.6; extra == "linux"
|
|
35
|
+
Requires-Dist: pystray>=0.19; extra == "linux"
|
|
36
|
+
Requires-Dist: Pillow>=10.0; extra == "linux"
|
|
37
|
+
Provides-Extra: server
|
|
38
|
+
Requires-Dist: aiohttp>=3.9; extra == "server"
|
|
39
|
+
Provides-Extra: tts
|
|
40
|
+
Requires-Dist: piper-tts>=1.3.0; extra == "tts"
|
|
41
|
+
Requires-Dist: edge-tts>=6.1.0; extra == "tts"
|
|
42
|
+
Provides-Extra: train
|
|
43
|
+
Requires-Dist: torch>=2.2; extra == "train"
|
|
44
|
+
Requires-Dist: transformers>=4.40; extra == "train"
|
|
45
|
+
Requires-Dist: peft>=0.10; extra == "train"
|
|
46
|
+
Provides-Extra: sherpa
|
|
47
|
+
Requires-Dist: sherpa-onnx>=1.12; extra == "sherpa"
|
|
48
|
+
Provides-Extra: dev
|
|
49
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
50
|
+
Requires-Dist: pytest-mock; extra == "dev"
|
|
51
|
+
Requires-Dist: ruff<0.18,>=0.15; extra == "dev"
|
|
52
|
+
Dynamic: license-file
|
|
53
|
+
|
|
54
|
+
# voiceio
|
|
55
|
+
|
|
56
|
+
[](https://github.com/Hugo0/voiceio/actions/workflows/ci.yml)
|
|
57
|
+
[](https://pypi.org/project/python-voiceio/)
|
|
58
|
+
[](https://pypi.org/project/python-voiceio/)
|
|
59
|
+
[](LICENSE)
|
|
60
|
+
|
|
61
|
+
**Voice dictation for Linux that learns you.** Local, private, open source, and it fine-tunes itself on your own voice.
|
|
62
|
+
|
|
63
|
+
[](https://voiceio.dev)
|
|
64
|
+
|
|
65
|
+
**[Try it in your browser at voiceio.dev](https://voiceio.dev)**: no install, the model runs in the tab.
|
|
66
|
+
|
|
67
|
+
- **Types anywhere.** Press your hotkey, speak, and the words stream into the focused app with a live underlined preview (IBus), on Wayland and X11: GNOME, KDE, Hyprland, sway, i3.
|
|
68
|
+
- **Runs on your machine.** Whisper decodes your speech locally. No account, no server, no telemetry; logs never contain your words.
|
|
69
|
+
- **Learns your words.** Keep your recordings (opt-in, on your disk) and voiceio fine-tunes its model on them while the machine is idle. It switches only when the new model wins on clips it never trained on. On the author's voice: 12.1% → 4.9% word errors after one night on a laptop CPU.
|
|
70
|
+
|
|
71
|
+
## Install
|
|
72
|
+
|
|
73
|
+
**With a coding agent.** Paste this into Claude Code, Codex or any agent with a shell:
|
|
74
|
+
|
|
75
|
+
```text
|
|
76
|
+
Install voiceio on this machine following https://voiceio.dev/llms.txt. Ask me which hotkey I want and whether to keep my recordings on this computer so voiceio can learn my voice. Finish with voiceio doctor and fix whatever it reports.
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
**By hand:**
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
sudo apt install pipx build-essential python3-dev portaudio19-dev ibus gir1.2-ibus-1.0 python3-gi # Debian/Ubuntu
|
|
83
|
+
sudo dnf install pipx gcc gcc-c++ make python3-devel portaudio-devel ibus ibus-libs python3-gobject # Fedora
|
|
84
|
+
sudo pacman -S python-pipx base-devel portaudio ibus python-gobject # Arch
|
|
85
|
+
|
|
86
|
+
pipx install 'python-voiceio[desktop]'
|
|
87
|
+
voiceio setup # model, hotkey, autostart
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Then press your hotkey, speak, and press it again. `voiceio doctor` shows what works and `voiceio doctor --fix` repairs what it can. NixOS, other extras and installing from source: [docs/linux.md](docs/linux.md). Agent runbook: [INSTALL.md](INSTALL.md).
|
|
91
|
+
|
|
92
|
+
## Make it yours
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
voiceio vocab add Kalshi # a name, recognized from the next dictation
|
|
96
|
+
voiceio corrections add kalchi Kalshi # fix whatever still comes out wrong
|
|
97
|
+
voiceio learn schedule on # learn from your recordings while the machine is idle
|
|
98
|
+
voiceio learn status # what it learned and how each model scores
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Scheduled learning runs daily or weekly at a time you pick, only on an idle machine on mains power, with a notification you can stop. It never switches models unless the new one wins, and `voiceio learn rollback` undoes a switch. Details: [docs/learning.md](docs/learning.md).
|
|
102
|
+
|
|
103
|
+
## Configure
|
|
104
|
+
|
|
105
|
+
Everything lives in `~/.config/voiceio/config.toml`; [config.example.toml](config.example.toml) documents every option. `voiceio config set section.key value` edits it in place.
|
|
106
|
+
|
|
107
|
+
## Docs
|
|
108
|
+
|
|
109
|
+
| | |
|
|
110
|
+
|---|---|
|
|
111
|
+
| [Learning your voice](docs/learning.md) | Vocabulary, corrections, labeling, fine-tuning, evaluation, schedule |
|
|
112
|
+
| [Choosing a model](docs/models.md) | Whisper sizes, Parakeet, whisper.cpp servers, licenses |
|
|
113
|
+
| [Commands](docs/commands.md) | Every `voiceio` command |
|
|
114
|
+
| [Your data](docs/data.md) | What is stored where, and what is off by default |
|
|
115
|
+
| [Phone dictation](docs/phone.md) | Run voiceio headless on a home server |
|
|
116
|
+
| [Linux support and troubleshooting](docs/linux.md) | Desktops, backends, NixOS, fixes |
|
|
117
|
+
| [Comparisons](https://voiceio.dev/vs/) | voiceio vs Voxtype, Wispr Flow, Superwhisper, Dragon and others |
|
|
118
|
+
|
|
119
|
+
## Roadmap
|
|
120
|
+
|
|
121
|
+
- **Unbounded vocabulary**: a decoder whose biasing has no token cap (see [model choice](CONTRIBUTING.md#model-choice-why-whisper-and-what-would-change-it)).
|
|
122
|
+
- **GPU on AMD and Intel**: let setup run a Vulkan whisper.cpp server for you.
|
|
123
|
+
- **Typing without IBus on GNOME and KDE**: a libei backend.
|
|
124
|
+
- **Distro packages**: AUR, .deb and .rpm.
|
|
125
|
+
- **One model everywhere**: sync your fine-tune across your machines.
|
|
126
|
+
|
|
127
|
+
## Contributing
|
|
128
|
+
|
|
129
|
+
Open an issue before a large PR. [CONTRIBUTING.md](CONTRIBUTING.md) covers the architecture, conventions and the reasoning behind the model choice. Install reports and ideas are welcome on the [feedback board](https://voiceio.dev/#feedback).
|
|
130
|
+
|
|
131
|
+
## Credits
|
|
132
|
+
|
|
133
|
+
- Speech recognition: [faster-whisper](https://github.com/SYSTRAN/faster-whisper) and [CTranslate2](https://github.com/OpenNMT/CTranslate2), with OpenAI's [Whisper](https://github.com/openai/whisper) models. The in-browser demo runs [Moonshine](https://github.com/usefulsensors/moonshine) through [transformers.js](https://github.com/huggingface/transformers.js).
|
|
134
|
+
- Ideas borrowed with thanks: modifier release, paste keys and media pausing from [Voxtype](https://github.com/peteonrails/voxtype); the landing page's look from Romario Kavin's [KOLlateral](https://github.com/RomarioKavin1/kollateral).
|
|
135
|
+
|
|
136
|
+
## License
|
|
137
|
+
|
|
138
|
+
MIT. Model weights are not part of voiceio and carry their own licenses (see [docs/models.md](docs/models.md)).
|
|
@@ -13,6 +13,7 @@ tests/test_audio_source.py
|
|
|
13
13
|
tests/test_backend_probes.py
|
|
14
14
|
tests/test_cli.py
|
|
15
15
|
tests/test_clipboard_read.py
|
|
16
|
+
tests/test_clipboard_typer.py
|
|
16
17
|
tests/test_commands.py
|
|
17
18
|
tests/test_concurrency_lockdown.py
|
|
18
19
|
tests/test_config.py
|
|
@@ -42,6 +43,8 @@ tests/test_learn_review.py
|
|
|
42
43
|
tests/test_learn_targets.py
|
|
43
44
|
tests/test_llm.py
|
|
44
45
|
tests/test_llm_api.py
|
|
46
|
+
tests/test_media.py
|
|
47
|
+
tests/test_modifier_guard.py
|
|
45
48
|
tests/test_numbers.py
|
|
46
49
|
tests/test_pipeline.py
|
|
47
50
|
tests/test_platform.py
|
|
@@ -84,6 +87,7 @@ voiceio/hints.py
|
|
|
84
87
|
voiceio/history.py
|
|
85
88
|
voiceio/llm.py
|
|
86
89
|
voiceio/llm_api.py
|
|
90
|
+
voiceio/media.py
|
|
87
91
|
voiceio/numbers.py
|
|
88
92
|
voiceio/pidlock.py
|
|
89
93
|
voiceio/platform.py
|
|
@@ -189,6 +193,7 @@ voiceio/typers/collecting.py
|
|
|
189
193
|
voiceio/typers/fcitx_bridge.py
|
|
190
194
|
voiceio/typers/ibus.py
|
|
191
195
|
voiceio/typers/manager.py
|
|
196
|
+
voiceio/typers/modifier_guard.py
|
|
192
197
|
voiceio/typers/pynput_type.py
|
|
193
198
|
voiceio/typers/wtype.py
|
|
194
199
|
voiceio/typers/xdotool.py
|
|
@@ -171,3 +171,35 @@ def test_copy_text_wayland_falls_back_to_wl_copy():
|
|
|
171
171
|
from voiceio.clipboard_read import copy_text
|
|
172
172
|
assert copy_text("hello") is True
|
|
173
173
|
assert calls == ["xclip", "wl-copy"]
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def test_read_clipboard_keeps_text_exact_and_skips_primary():
|
|
177
|
+
"""Restoring needs the clipboard byte for byte, never the mouse selection."""
|
|
178
|
+
with patch("voiceio.clipboard_read.detect", return_value=_mock_platform(display_server="x11")), \
|
|
179
|
+
patch("shutil.which", return_value="/usr/bin/xclip"), \
|
|
180
|
+
patch("subprocess.run") as mock_run:
|
|
181
|
+
mock_run.return_value = MagicMock(returncode=0, stdout=" two lines\nhere\n")
|
|
182
|
+
from voiceio.clipboard_read import read_clipboard
|
|
183
|
+
assert read_clipboard() == " two lines\nhere\n"
|
|
184
|
+
assert mock_run.call_args[0][0] == ["xclip", "-o", "-selection", "clipboard"]
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def test_read_clipboard_wayland_falls_back_to_wl_paste():
|
|
188
|
+
with patch("voiceio.clipboard_read.detect", return_value=_mock_platform(display_server="wayland")), \
|
|
189
|
+
patch("shutil.which", return_value="/usr/bin/tool"), \
|
|
190
|
+
patch("subprocess.run") as mock_run:
|
|
191
|
+
mock_run.side_effect = [
|
|
192
|
+
MagicMock(returncode=1, stdout=""),
|
|
193
|
+
MagicMock(returncode=0, stdout="wayland text"),
|
|
194
|
+
]
|
|
195
|
+
from voiceio.clipboard_read import read_clipboard
|
|
196
|
+
assert read_clipboard() == "wayland text"
|
|
197
|
+
assert mock_run.call_args[0][0] == ["wl-paste", "--no-newline"]
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def test_read_clipboard_empty_is_none():
|
|
201
|
+
with patch("voiceio.clipboard_read.detect", return_value=_mock_platform(display_server="x11")), \
|
|
202
|
+
patch("shutil.which", return_value="/usr/bin/xclip"), \
|
|
203
|
+
patch("subprocess.run", return_value=MagicMock(returncode=0, stdout="")):
|
|
204
|
+
from voiceio.clipboard_read import read_clipboard
|
|
205
|
+
assert read_clipboard() is None
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"""Clipboard typer: configurable paste keystroke and clipboard restore."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import subprocess
|
|
5
|
+
from unittest.mock import MagicMock, patch
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
|
|
9
|
+
from voiceio import config
|
|
10
|
+
from voiceio.typers import clipboard as clip_mod, typer_options
|
|
11
|
+
from voiceio.typers.clipboard import ClipboardTyper, parse_paste_keys, paste_command
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.mark.parametrize("spec,expected", [
|
|
15
|
+
("ctrl+v", (["ctrl"], "v")),
|
|
16
|
+
("Ctrl + Shift + V", (["ctrl", "shift"], "v")),
|
|
17
|
+
("shift+insert", (["shift"], "insert")),
|
|
18
|
+
])
|
|
19
|
+
def test_parse_paste_keys(spec, expected):
|
|
20
|
+
assert parse_paste_keys(spec) == expected
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@pytest.mark.parametrize("spec", ["ctrl+q", "hyper+v", "ctrl+ctrl+v", ""])
|
|
24
|
+
def test_unknown_paste_keys_fall_back_to_ctrl_v(spec, caplog):
|
|
25
|
+
assert parse_paste_keys(spec) == (["ctrl"], "v")
|
|
26
|
+
assert "paste_keys" in caplog.text
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_paste_command_per_tool():
|
|
30
|
+
mods, key = ["ctrl", "shift"], "v"
|
|
31
|
+
assert paste_command("ydotool", mods, key) == \
|
|
32
|
+
["ydotool", "key", "29:1", "42:1", "47:1", "47:0", "42:0", "29:0"]
|
|
33
|
+
assert paste_command("wtype", mods, key) == \
|
|
34
|
+
["wtype", "-M", "ctrl", "-M", "shift", "-k", "v", "-m", "shift", "-m", "ctrl"]
|
|
35
|
+
assert paste_command("xdotool", mods, key) == \
|
|
36
|
+
["xdotool", "key", "--clearmodifiers", "ctrl+shift+v"]
|
|
37
|
+
assert paste_command("xdotool", ["shift"], "insert") == \
|
|
38
|
+
["xdotool", "key", "--clearmodifiers", "shift+Insert"]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _wayland_typer(monkeypatch, tool="ydotool", **kwargs) -> ClipboardTyper:
|
|
42
|
+
monkeypatch.setattr(clip_mod.sys, "platform", "linux")
|
|
43
|
+
monkeypatch.setenv("WAYLAND_DISPLAY", "wayland-0")
|
|
44
|
+
available = {"wl-copy", tool}
|
|
45
|
+
monkeypatch.setattr(clip_mod.shutil, "which",
|
|
46
|
+
lambda name: f"/usr/bin/{name}" if name in available else None)
|
|
47
|
+
return ClipboardTyper(**kwargs)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@pytest.mark.parametrize("tool,expected", [
|
|
51
|
+
("ydotool", ["ydotool", "key", "29:1", "42:1", "47:1", "47:0", "42:0", "29:0"]),
|
|
52
|
+
("wtype", ["wtype", "-M", "ctrl", "-M", "shift", "-k", "v", "-m", "shift", "-m", "ctrl"]),
|
|
53
|
+
])
|
|
54
|
+
def test_configured_keys_are_what_gets_pressed(monkeypatch, tool, expected):
|
|
55
|
+
typer = _wayland_typer(monkeypatch, tool, paste_keys="ctrl+shift+v")
|
|
56
|
+
with patch.object(clip_mod.subprocess, "run") as run:
|
|
57
|
+
typer.type_text("hello")
|
|
58
|
+
assert run.call_args_list[0].args[0] == ["wl-copy", "--"]
|
|
59
|
+
assert run.call_args_list[1].args[0] == expected
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_default_paste_is_ctrl_v(monkeypatch):
|
|
63
|
+
typer = _wayland_typer(monkeypatch)
|
|
64
|
+
with patch.object(clip_mod.subprocess, "run") as run:
|
|
65
|
+
typer.type_text("hello")
|
|
66
|
+
assert run.call_args_list[1].args[0] == ["ydotool", "key", "29:1", "47:1", "47:0", "29:0"]
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_failed_paste_releases_the_configured_modifiers(monkeypatch):
|
|
70
|
+
typer = _wayland_typer(monkeypatch, paste_keys="ctrl+shift+v")
|
|
71
|
+
|
|
72
|
+
def run(cmd, **kwargs):
|
|
73
|
+
if cmd[:2] == ["ydotool", "key"] and cmd[2].endswith(":1"):
|
|
74
|
+
raise subprocess.TimeoutExpired(cmd, 3)
|
|
75
|
+
return MagicMock(returncode=0)
|
|
76
|
+
|
|
77
|
+
with patch.object(clip_mod.subprocess, "run", side_effect=run) as mock_run, \
|
|
78
|
+
pytest.raises(subprocess.TimeoutExpired):
|
|
79
|
+
typer.type_text("hello")
|
|
80
|
+
assert mock_run.call_args.args[0] == ["ydotool", "key", "29:0", "42:0"]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class FakeClipboard:
|
|
84
|
+
def __init__(self, text):
|
|
85
|
+
self.text = text
|
|
86
|
+
self.reads = 0
|
|
87
|
+
self.writes = []
|
|
88
|
+
|
|
89
|
+
def read(self):
|
|
90
|
+
self.reads += 1
|
|
91
|
+
return self.text
|
|
92
|
+
|
|
93
|
+
def copy(self, text):
|
|
94
|
+
self.writes.append(text)
|
|
95
|
+
self.text = text
|
|
96
|
+
return True
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@pytest.fixture
|
|
100
|
+
def clipboard(monkeypatch):
|
|
101
|
+
board = FakeClipboard("user's own text\n")
|
|
102
|
+
monkeypatch.setattr(clip_mod.clipboard_read, "read_clipboard", board.read)
|
|
103
|
+
monkeypatch.setattr(clip_mod.clipboard_read, "copy_text", board.copy)
|
|
104
|
+
monkeypatch.setattr(clip_mod, "RESTORE_DELAY_SECS", 0.01)
|
|
105
|
+
return board
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _copying_run(board):
|
|
109
|
+
"""subprocess.run stand-in: wl-copy puts its input on the fake board."""
|
|
110
|
+
def run(cmd, input=None, **kwargs):
|
|
111
|
+
if cmd[0] == "wl-copy":
|
|
112
|
+
board.text = input.decode()
|
|
113
|
+
return MagicMock(returncode=0)
|
|
114
|
+
return run
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _settle(typer):
|
|
118
|
+
"""Wait for a scheduled restore (None: it already ran)."""
|
|
119
|
+
timer = typer._restore_timer
|
|
120
|
+
if timer is not None:
|
|
121
|
+
timer.join(timeout=2)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _paste(typer, board, *texts):
|
|
125
|
+
"""Paste each text, then let the restore timer run."""
|
|
126
|
+
with patch.object(clip_mod.subprocess, "run", side_effect=_copying_run(board)):
|
|
127
|
+
for text in texts:
|
|
128
|
+
typer.type_text(text)
|
|
129
|
+
_settle(typer)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def test_restores_the_users_clipboard_after_pasting(monkeypatch, clipboard):
|
|
133
|
+
typer = _wayland_typer(monkeypatch, restore_clipboard=True)
|
|
134
|
+
_paste(typer, clipboard, "dictated")
|
|
135
|
+
assert clipboard.writes == ["user's own text\n"]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def test_streaming_burst_saves_once_and_restores_once(monkeypatch, clipboard):
|
|
139
|
+
typer = _wayland_typer(monkeypatch, restore_clipboard=True)
|
|
140
|
+
monkeypatch.setattr(clip_mod, "RESTORE_DELAY_SECS", 5)
|
|
141
|
+
with patch.object(clip_mod.subprocess, "run", side_effect=_copying_run(clipboard)):
|
|
142
|
+
typer.type_text("one ")
|
|
143
|
+
typer.type_text("two")
|
|
144
|
+
assert clipboard.reads == 1 # saved before the first paste only
|
|
145
|
+
typer._restore_timer.cancel()
|
|
146
|
+
typer._restore_saved(typer._restore_seq)
|
|
147
|
+
assert clipboard.writes == ["user's own text\n"]
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_superseded_restore_does_nothing(monkeypatch, clipboard):
|
|
151
|
+
typer = _wayland_typer(monkeypatch, restore_clipboard=True)
|
|
152
|
+
typer._saved, typer._pasted, typer._restore_seq = "old", "x", 2
|
|
153
|
+
clipboard.text = "x"
|
|
154
|
+
typer._restore_saved(1)
|
|
155
|
+
assert clipboard.writes == []
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def test_no_restore_over_something_copied_since(monkeypatch, clipboard):
|
|
159
|
+
typer = _wayland_typer(monkeypatch, restore_clipboard=True)
|
|
160
|
+
|
|
161
|
+
def run(cmd, input=None, **kwargs):
|
|
162
|
+
clipboard.text = "copied by the user meanwhile"
|
|
163
|
+
return MagicMock(returncode=0)
|
|
164
|
+
|
|
165
|
+
with patch.object(clip_mod.subprocess, "run", side_effect=run):
|
|
166
|
+
typer.type_text("dictated")
|
|
167
|
+
_settle(typer)
|
|
168
|
+
assert clipboard.writes == []
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def test_empty_clipboard_is_left_with_the_transcript(monkeypatch, clipboard):
|
|
172
|
+
clipboard.text = None
|
|
173
|
+
typer = _wayland_typer(monkeypatch, restore_clipboard=True)
|
|
174
|
+
_paste(typer, clipboard, "dictated")
|
|
175
|
+
assert clipboard.writes == []
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def test_restore_disabled_never_reads_the_clipboard(monkeypatch, clipboard):
|
|
179
|
+
typer = _wayland_typer(monkeypatch)
|
|
180
|
+
with patch.object(clip_mod.subprocess, "run"):
|
|
181
|
+
typer.type_text("dictated")
|
|
182
|
+
assert clipboard.reads == 0
|
|
183
|
+
assert typer._restore_timer is None
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
@pytest.mark.parametrize("copy,notify,expected", [
|
|
187
|
+
("off", False, True),
|
|
188
|
+
("final", False, False), # the transcript is meant to stay on the clipboard
|
|
189
|
+
("live", False, False),
|
|
190
|
+
("off", True, False), # the notification says "copied to clipboard"
|
|
191
|
+
])
|
|
192
|
+
def test_restore_only_when_nothing_keeps_the_transcript(copy, notify, expected):
|
|
193
|
+
cfg = config.Config()
|
|
194
|
+
cfg.output.copy_to_clipboard = copy
|
|
195
|
+
cfg.feedback.notify_clipboard = notify
|
|
196
|
+
assert typer_options(cfg)["restore_clipboard"] is expected
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def test_options_reach_the_clipboard_typer(linux_wayland_gnome):
|
|
200
|
+
from voiceio.typers import create_typer_backend
|
|
201
|
+
typer = create_typer_backend("clipboard", linux_wayland_gnome,
|
|
202
|
+
paste_keys="shift+insert", restore_clipboard=True)
|
|
203
|
+
assert (typer._paste_mods, typer._paste_key) == (["shift"], "insert")
|
|
204
|
+
assert typer._restore_clipboard is True
|