python-voiceio 1.9.3__tar.gz → 1.10.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.
Files changed (223) hide show
  1. {python_voiceio-1.9.3/python_voiceio.egg-info → python_voiceio-1.10.0}/PKG-INFO +1 -1
  2. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/pyproject.toml +1 -1
  3. {python_voiceio-1.9.3 → python_voiceio-1.10.0/python_voiceio.egg-info}/PKG-INFO +1 -1
  4. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/python_voiceio.egg-info/SOURCES.txt +3 -0
  5. python_voiceio-1.10.0/tests/test_mic_gain.py +85 -0
  6. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_service.py +2 -2
  7. python_voiceio-1.10.0/voiceio/__init__.py +1 -0
  8. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/app.py +34 -20
  9. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/config.py +5 -1
  10. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/feedback.py +13 -1
  11. python_voiceio-1.10.0/voiceio/mic_gain.py +123 -0
  12. python_voiceio-1.10.0/voiceio/sounds/alert.wav +0 -0
  13. python_voiceio-1.9.3/voiceio/__init__.py +0 -1
  14. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/LICENSE +0 -0
  15. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/README.md +0 -0
  16. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/python_voiceio.egg-info/dependency_links.txt +0 -0
  17. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/python_voiceio.egg-info/entry_points.txt +0 -0
  18. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/python_voiceio.egg-info/requires.txt +0 -0
  19. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/python_voiceio.egg-info/top_level.txt +0 -0
  20. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/setup.cfg +0 -0
  21. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_app_wiring.py +0 -0
  22. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_audio_quality.py +0 -0
  23. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_audio_source.py +0 -0
  24. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_backend_probes.py +0 -0
  25. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_cancel.py +0 -0
  26. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_cli.py +0 -0
  27. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_clipboard_read.py +0 -0
  28. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_clipboard_typer.py +0 -0
  29. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_commands.py +0 -0
  30. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_concurrency_lockdown.py +0 -0
  31. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_config.py +0 -0
  32. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_context_terms.py +0 -0
  33. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_corrections.py +0 -0
  34. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_dead_mic.py +0 -0
  35. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_decoders.py +0 -0
  36. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_engine.py +0 -0
  37. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_evdev_uaccess.py +0 -0
  38. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_fallback.py +0 -0
  39. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_fcitx_bridge.py +0 -0
  40. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_feedback_stream.py +0 -0
  41. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_focus.py +0 -0
  42. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_health.py +0 -0
  43. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_hints.py +0 -0
  44. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_history.py +0 -0
  45. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_hotkey_modes.py +0 -0
  46. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_ibus_engine_limits.py +0 -0
  47. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_ibus_engine_listener.py +0 -0
  48. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_ibus_pending.py +0 -0
  49. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_ibus_pids.py +0 -0
  50. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_ibus_ping.py +0 -0
  51. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_ibus_session.py +0 -0
  52. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_ibus_textlimit.py +0 -0
  53. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_ibus_typer.py +0 -0
  54. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_idle_audio.py +0 -0
  55. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_learn_clean.py +0 -0
  56. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_learn_import.py +0 -0
  57. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_learn_language.py +0 -0
  58. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_learn_metrics.py +0 -0
  59. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_learn_pipeline.py +0 -0
  60. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_learn_quiet.py +0 -0
  61. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_learn_resume.py +0 -0
  62. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_learn_review.py +0 -0
  63. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_learn_targets.py +0 -0
  64. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_llm.py +0 -0
  65. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_llm_api.py +0 -0
  66. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_modifier_guard.py +0 -0
  67. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_numbers.py +0 -0
  68. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_paste_last.py +0 -0
  69. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_pipeline.py +0 -0
  70. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_platform.py +0 -0
  71. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_postcorrect.py +0 -0
  72. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_postprocess.py +0 -0
  73. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_prebuffer.py +0 -0
  74. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_presence.py +0 -0
  75. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_prompt.py +0 -0
  76. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_quiet_audio.py +0 -0
  77. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_recorder_integration.py +0 -0
  78. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_retention.py +0 -0
  79. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_robustness.py +0 -0
  80. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_security_hardening.py +0 -0
  81. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_server.py +0 -0
  82. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_setup.py +0 -0
  83. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_streaming.py +0 -0
  84. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_suggest.py +0 -0
  85. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_tokens.py +0 -0
  86. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_transcriber.py +0 -0
  87. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_tts.py +0 -0
  88. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_typer_manager.py +0 -0
  89. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_vad.py +0 -0
  90. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_vocabulary.py +0 -0
  91. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_wordfreq.py +0 -0
  92. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/tests/test_worker_decode.py +0 -0
  93. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/__main__.py +0 -0
  94. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/audio_source.py +0 -0
  95. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/backends.py +0 -0
  96. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/cli/__init__.py +0 -0
  97. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/cli/configcmd.py +0 -0
  98. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/cli/corrections.py +0 -0
  99. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/cli/doctor.py +0 -0
  100. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/cli/history.py +0 -0
  101. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/cli/learn.py +0 -0
  102. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/cli/models.py +0 -0
  103. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/cli/servicecmd.py +0 -0
  104. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/cli/uninstall.py +0 -0
  105. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/cli/vocab.py +0 -0
  106. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/clipboard_read.py +0 -0
  107. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/commands.py +0 -0
  108. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/consent.py +0 -0
  109. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/context_terms.py +0 -0
  110. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/core/__init__.py +0 -0
  111. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/core/bias.py +0 -0
  112. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/core/decoders/__init__.py +0 -0
  113. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/core/decoders/base.py +0 -0
  114. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/core/decoders/catalog.py +0 -0
  115. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/core/decoders/faster_whisper/__init__.py +0 -0
  116. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/core/decoders/faster_whisper/worker.py +0 -0
  117. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/core/decoders/lanes.py +0 -0
  118. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/core/decoders/sherpa.py +0 -0
  119. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/core/decoders/whispercpp.py +0 -0
  120. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/core/engine.py +0 -0
  121. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/core/pipeline.py +0 -0
  122. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/corrections.py +0 -0
  123. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/data/60-voiceio-uaccess.rules +0 -0
  124. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/data/__init__.py +0 -0
  125. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/demo.py +0 -0
  126. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/display.py +0 -0
  127. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/focus.py +0 -0
  128. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/health.py +0 -0
  129. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/hints.py +0 -0
  130. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/history.py +0 -0
  131. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/hotkeys/__init__.py +0 -0
  132. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/hotkeys/base.py +0 -0
  133. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/hotkeys/chain.py +0 -0
  134. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/hotkeys/evdev.py +0 -0
  135. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/hotkeys/pynput_backend.py +0 -0
  136. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/hotkeys/socket_backend.py +0 -0
  137. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/ibus/__init__.py +0 -0
  138. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/ibus/engine.py +0 -0
  139. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/ibus/install.py +0 -0
  140. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/ibus/pending.py +0 -0
  141. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/ibus/session.py +0 -0
  142. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/ibus/textlimit.py +0 -0
  143. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/idle_audio.py +0 -0
  144. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/learn/__init__.py +0 -0
  145. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/learn/augment.py +0 -0
  146. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/learn/clean.py +0 -0
  147. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/learn/evaluate.py +0 -0
  148. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/learn/importer.py +0 -0
  149. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/learn/metrics.py +0 -0
  150. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/learn/quiet.py +0 -0
  151. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/learn/review.py +0 -0
  152. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/learn/store.py +0 -0
  153. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/learn/teacher.py +0 -0
  154. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/learn/train.py +0 -0
  155. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/llm.py +0 -0
  156. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/llm_api.py +0 -0
  157. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/models/__init__.py +0 -0
  158. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/models/silero_vad.onnx +0 -0
  159. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/numbers.py +0 -0
  160. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/pidlock.py +0 -0
  161. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/platform.py +0 -0
  162. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/postcorrect.py +0 -0
  163. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/postprocess.py +0 -0
  164. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/power.py +0 -0
  165. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/presence.py +0 -0
  166. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/privacy.py +0 -0
  167. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/prompt.py +0 -0
  168. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/quiet_audio.py +0 -0
  169. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/recorder.py +0 -0
  170. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/retention.py +0 -0
  171. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/server/__init__.py +0 -0
  172. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/server/__main__.py +0 -0
  173. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/server/app.py +0 -0
  174. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/server/config.py +0 -0
  175. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/server/runtime.py +0 -0
  176. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/service.py +0 -0
  177. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/setup/__init__.py +0 -0
  178. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/setup/configfile.py +0 -0
  179. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/setup/extras.py +0 -0
  180. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/setup/hotkey.py +0 -0
  181. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/setup/noninteractive.py +0 -0
  182. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/setup/speech.py +0 -0
  183. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/setup/steps.py +0 -0
  184. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/setup/system.py +0 -0
  185. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/setup/ui.py +0 -0
  186. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/sounds/__init__.py +0 -0
  187. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/sounds/commit.wav +0 -0
  188. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/sounds/start.wav +0 -0
  189. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/sounds/stop.wav +0 -0
  190. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/streaming.py +0 -0
  191. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/suggest.py +0 -0
  192. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/tokens.py +0 -0
  193. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/tray/__init__.py +0 -0
  194. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/tray/_icons.py +0 -0
  195. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/tray/_indicator.py +0 -0
  196. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/tray/_pystray.py +0 -0
  197. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/tts/__init__.py +0 -0
  198. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/tts/base.py +0 -0
  199. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/tts/chain.py +0 -0
  200. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/tts/edge_engine.py +0 -0
  201. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/tts/espeak.py +0 -0
  202. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/tts/piper_engine.py +0 -0
  203. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/tts/player.py +0 -0
  204. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/typers/__init__.py +0 -0
  205. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/typers/base.py +0 -0
  206. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/typers/chain.py +0 -0
  207. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/typers/clipboard.py +0 -0
  208. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/typers/collecting.py +0 -0
  209. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/typers/fcitx_bridge.py +0 -0
  210. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/typers/ibus.py +0 -0
  211. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/typers/manager.py +0 -0
  212. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/typers/modifier_guard.py +0 -0
  213. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/typers/paste.py +0 -0
  214. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/typers/pynput_type.py +0 -0
  215. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/typers/wtype.py +0 -0
  216. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/typers/xdotool.py +0 -0
  217. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/typers/ydotool.py +0 -0
  218. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/ui.py +0 -0
  219. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/vad.py +0 -0
  220. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/vocab_stats.py +0 -0
  221. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/vocabulary.py +0 -0
  222. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/watchdog.py +0 -0
  223. {python_voiceio-1.9.3 → python_voiceio-1.10.0}/voiceio/wordfreq.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-voiceio
3
- Version: 1.9.3
3
+ Version: 1.10.0
4
4
  Summary: Voice dictation for Linux. Speak → text, locally, instantly.
5
5
  Author: Hugo Montenegro
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-voiceio"
7
- version = "1.9.3"
7
+ version = "1.10.0"
8
8
  description = "Voice dictation for Linux. Speak → text, locally, instantly."
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-voiceio
3
- Version: 1.9.3
3
+ Version: 1.10.0
4
4
  Summary: Voice dictation for Linux. Speak → text, locally, instantly.
5
5
  Author: Hugo Montenegro
6
6
  License-Expression: MIT
@@ -52,6 +52,7 @@ tests/test_learn_review.py
52
52
  tests/test_learn_targets.py
53
53
  tests/test_llm.py
54
54
  tests/test_llm_api.py
55
+ tests/test_mic_gain.py
55
56
  tests/test_modifier_guard.py
56
57
  tests/test_numbers.py
57
58
  tests/test_paste_last.py
@@ -101,6 +102,7 @@ voiceio/history.py
101
102
  voiceio/idle_audio.py
102
103
  voiceio/llm.py
103
104
  voiceio/llm_api.py
105
+ voiceio/mic_gain.py
104
106
  voiceio/numbers.py
105
107
  voiceio/pidlock.py
106
108
  voiceio/platform.py
@@ -187,6 +189,7 @@ voiceio/setup/steps.py
187
189
  voiceio/setup/system.py
188
190
  voiceio/setup/ui.py
189
191
  voiceio/sounds/__init__.py
192
+ voiceio/sounds/alert.wav
190
193
  voiceio/sounds/commit.wav
191
194
  voiceio/sounds/start.wav
192
195
  voiceio/sounds/stop.wav
@@ -0,0 +1,85 @@
1
+ """Input gain follows the recordings: down on clipping, up when very quiet,
2
+ never back to a level that clipped."""
3
+ from __future__ import annotations
4
+
5
+ from voiceio import mic_gain
6
+ from voiceio.mic_gain import MicGain
7
+
8
+
9
+ class FakeSource:
10
+ def __init__(self, volume):
11
+ self.volume = volume
12
+ self.sets = []
13
+
14
+ def get(self):
15
+ return self.volume
16
+
17
+ def set(self, v):
18
+ self.sets.append(v)
19
+ self.volume = v
20
+ return True
21
+
22
+
23
+ def _gain(volume=0.5):
24
+ g = MicGain(enabled=False)
25
+ g._source = FakeSource(volume)
26
+ return g
27
+
28
+
29
+ def test_clipping_lowers_by_a_fifth():
30
+ g = _gain(0.5)
31
+ change = g.after_recording(peak=1.0, clip_ratio=0.02, secs=10)
32
+ assert change.reason == "clipping" and g._source.volume == 0.4
33
+
34
+
35
+ def test_never_below_the_floor():
36
+ g = _gain(0.11)
37
+ g.after_recording(1.0, 0.02, 10)
38
+ assert g._source.volume == mic_gain.FLOOR
39
+
40
+
41
+ def test_three_quiet_recordings_raise_but_not_to_a_level_that_clipped():
42
+ g = _gain(0.5)
43
+ g.after_recording(1.0, 0.02, 10) # 0.5 clipped -> 0.4
44
+ for _ in range(2):
45
+ assert g.after_recording(0.05, 0.0, 10) is None
46
+ assert g.after_recording(0.05, 0.0, 10) is None # third quiet one: would reach 0.5
47
+ assert g._source.volume == 0.4 # capped at 0.5 * 0.8: no oscillation
48
+
49
+
50
+ def test_quiet_without_any_clipping_history_raises():
51
+ g = _gain(0.3)
52
+ for _ in range(3):
53
+ change = g.after_recording(0.05, 0.0, 10)
54
+ assert change.new == round(0.3 / mic_gain.STEP, 2)
55
+
56
+
57
+ def test_short_or_dead_recordings_dont_count():
58
+ g = _gain(0.3)
59
+ for _ in range(5):
60
+ assert g.after_recording(0.05, 0.0, 1.0) is None # too short
61
+ assert g.after_recording(0.0, 0.0, 10) is None # muted mic: not a gain problem
62
+ assert g._source.sets == []
63
+
64
+
65
+ def test_normal_level_resets_the_quiet_streak():
66
+ g = _gain(0.3)
67
+ g.after_recording(0.05, 0.0, 10)
68
+ g.after_recording(0.05, 0.0, 10)
69
+ g.after_recording(0.5, 0.0, 10)
70
+ assert g.after_recording(0.05, 0.0, 10) is None
71
+
72
+
73
+ def test_a_chosen_device_is_left_alone():
74
+ assert not MicGain(enabled=True, device="hw:1").active
75
+
76
+
77
+ def test_off_without_volume_tools(monkeypatch):
78
+ # conftest: quiet_audio._which finds nothing in tests
79
+ assert not MicGain(enabled=True).active
80
+
81
+
82
+ def test_failures_never_raise():
83
+ g = _gain(0.5)
84
+ g._source.get = lambda: (_ for _ in ()).throw(RuntimeError("boom"))
85
+ assert g.after_recording(1.0, 0.02, 10) is None
@@ -44,7 +44,7 @@ def test_learn_timer_runs_maintain_at_idle_priority(monkeypatch, tmp_path):
44
44
  "MemoryHigh=25%", "OOMScoreAdjust=500"):
45
45
  assert f"\n{line}\n" in unit, line
46
46
  timer = service.LEARN_TIMER_PATH.read_text()
47
- assert "Persistent=true" in timer and "OnCalendar=*-*-* 03:30" in timer
47
+ assert "Persistent=true" in timer and "OnCalendar=Sun *-*-* 03:30" in timer
48
48
  assert ("enable", "--now", service.LEARN_TIMER_NAME) in calls
49
49
 
50
50
  service.uninstall_learn_timer()
@@ -109,7 +109,7 @@ def test_learn_schedule_comes_from_config(monkeypatch, tmp_path):
109
109
  monkeypatch.setattr(service, "LEARN_SERVICE_PATH", tmp_path / "s.service")
110
110
  monkeypatch.setattr(service, "LEARN_TIMER_PATH", tmp_path / "s.timer")
111
111
  service.install_learn_timer()
112
- assert "OnCalendar=*-*-* 03:30" in service.LEARN_TIMER_PATH.read_text()
112
+ assert "OnCalendar=Sun *-*-* 03:30" in service.LEARN_TIMER_PATH.read_text() # weekly by default
113
113
 
114
114
  config.set_value("learn", "at", "2:05")
115
115
  config.set_value("learn", "every", "week")
@@ -0,0 +1 @@
1
+ __version__ = "1.10.0"
@@ -111,6 +111,8 @@ class VoiceIO:
111
111
  self._recording_bias = Bias()
112
112
  from voiceio.quiet_audio import QuietAudio
113
113
  self._quiet = QuietAudio(cfg.audio.pause_media, cfg.audio.duck_volume)
114
+ from voiceio.mic_gain import MicGain
115
+ self._mic_gain = MicGain(cfg.audio.auto_gain, cfg.audio.device)
114
116
 
115
117
  self._command_processor = CommandProcessor(enabled=cfg.commands.enabled, editing=cfg.commands.editing)
116
118
  self._streaming = cfg.output.streaming
@@ -437,10 +439,9 @@ class VoiceIO:
437
439
  if dead:
438
440
  log.warning("Mic still silent after reopening")
439
441
  from voiceio import feedback
440
- feedback.notify(
442
+ feedback.mic_alert(
441
443
  "VoiceIO: microphone stopped sending audio",
442
- "Your words so far are kept. Stop, check the input device, and try again.",
443
- urgent=True,
444
+ "Your words so far are kept.",
444
445
  )
445
446
  else:
446
447
  log.info("Mic stream recovered mid-recording")
@@ -457,10 +458,9 @@ class VoiceIO:
457
458
  if reason == "no_speech":
458
459
  from voiceio import feedback
459
460
  if not self.recorder.has_signal():
460
- feedback.notify(
461
+ feedback.mic_alert(
461
462
  "VoiceIO: microphone is muted",
462
- "Stopped — no audio is reaching voiceio. Check your mic's "
463
- "mute button and that the right input device is selected.",
463
+ "Stopped — no audio is reaching voiceio.",
464
464
  )
465
465
  else:
466
466
  feedback.notify(
@@ -485,11 +485,9 @@ class VoiceIO:
485
485
  return
486
486
  log.warning("Recording captured no audio at all — mic muted?")
487
487
  from voiceio import feedback
488
- feedback.notify(
488
+ feedback.mic_alert(
489
489
  "VoiceIO: microphone is muted — nothing recorded",
490
- "No audio reached voiceio for that entire recording. Check your "
491
- "mic's mute button and which input device is selected.",
492
- urgent=True,
490
+ "No audio reached voiceio for that entire recording.",
493
491
  )
494
492
 
495
493
  def _request_stop(self) -> None:
@@ -576,10 +574,9 @@ class VoiceIO:
576
574
  if not self.recorder.has_signal():
577
575
  log.warning("Mic still silent after reopen — muted or wrong device?")
578
576
  from voiceio import feedback
579
- feedback.notify(
577
+ feedback.mic_alert(
580
578
  "VoiceIO: microphone appears silent",
581
- "Check that your mic is unmuted and the right input device is selected.",
582
- urgent=True,
579
+ "No sound is coming from the input device.",
583
580
  )
584
581
  elif not self.recorder.has_signal():
585
582
  log.warning("Mic appears silent or muted (pre-buffer is all zeros)")
@@ -634,7 +631,7 @@ class VoiceIO:
634
631
  audio = self.recorder.stop()
635
632
  tray.set_recording(False)
636
633
  self._idle_audio.touch()
637
- self._warn_if_clipping()
634
+ self._check_input_level(time.monotonic() - self._record_start)
638
635
 
639
636
  self.engine.prepare_final(title=self._context_title)
640
637
 
@@ -706,13 +703,15 @@ class VoiceIO:
706
703
  _CLIP_RATIO_WARN = 0.005 # >0.5% of samples in flat-top runs
707
704
  _CLIP_WARN_INTERVAL = 300 # seconds between warnings
708
705
 
709
- def _warn_if_clipping(self) -> None:
710
- """Warn (rate-limited) when the mic input is saturating the ADC.
711
-
712
- Clipping cannot be repaired in software — the gain must come down
713
- before the ADC — so tell the user how to lower it.
714
- """
706
+ def _check_input_level(self, secs: float) -> None:
707
+ """After a recording: fix the input gain when auto_gain can, else
708
+ warn (rate-limited) about clipping. Off this thread: the volume tools
709
+ are subprocesses."""
715
710
  meter = self.recorder.get_meter()
711
+ if self._mic_gain.active:
712
+ threading.Thread(target=self._adjust_gain, args=(meter, secs),
713
+ daemon=True, name="mic-gain").start()
714
+ return
716
715
  if meter["clip_ratio"] < self._CLIP_RATIO_WARN:
717
716
  return
718
717
  now = time.monotonic()
@@ -731,6 +730,21 @@ class VoiceIO:
731
730
  "Input is clipping — lower your mic gain in system sound settings.",
732
731
  )
733
732
 
733
+ def _adjust_gain(self, meter: dict[str, float], secs: float) -> None:
734
+ change = self._mic_gain.after_recording(meter["peak"], meter["clip_ratio"], secs)
735
+ if change is None or change.reason != "clipping":
736
+ return
737
+ now = time.monotonic()
738
+ if now - self._last_clip_warn < self._CLIP_WARN_INTERVAL:
739
+ return
740
+ self._last_clip_warn = now
741
+ from voiceio import feedback
742
+ feedback.notify(
743
+ "VoiceIO lowered your mic volume",
744
+ f"It was clipping: {change.old:.0%} → {change.new:.0%}. "
745
+ "Turn it off with: voiceio config set audio.auto_gain false",
746
+ )
747
+
734
748
  # ── Background work ─────────────────────────────────────────────────
735
749
 
736
750
  def _finalize_streaming(
@@ -128,6 +128,10 @@ class AudioConfig:
128
128
  # last dictation; "never": closed between dictations, opened on the hotkey.
129
129
  keep_warm: str = "auto"
130
130
  warm_minutes: float = 5.0
131
+ # Follow your recordings with the default mic's input volume
132
+ # (voiceio/mic_gain.py): down a fifth after one that clipped, up a fifth
133
+ # after three that were very quiet, never back to a level that clipped.
134
+ auto_gain: bool = True
131
135
 
132
136
 
133
137
  @dataclass
@@ -290,7 +294,7 @@ class LearnConfig:
290
294
  # When nightly learning runs (`voiceio learn schedule on`): a time of day,
291
295
  # and "day" or "week" (weekly runs on Sunday).
292
296
  at: str = "03:30"
293
- every: str = "day"
297
+ every: str = "week"
294
298
  # It works only while nobody is using the computer: no keyboard or mouse
295
299
  # input for away_minutes (or the screen locked), on mains power, and
296
300
  # starts only when the 1-minute load per CPU is at most max_load. When
@@ -76,7 +76,7 @@ def warm_up() -> None:
76
76
 
77
77
 
78
78
  def _load_sounds() -> None:
79
- for name in ("start", "stop", "commit"):
79
+ for name in ("start", "stop", "commit", "alert"):
80
80
  path = _SOUNDS_DIR / f"{name}.wav"
81
81
  if name in _sounds or not path.exists():
82
82
  continue
@@ -141,6 +141,18 @@ def play_commit_sound() -> None:
141
141
  _play("commit")
142
142
 
143
143
 
144
+ # The usual cause, by far: a hardware mic switch, a mute key or a muted
145
+ # source. Each look the same to voiceio — a stream that sends only zeros.
146
+ MIC_HINT = "Is the mic switched off or muted (privacy switch, mute key)?"
147
+
148
+
149
+ def mic_alert(title: str, body: str) -> None:
150
+ """A dead or muted mic: an alert sound as well as the notification, since
151
+ you are talking, not looking at the screen."""
152
+ _play("alert")
153
+ notify(title, f"{body} {MIC_HINT}", urgent=True)
154
+
155
+
144
156
  def cue_secs(name: str) -> float:
145
157
  """How long a cue takes to play out; 0 when sound output is off."""
146
158
  if _stream is None or name not in _sounds:
@@ -0,0 +1,123 @@
1
+ """Mic input gain that follows your recordings ([audio] auto_gain).
2
+
3
+ Clipping can't be repaired after the ADC, so the fix is the input volume.
4
+ After each recording:
5
+
6
+ clipped (flat-top runs in >= 0.5% of samples)
7
+ → lower the default source's volume by a fifth, and remember that the
8
+ old level clipped: it is never raised back to it
9
+ three spoken recordings in a row that peaked below 0.1
10
+ → raise it by a fifth, staying under the level that clipped
11
+
12
+ Only the default PipeWire/Pulse source is touched (wpctl, else pactl), and
13
+ never below 10% or above 100%. A device chosen in [audio] device is the
14
+ user's to set.
15
+ """
16
+ from __future__ import annotations
17
+
18
+ import logging
19
+ import re
20
+ from dataclasses import dataclass
21
+
22
+ from voiceio import quiet_audio
23
+ from voiceio.quiet_audio import _Caller
24
+
25
+ log = logging.getLogger(__name__)
26
+
27
+ CLIP_RATIO = 0.005
28
+ QUIET_PEAK = 0.1
29
+ QUIET_STREAK = 3
30
+ MIN_SPEECH_SECS = 3.0
31
+ STEP = 0.8
32
+ FLOOR, CEILING = 0.10, 1.0
33
+
34
+
35
+ class _WpctlSource:
36
+ def __init__(self, call: _Caller):
37
+ self._call = call
38
+
39
+ def get(self) -> float | None:
40
+ m = re.match(r"Volume: ([\d.]+)", self._call("wpctl", "get-volume", "@DEFAULT_AUDIO_SOURCE@") or "")
41
+ return float(m[1]) if m else None
42
+
43
+ def set(self, volume: float) -> bool:
44
+ return self._call("wpctl", "set-volume", "@DEFAULT_AUDIO_SOURCE@", f"{volume:.2f}") is not None
45
+
46
+
47
+ class _PactlSource:
48
+ def __init__(self, call: _Caller):
49
+ self._call = call
50
+
51
+ def get(self) -> float | None:
52
+ m = re.search(r"(\d+)%", self._call("pactl", "get-source-volume", "@DEFAULT_SOURCE@") or "")
53
+ return int(m[1]) / 100 if m else None
54
+
55
+ def set(self, volume: float) -> bool:
56
+ return self._call("pactl", "set-source-volume", "@DEFAULT_SOURCE@",
57
+ f"{round(volume * 100)}%") is not None
58
+
59
+
60
+ @dataclass
61
+ class Change:
62
+ old: float
63
+ new: float
64
+ reason: str # "clipping" | "quiet"
65
+
66
+
67
+ class MicGain:
68
+ def __init__(self, enabled: bool = True, device: str = "default", run=None):
69
+ call = _Caller(run)
70
+ self._source = None
71
+ if enabled and device in ("", "default"):
72
+ # Through the module: tests swap quiet_audio._which/_run for guards.
73
+ if quiet_audio._which("wpctl"):
74
+ self._source = _WpctlSource(call)
75
+ elif quiet_audio._which("pactl"):
76
+ self._source = _PactlSource(call)
77
+ self._clipped_at: float | None = None # lowest level known to clip
78
+ self._quiet = 0
79
+
80
+ @property
81
+ def active(self) -> bool:
82
+ return self._source is not None
83
+
84
+ def after_recording(self, peak: float, clip_ratio: float, secs: float) -> Change | None:
85
+ """Adjust after one recording; the change made, if any. Never raises."""
86
+ if self._source is None:
87
+ return None
88
+ try:
89
+ return self._adjust(peak, clip_ratio, secs)
90
+ except Exception: # noqa: BLE001 — a gain tweak must never break dictation
91
+ log.debug("mic gain adjustment failed", exc_info=True)
92
+ return None
93
+
94
+ def _adjust(self, peak: float, clip_ratio: float, secs: float) -> Change | None:
95
+ if clip_ratio >= CLIP_RATIO:
96
+ self._quiet = 0
97
+ old = self._source.get()
98
+ if old is None:
99
+ return None
100
+ self._clipped_at = old if self._clipped_at is None else min(self._clipped_at, old)
101
+ new = max(FLOOR, round(old * STEP, 2))
102
+ return self._set(old, new, "clipping")
103
+ if secs < MIN_SPEECH_SECS or peak <= 0:
104
+ return None # too short to judge, or a dead/muted mic: not a gain problem
105
+ if peak >= QUIET_PEAK:
106
+ self._quiet = 0
107
+ return None
108
+ self._quiet += 1
109
+ if self._quiet < QUIET_STREAK:
110
+ return None
111
+ self._quiet = 0
112
+ old = self._source.get()
113
+ if old is None:
114
+ return None
115
+ top = CEILING if self._clipped_at is None else min(CEILING, round(self._clipped_at * STEP, 2))
116
+ new = min(top, round(old / STEP, 2))
117
+ return self._set(old, new, "quiet")
118
+
119
+ def _set(self, old: float, new: float, reason: str) -> Change | None:
120
+ if abs(new - old) < 0.005 or not self._source.set(new):
121
+ return None
122
+ log.info("Mic gain %s: input volume %.0f%% → %.0f%%", reason, old * 100, new * 100)
123
+ return Change(old, new, reason)
@@ -1 +0,0 @@
1
- __version__ = "1.9.3"
File without changes