python-voiceio 1.12.0__tar.gz → 1.13.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 (228) hide show
  1. {python_voiceio-1.12.0/python_voiceio.egg-info → python_voiceio-1.13.0}/PKG-INFO +2 -2
  2. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/README.md +1 -1
  3. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/pyproject.toml +1 -1
  4. {python_voiceio-1.12.0 → python_voiceio-1.13.0/python_voiceio.egg-info}/PKG-INFO +2 -2
  5. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/python_voiceio.egg-info/SOURCES.txt +1 -0
  6. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_sherpa.py +6 -1
  7. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_tts.py +76 -0
  8. python_voiceio-1.13.0/voiceio/__init__.py +1 -0
  9. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/cli/models.py +4 -1
  10. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/config.py +2 -1
  11. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/core/decoders/catalog.py +18 -5
  12. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/core/decoders/fetch.py +18 -8
  13. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/health.py +4 -4
  14. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/server/runtime.py +1 -0
  15. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/tts/chain.py +4 -1
  16. python_voiceio-1.13.0/voiceio/tts/supertonic_engine.py +91 -0
  17. python_voiceio-1.12.0/voiceio/__init__.py +0 -1
  18. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/LICENSE +0 -0
  19. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/python_voiceio.egg-info/dependency_links.txt +0 -0
  20. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/python_voiceio.egg-info/entry_points.txt +0 -0
  21. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/python_voiceio.egg-info/requires.txt +0 -0
  22. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/python_voiceio.egg-info/top_level.txt +0 -0
  23. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/setup.cfg +0 -0
  24. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_app_wiring.py +0 -0
  25. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_audio_quality.py +0 -0
  26. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_audio_source.py +0 -0
  27. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_backend_probes.py +0 -0
  28. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_cancel.py +0 -0
  29. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_cli.py +0 -0
  30. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_clipboard_read.py +0 -0
  31. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_clipboard_typer.py +0 -0
  32. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_commands.py +0 -0
  33. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_concurrency_lockdown.py +0 -0
  34. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_config.py +0 -0
  35. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_context_terms.py +0 -0
  36. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_corrections.py +0 -0
  37. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_dead_mic.py +0 -0
  38. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_decoders.py +0 -0
  39. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_engine.py +0 -0
  40. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_evdev_uaccess.py +0 -0
  41. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_fallback.py +0 -0
  42. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_fcitx_bridge.py +0 -0
  43. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_feedback_stream.py +0 -0
  44. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_focus.py +0 -0
  45. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_health.py +0 -0
  46. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_hints.py +0 -0
  47. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_history.py +0 -0
  48. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_hotkey_modes.py +0 -0
  49. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_ibus_engine_limits.py +0 -0
  50. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_ibus_engine_listener.py +0 -0
  51. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_ibus_pending.py +0 -0
  52. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_ibus_pids.py +0 -0
  53. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_ibus_ping.py +0 -0
  54. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_ibus_session.py +0 -0
  55. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_ibus_textlimit.py +0 -0
  56. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_ibus_typer.py +0 -0
  57. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_idle_audio.py +0 -0
  58. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_learn_clean.py +0 -0
  59. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_learn_import.py +0 -0
  60. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_learn_language.py +0 -0
  61. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_learn_metrics.py +0 -0
  62. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_learn_pipeline.py +0 -0
  63. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_learn_quiet.py +0 -0
  64. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_learn_resume.py +0 -0
  65. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_learn_review.py +0 -0
  66. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_learn_targets.py +0 -0
  67. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_llm.py +0 -0
  68. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_llm_api.py +0 -0
  69. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_mic_gain.py +0 -0
  70. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_modifier_guard.py +0 -0
  71. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_numbers.py +0 -0
  72. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_paste_last.py +0 -0
  73. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_pipeline.py +0 -0
  74. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_platform.py +0 -0
  75. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_postcorrect.py +0 -0
  76. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_postprocess.py +0 -0
  77. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_prebuffer.py +0 -0
  78. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_presence.py +0 -0
  79. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_prompt.py +0 -0
  80. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_quiet_audio.py +0 -0
  81. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_recorder_integration.py +0 -0
  82. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_report.py +0 -0
  83. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_retention.py +0 -0
  84. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_robustness.py +0 -0
  85. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_security_hardening.py +0 -0
  86. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_server.py +0 -0
  87. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_service.py +0 -0
  88. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_setup.py +0 -0
  89. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_streaming.py +0 -0
  90. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_suggest.py +0 -0
  91. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_tokens.py +0 -0
  92. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_transcriber.py +0 -0
  93. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_typer_manager.py +0 -0
  94. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_vad.py +0 -0
  95. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_vocabulary.py +0 -0
  96. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_wordfreq.py +0 -0
  97. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/tests/test_worker_decode.py +0 -0
  98. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/__main__.py +0 -0
  99. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/app.py +0 -0
  100. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/audio_source.py +0 -0
  101. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/backends.py +0 -0
  102. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/cli/__init__.py +0 -0
  103. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/cli/configcmd.py +0 -0
  104. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/cli/corrections.py +0 -0
  105. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/cli/doctor.py +0 -0
  106. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/cli/history.py +0 -0
  107. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/cli/learn.py +0 -0
  108. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/cli/servicecmd.py +0 -0
  109. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/cli/uninstall.py +0 -0
  110. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/cli/vocab.py +0 -0
  111. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/clipboard_read.py +0 -0
  112. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/commands.py +0 -0
  113. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/consent.py +0 -0
  114. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/context_terms.py +0 -0
  115. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/core/__init__.py +0 -0
  116. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/core/bias.py +0 -0
  117. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/core/decoders/__init__.py +0 -0
  118. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/core/decoders/base.py +0 -0
  119. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/core/decoders/faster_whisper/__init__.py +0 -0
  120. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/core/decoders/faster_whisper/worker.py +0 -0
  121. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/core/decoders/lanes.py +0 -0
  122. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/core/decoders/sherpa.py +0 -0
  123. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/core/decoders/whispercpp.py +0 -0
  124. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/core/engine.py +0 -0
  125. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/core/pipeline.py +0 -0
  126. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/corrections.py +0 -0
  127. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/data/60-voiceio-uaccess.rules +0 -0
  128. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/data/__init__.py +0 -0
  129. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/demo.py +0 -0
  130. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/display.py +0 -0
  131. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/feedback.py +0 -0
  132. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/focus.py +0 -0
  133. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/hints.py +0 -0
  134. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/history.py +0 -0
  135. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/hotkeys/__init__.py +0 -0
  136. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/hotkeys/base.py +0 -0
  137. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/hotkeys/chain.py +0 -0
  138. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/hotkeys/evdev.py +0 -0
  139. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/hotkeys/pynput_backend.py +0 -0
  140. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/hotkeys/socket_backend.py +0 -0
  141. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/ibus/__init__.py +0 -0
  142. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/ibus/engine.py +0 -0
  143. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/ibus/install.py +0 -0
  144. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/ibus/pending.py +0 -0
  145. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/ibus/session.py +0 -0
  146. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/ibus/textlimit.py +0 -0
  147. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/idle_audio.py +0 -0
  148. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/learn/__init__.py +0 -0
  149. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/learn/augment.py +0 -0
  150. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/learn/clean.py +0 -0
  151. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/learn/evaluate.py +0 -0
  152. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/learn/importer.py +0 -0
  153. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/learn/metrics.py +0 -0
  154. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/learn/quiet.py +0 -0
  155. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/learn/review.py +0 -0
  156. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/learn/store.py +0 -0
  157. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/learn/teacher.py +0 -0
  158. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/learn/train.py +0 -0
  159. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/llm.py +0 -0
  160. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/llm_api.py +0 -0
  161. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/mic_gain.py +0 -0
  162. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/models/__init__.py +0 -0
  163. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/models/silero_vad.onnx +0 -0
  164. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/numbers.py +0 -0
  165. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/pidlock.py +0 -0
  166. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/platform.py +0 -0
  167. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/postcorrect.py +0 -0
  168. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/postprocess.py +0 -0
  169. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/power.py +0 -0
  170. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/presence.py +0 -0
  171. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/privacy.py +0 -0
  172. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/prompt.py +0 -0
  173. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/quiet_audio.py +0 -0
  174. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/recorder.py +0 -0
  175. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/report.py +0 -0
  176. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/retention.py +0 -0
  177. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/server/__init__.py +0 -0
  178. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/server/__main__.py +0 -0
  179. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/server/app.py +0 -0
  180. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/server/config.py +0 -0
  181. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/service.py +0 -0
  182. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/setup/__init__.py +0 -0
  183. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/setup/configfile.py +0 -0
  184. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/setup/extras.py +0 -0
  185. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/setup/hotkey.py +0 -0
  186. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/setup/noninteractive.py +0 -0
  187. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/setup/speech.py +0 -0
  188. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/setup/steps.py +0 -0
  189. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/setup/system.py +0 -0
  190. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/setup/ui.py +0 -0
  191. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/sounds/__init__.py +0 -0
  192. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/sounds/alert.wav +0 -0
  193. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/sounds/commit.wav +0 -0
  194. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/sounds/start.wav +0 -0
  195. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/sounds/stop.wav +0 -0
  196. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/streaming.py +0 -0
  197. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/suggest.py +0 -0
  198. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/tokens.py +0 -0
  199. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/tray/__init__.py +0 -0
  200. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/tray/_icons.py +0 -0
  201. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/tray/_indicator.py +0 -0
  202. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/tray/_pystray.py +0 -0
  203. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/tts/__init__.py +0 -0
  204. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/tts/base.py +0 -0
  205. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/tts/edge_engine.py +0 -0
  206. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/tts/espeak.py +0 -0
  207. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/tts/piper_engine.py +0 -0
  208. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/tts/player.py +0 -0
  209. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/typers/__init__.py +0 -0
  210. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/typers/base.py +0 -0
  211. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/typers/chain.py +0 -0
  212. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/typers/clipboard.py +0 -0
  213. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/typers/collecting.py +0 -0
  214. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/typers/fcitx_bridge.py +0 -0
  215. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/typers/ibus.py +0 -0
  216. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/typers/manager.py +0 -0
  217. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/typers/modifier_guard.py +0 -0
  218. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/typers/paste.py +0 -0
  219. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/typers/pynput_type.py +0 -0
  220. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/typers/wtype.py +0 -0
  221. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/typers/xdotool.py +0 -0
  222. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/typers/ydotool.py +0 -0
  223. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/ui.py +0 -0
  224. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/vad.py +0 -0
  225. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/vocab_stats.py +0 -0
  226. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/vocabulary.py +0 -0
  227. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/watchdog.py +0 -0
  228. {python_voiceio-1.12.0 → python_voiceio-1.13.0}/voiceio/wordfreq.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-voiceio
3
- Version: 1.12.0
3
+ Version: 1.13.0
4
4
  Summary: Voice dictation for Linux. Speak → text, locally, instantly.
5
5
  Author: Hugo Montenegro
6
6
  License-Expression: MIT
@@ -65,7 +65,7 @@ Dynamic: license-file
65
65
  **[Try it in your browser at voiceio.dev](https://voiceio.dev)**: no install, the model runs in the tab.
66
66
 
67
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, or NVIDIA's Parakeet (about 2x faster, with every name you use as a hotword), decodes your speech locally. No account, no server, no telemetry; logs never contain your words.
68
+ - **Runs on your machine.** Whisper, or NVIDIA's Parakeet (text lands ~0.7 s after you stop talking instead of ~4.4 s, with every name you use as a hotword), decodes your speech locally. No account, no server, no telemetry; logs never contain your words.
69
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
70
 
71
71
  ## Install
@@ -12,7 +12,7 @@
12
12
  **[Try it in your browser at voiceio.dev](https://voiceio.dev)**: no install, the model runs in the tab.
13
13
 
14
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, or NVIDIA's Parakeet (about 2x faster, with every name you use as a hotword), decodes your speech locally. No account, no server, no telemetry; logs never contain your words.
15
+ - **Runs on your machine.** Whisper, or NVIDIA's Parakeet (text lands ~0.7 s after you stop talking instead of ~4.4 s, with every name you use as a hotword), decodes your speech locally. No account, no server, no telemetry; logs never contain your words.
16
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
17
 
18
18
  ## Install
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "python-voiceio"
7
- version = "1.12.0"
7
+ version = "1.13.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.12.0
3
+ Version: 1.13.0
4
4
  Summary: Voice dictation for Linux. Speak → text, locally, instantly.
5
5
  Author: Hugo Montenegro
6
6
  License-Expression: MIT
@@ -65,7 +65,7 @@ Dynamic: license-file
65
65
  **[Try it in your browser at voiceio.dev](https://voiceio.dev)**: no install, the model runs in the tab.
66
66
 
67
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, or NVIDIA's Parakeet (about 2x faster, with every name you use as a hotword), decodes your speech locally. No account, no server, no telemetry; logs never contain your words.
68
+ - **Runs on your machine.** Whisper, or NVIDIA's Parakeet (text lands ~0.7 s after you stop talking instead of ~4.4 s, with every name you use as a hotword), decodes your speech locally. No account, no server, no telemetry; logs never contain your words.
69
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
70
 
71
71
  ## Install
@@ -208,6 +208,7 @@ voiceio/tts/edge_engine.py
208
208
  voiceio/tts/espeak.py
209
209
  voiceio/tts/piper_engine.py
210
210
  voiceio/tts/player.py
211
+ voiceio/tts/supertonic_engine.py
211
212
  voiceio/typers/__init__.py
212
213
  voiceio/typers/base.py
213
214
  voiceio/typers/chain.py
@@ -195,7 +195,8 @@ def test_default_is_parakeet_only_when_sherpa_has_the_fix(monkeypatch):
195
195
  assert catalog.default_model("en") == "small"
196
196
  monkeypatch.setattr(sherpa, "features", lambda: SherpaFeatures(True, True, False))
197
197
  assert catalog.default_model("en") == catalog.PARAKEET
198
- assert catalog.default_model("es") == "small" # Parakeet v2 is English only
198
+ assert catalog.default_model("es") == catalog.PARAKEET_V3
199
+ assert catalog.default_model("ja") == "small" # outside Parakeet v3's languages
199
200
 
200
201
 
201
202
  def test_config_resolves_unset_or_auto_name_but_an_explicit_choice_wins(monkeypatch, tmp_path):
@@ -343,6 +344,10 @@ def test_doctor_reports_the_decoder(monkeypatch):
343
344
  assert "aren't used" in _learning_status(cfg)["Voice model"]
344
345
  cfg.model.name = "small"
345
346
  assert catalog.PARAKEET in _decoder_status(cfg)["Decoder"]
347
+ cfg.model.language = "es"
348
+ assert catalog.PARAKEET_V3 in _decoder_status(cfg)["Decoder"]
349
+ cfg.model.language = "ja"
350
+ assert _decoder_status(cfg) == {}
346
351
 
347
352
 
348
353
  def test_eval_results_are_found_for_sherpa_models(monkeypatch, tmp_path):
@@ -423,3 +423,79 @@ def test_piper_shutdown_releases_voice():
423
423
  engine._voice = MagicMock()
424
424
  engine.shutdown()
425
425
  assert engine._voice is None
426
+
427
+
428
+ # ── supertonic tests ─────────────────────────────────────────────────
429
+
430
+ class _FakeGenerated:
431
+ samples = [0.0, 0.5, -1.5]
432
+ sample_rate = 44100
433
+
434
+
435
+ def _fake_sherpa(calls):
436
+ so = MagicMock()
437
+
438
+ class Tts:
439
+ def __init__(self, config):
440
+ calls["config"] = config
441
+
442
+ def generate(self, text, gen):
443
+ calls["text"], calls["gen"] = text, gen
444
+ return _FakeGenerated()
445
+
446
+ so.OfflineTts = Tts
447
+ so.GenerationConfig = lambda: type("G", (), {})()
448
+ return so
449
+
450
+
451
+ def _supertonic_dir(path):
452
+ from voiceio.tts import supertonic_engine as st
453
+ for stem in st._STEMS:
454
+ (path / f"{stem}.int8.onnx").write_bytes(b"")
455
+ for f in st._FILES:
456
+ (path / f).write_bytes(b"")
457
+ return path
458
+
459
+
460
+ def test_supertonic_probe_needs_supertonic_in_sherpa():
461
+ from voiceio.tts.supertonic_engine import SupertonicEngine
462
+ old = MagicMock(spec=[]) # a sherpa-onnx without OfflineTtsSupertonicModelConfig
463
+ with patch.dict("sys.modules", {"sherpa_onnx": old}):
464
+ assert not SupertonicEngine().probe().ok
465
+ with patch.dict("sys.modules", {"sherpa_onnx": MagicMock()}):
466
+ assert SupertonicEngine().probe().ok
467
+
468
+
469
+ def test_supertonic_reads_in_its_language_and_returns_int16(tmp_path):
470
+ from voiceio.tts import supertonic_engine as st
471
+ calls = {}
472
+ model = _supertonic_dir(tmp_path)
473
+ with patch.dict("sys.modules", {"sherpa_onnx": _fake_sherpa(calls)}), \
474
+ patch("voiceio.core.decoders.fetch.download_archive", return_value=model) as dl:
475
+ engine = st.SupertonicEngine(language="es")
476
+ audio, rate = engine.synthesize("hola", voice="", speed=0)
477
+ engine.synthesize("otra vez", voice="2", speed=1.2)
478
+ dl.assert_called_once() # loaded once, then reused
479
+ assert dl.call_args.args[0] == st.MODEL
480
+ assert calls["gen"].extra == {"lang": "es"}
481
+ assert calls["gen"].sid == 2 and calls["gen"].speed == 1.2
482
+ assert rate == 44100 and audio.dtype == np.int16
483
+ assert audio.tolist()[2] == -32767 # clipped, not wrapped
484
+
485
+
486
+ def test_supertonic_auto_language_reads_english():
487
+ from voiceio.tts.supertonic_engine import SupertonicEngine
488
+ assert SupertonicEngine(language="auto")._language == "en"
489
+ assert SupertonicEngine()._language == "en"
490
+
491
+
492
+ def test_supertonic_completeness_check(tmp_path):
493
+ from voiceio.tts import supertonic_engine as st
494
+ assert not st._complete(tmp_path)
495
+ assert st._complete(_supertonic_dir(tmp_path))
496
+
497
+
498
+ def test_chain_passes_language_to_supertonic():
499
+ from voiceio.tts import chain
500
+ engine = chain._create("supertonic", TTSConfig(engine="supertonic", language="de"))
501
+ assert engine.name == "supertonic" and engine._language == "de"
@@ -0,0 +1 @@
1
+ __version__ = "1.13.0"
@@ -180,4 +180,7 @@ def cmd_sherpa(args: argparse.Namespace) -> None:
180
180
  wheel = fetch.install_patched_sherpa()
181
181
  except OSError as e:
182
182
  raise SystemExit(str(e)) from e
183
- print(f"installed {wheel}\nnext: voiceio models use parakeet-tdt-0.6b-v2")
183
+ from voiceio import config
184
+ from voiceio.core.decoders.catalog import PARAKEET, PARAKEET_V3
185
+ name = PARAKEET if config.load().model.language == "en" else PARAKEET_V3
186
+ print(f"installed {wheel}\nnext: voiceio models use {name}")
@@ -248,11 +248,12 @@ class PostCorrectConfig:
248
248
  @dataclass
249
249
  class TTSConfig:
250
250
  enabled: bool = True
251
- engine: str = "auto" # "auto" | "piper" | "espeak" | "edge-tts"
251
+ engine: str = "auto" # "auto" | "piper" | "supertonic" | "espeak" | "edge-tts"
252
252
  hotkey: str = "ctrl+alt+s" # "s" for speak
253
253
  voice: str = "" # empty = engine default
254
254
  speed: float = 1.0 # 0.5–2.0
255
255
  model: str = "" # piper model name, empty = default
256
+ language: str = "" # supertonic: language to read in (en, es, de…), empty = en
256
257
 
257
258
 
258
259
  @dataclass
@@ -15,6 +15,11 @@ from voiceio.core.decoders.base import BiasMode
15
15
 
16
16
  WHISPER_DEFAULT = "small"
17
17
  PARAKEET = "parakeet-tdt-0.6b-v2"
18
+ PARAKEET_V3 = "parakeet-tdt-0.6b-v3"
19
+ # The 25 European languages Parakeet v3 was trained on. It detects which one
20
+ # is spoken by itself, so `[model] language` only picks the model.
21
+ PARAKEET_V3_LANGUAGES = frozenset(
22
+ "bg hr cs da nl en et fi fr de el hu it lv lt mt pl pt ro sk sl es sv ru uk".split())
18
23
 
19
24
 
20
25
  @dataclass(frozen=True)
@@ -43,11 +48,18 @@ _SHERPA_RELEASES = "https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-
43
48
  # unlimited hotwords), and Qwen3-ASR (free-text context biasing).
44
49
  CATALOG: tuple[ModelInfo, ...] = (
45
50
  ModelInfo(PARAKEET, "sherpa-onnx", "600M", "CC-BY-4.0", BiasMode.UNBOUNDED, languages="en",
46
- notes="default when sherpa-onnx has the NeMo TDT beam-search fix: ~2x faster "
47
- "than whisper-small, every proper noun of your vocabulary as a hotword",
51
+ notes="default when sherpa-onnx has the NeMo TDT beam-search fix: text ~0.7s after you stop "
52
+ "talking vs ~4.4s with Whisper, every proper noun of your vocabulary as a hotword",
48
53
  url=f"{_SHERPA_RELEASES}/sherpa-onnx-nemo-parakeet-tdt-0.6b-v2-int8.tar.bz2",
49
54
  sha256="157c157bc51155e03e37d2466522a3a737dd9c72bb25f36eb18912964161e1ad",
50
55
  size_mb=482),
56
+ ModelInfo(PARAKEET_V3, "sherpa-onnx", "600M", "CC-BY-4.0", BiasMode.UNBOUNDED,
57
+ languages=",".join(sorted(PARAKEET_V3_LANGUAGES)),
58
+ notes="the default for the other 24 European languages it covers; v2 stays the "
59
+ "English default (trained on English only, lower English WER)",
60
+ url=f"{_SHERPA_RELEASES}/sherpa-onnx-nemo-parakeet-tdt-0.6b-v3-int8.tar.bz2",
61
+ sha256="5793d0fd397c5778d2cf2126994d58e9d56b1be7c04d13c7a15bb1b4eafb16bf",
62
+ size_mb=487),
51
63
  ModelInfo("small", "faster-whisper", "244M", "MIT", BiasMode.HOTWORDS | BiasMode.PROMPT,
52
64
  notes="~5x realtime on a laptop CPU; hotwords cap ~35 terms; "
53
65
  "the model `voiceio learn` fine-tunes"),
@@ -97,10 +109,11 @@ def default_model(language: str = "en") -> str:
97
109
  Parakeet once the installed sherpa-onnx has the NeMo TDT
98
110
  modified_beam_search fix (without it, hotwords lose speech — see
99
111
  CLAUDE.md "Model choice"), so the default flips by itself when upstream
100
- ships the fix. English only: other languages keep Whisper.
112
+ ships the fix. English gets v2; the other languages Parakeet v3 covers get
113
+ v3; the rest keep Whisper.
101
114
  """
102
- if language == "en":
115
+ if language in PARAKEET_V3_LANGUAGES:
103
116
  from voiceio.core.decoders.sherpa import features
104
117
  if features().length_reward:
105
- return PARAKEET
118
+ return PARAKEET if language == "en" else PARAKEET_V3
106
119
  return WHISPER_DEFAULT
@@ -68,19 +68,29 @@ def download(name: str, *, progress: Callable[[int, int], None] | None = None) -
68
68
  m: ModelInfo | None = info(name)
69
69
  if m is None or not m.url:
70
70
  raise OSError(f"{name} is not a model voiceio downloads itself")
71
+ return download_archive(name, m.url, m.sha256, m.size_mb, is_complete,
72
+ what="encoder/decoder/joiner/tokens.txt", progress=progress)
73
+
74
+
75
+ def download_archive(name: str, url: str, sha256: str, size_mb: int,
76
+ complete: Callable[[Path], bool], *, what: str,
77
+ progress: Callable[[int, int], None] | None = None) -> Path:
78
+ """Fetch, verify and unpack a sherpa-onnx release tarball as
79
+ `models_dir()/name`. `complete` says whether a directory holds the whole
80
+ model (`what` names its files for the error)."""
71
81
  dest = model_path(name)
72
- if is_complete(dest):
82
+ if complete(dest):
73
83
  return dest
74
84
  root = models_dir()
75
85
  root.mkdir(parents=True, exist_ok=True)
76
86
  part = root / f".{name}.{os.getpid()}.part"
77
87
  staging = root / f".{name}.{os.getpid()}.unpack"
78
- log.info("Downloading %s (%d MB) from %s", name, m.size_mb, m.url)
88
+ log.info("Downloading %s (%d MB) from %s", name, size_mb, url)
79
89
  try:
80
90
  digest = hashlib.sha256()
81
- req = urllib.request.Request(m.url, headers={"User-Agent": "voiceio"})
91
+ req = urllib.request.Request(url, headers={"User-Agent": "voiceio"})
82
92
  with urllib.request.urlopen(req, timeout=_TIMEOUT) as resp, open(part, "wb") as out:
83
- total = int(resp.headers.get("Content-Length") or m.size_mb * 1_000_000)
93
+ total = int(resp.headers.get("Content-Length") or size_mb * 1_000_000)
84
94
  done = 0
85
95
  while block := resp.read(_BLOCK):
86
96
  out.write(block)
@@ -88,12 +98,12 @@ def download(name: str, *, progress: Callable[[int, int], None] | None = None) -
88
98
  done += len(block)
89
99
  if progress is not None:
90
100
  progress(done, total)
91
- if digest.hexdigest() != m.sha256:
101
+ if digest.hexdigest() != sha256:
92
102
  raise OSError(f"{name}: checksum mismatch (got {digest.hexdigest()[:12]}…, "
93
- f"want {m.sha256[:12]}…) — download again")
103
+ f"want {sha256[:12]}…) — download again")
94
104
  _unpack(part, staging)
95
- if not is_complete(staging):
96
- raise OSError(f"{name}: the archive has no encoder/decoder/joiner/tokens.txt")
105
+ if not complete(staging):
106
+ raise OSError(f"{name}: the archive has no {what}")
97
107
  shutil.rmtree(dest, ignore_errors=True) # an incomplete earlier copy
98
108
  os.replace(staging, dest)
99
109
  except OSError:
@@ -244,7 +244,7 @@ def _check_features() -> FeatureGroups:
244
244
  def _decoder_status(cfg) -> dict[str, str]:
245
245
  """For a sherpa-onnx model: whether it can be biased, and why not. For
246
246
  Whisper: whether Parakeet would do better here."""
247
- from voiceio.core.decoders.catalog import PARAKEET, runtime_of
247
+ from voiceio.core.decoders.catalog import WHISPER_DEFAULT, default_model, runtime_of
248
248
  from voiceio.core.decoders.sherpa import features
249
249
  f = features()
250
250
  if runtime_of(cfg.model.name) == "sherpa-onnx":
@@ -256,9 +256,9 @@ def _decoder_status(cfg) -> dict[str, str]:
256
256
  how = "pre-tokenized" if f.pretokenized_hotwords else \
257
257
  "sherpa-tokenized, misses many names — fix: voiceio models sherpa"
258
258
  return {"Decoder": f"beam search, every proper noun as a hotword ({how})"}
259
- if f.length_reward and cfg.model.language == "en":
260
- return {"Decoder": f"Whisper; {PARAKEET} is ~2x faster here: "
261
- f"voiceio models use {PARAKEET}"}
259
+ if f.length_reward and (best := default_model(cfg.model.language)) != WHISPER_DEFAULT:
260
+ return {"Decoder": f"Whisper; {best} would put your text down several times "
261
+ f"sooner: voiceio models use {best}"}
262
262
  return {}
263
263
 
264
264
 
@@ -230,6 +230,7 @@ class ServerRuntime:
230
230
  voice=self.cfg.tts_voice,
231
231
  speed=self.cfg.tts_speed,
232
232
  model=self.cfg.tts_model,
233
+ language=self.model_cfg.language,
233
234
  )
234
235
  try:
235
236
  self._tts = tts_pkg.select(
@@ -13,10 +13,11 @@ _ENGINES = {
13
13
  "espeak": "voiceio.tts.espeak:EspeakEngine",
14
14
  "piper": "voiceio.tts.piper_engine:PiperEngine",
15
15
  "edge-tts": "voiceio.tts.edge_engine:EdgeEngine",
16
+ "supertonic": "voiceio.tts.supertonic_engine:SupertonicEngine",
16
17
  }
17
18
 
18
19
  # Preference order when engine = "auto"
19
- _AUTO_ORDER = ["piper", "edge-tts", "espeak"]
20
+ _AUTO_ORDER = ["piper", "supertonic", "edge-tts", "espeak"]
20
21
 
21
22
  # Engines that send the text off this machine to be synthesized. Auto-selection
22
23
  # can silently land on one (edge-tts needs no install, so it probes OK wherever
@@ -35,6 +36,8 @@ def _create(name: str, cfg: TTSConfig):
35
36
  cls = getattr(module, cls_name)
36
37
  if name == "piper":
37
38
  return cls(model=cfg.model)
39
+ if name == "supertonic":
40
+ return cls(language=cfg.language)
38
41
  return cls()
39
42
 
40
43
 
@@ -0,0 +1,91 @@
1
+ """Supertonic 3 TTS engine — neural, offline, 31 languages, via sherpa-onnx.
2
+
3
+ Faster than real time on a laptop CPU (RTF ~0.5 at 2 threads) in ~300 MB,
4
+ where Kokoro and Pocket TTS ran slower than real time on the same box. It
5
+ renders a whole sentence before playing, so the first audio comes after
6
+ ~2 s: worth it for reading text aloud, not for snappy one-word replies, which
7
+ is why Piper stays first in the auto chain. Weights are OpenRAIL-M.
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import logging
12
+ from pathlib import Path
13
+
14
+ import numpy as np
15
+
16
+ from voiceio.backends import ProbeResult
17
+
18
+ log = logging.getLogger(__name__)
19
+
20
+ MODEL = "sherpa-onnx-supertonic-3-tts-int8-2026-05-11"
21
+ _URL = f"https://github.com/k2-fsa/sherpa-onnx/releases/download/tts-models/{MODEL}.tar.bz2"
22
+ _SHA256 = "82fa96f91c4ef8abaae3a14a3f4153facf88bed821d1f7331cec2700f432c427"
23
+ _SIZE_MB = 129
24
+ _STEMS = ("duration_predictor", "text_encoder", "vector_estimator", "vocoder")
25
+ _FILES = ("tts.json", "unicode_indexer.bin", "voice.bin")
26
+ _HINT = "pip install 'python-voiceio[sherpa]' (then: voiceio models sherpa)"
27
+
28
+
29
+ def _complete(path: Path) -> bool:
30
+ return all((path / f).is_file() for f in _FILES) and all(
31
+ any(path.glob(f"{stem}*.onnx")) for stem in _STEMS)
32
+
33
+
34
+ def _onnx(path: Path, stem: str) -> str:
35
+ return str(sorted(path.glob(f"{stem}*.onnx"))[0])
36
+
37
+
38
+ class SupertonicEngine:
39
+ name = "supertonic"
40
+
41
+ def __init__(self, language: str = "", threads: int = 2):
42
+ # "auto" can't name a language to read in; English is the safe reading.
43
+ self._language = language if language not in ("", "auto") else "en"
44
+ self._threads = threads
45
+ self._tts = None # lazy: the first synthesize() loads (and maybe fetches)
46
+
47
+ def probe(self) -> ProbeResult:
48
+ try:
49
+ import sherpa_onnx
50
+ except ImportError as e:
51
+ return ProbeResult(ok=False, reason=f"sherpa-onnx missing: {e}", fix_hint=_HINT)
52
+ if not hasattr(sherpa_onnx, "OfflineTtsSupertonicModelConfig"):
53
+ return ProbeResult(ok=False, reason="this sherpa-onnx has no Supertonic support",
54
+ fix_hint=_HINT)
55
+ return ProbeResult(ok=True)
56
+
57
+ def _ensure(self) -> None:
58
+ if self._tts is not None:
59
+ return
60
+ import sherpa_onnx as so
61
+
62
+ from voiceio.core.decoders import fetch
63
+ path = fetch.download_archive(
64
+ MODEL, _URL, _SHA256, _SIZE_MB, _complete,
65
+ what=", ".join((*_STEMS, *_FILES)))
66
+ model = so.OfflineTtsModelConfig(num_threads=self._threads, provider="cpu")
67
+ model.supertonic = so.OfflineTtsSupertonicModelConfig(
68
+ duration_predictor=_onnx(path, "duration_predictor"),
69
+ text_encoder=_onnx(path, "text_encoder"),
70
+ vector_estimator=_onnx(path, "vector_estimator"),
71
+ vocoder=_onnx(path, "vocoder"),
72
+ tts_json=str(path / "tts.json"),
73
+ unicode_indexer=str(path / "unicode_indexer.bin"),
74
+ voice_style=str(path / "voice.bin"))
75
+ log.info("TTS: loading Supertonic 3 (%s)…", self._language)
76
+ self._tts = so.OfflineTts(so.OfflineTtsConfig(model=model, max_num_sentences=1))
77
+
78
+ def synthesize(self, text: str, voice: str, speed: float) -> tuple[np.ndarray, int]:
79
+ import sherpa_onnx as so
80
+
81
+ self._ensure()
82
+ gen = so.GenerationConfig()
83
+ gen.sid = int(voice) if voice.isdigit() else 0
84
+ gen.speed = speed if speed > 0 else 1.0
85
+ gen.extra = {"lang": self._language}
86
+ out = self._tts.generate(text, gen)
87
+ samples = np.asarray(out.samples, dtype=np.float32)
88
+ return (np.clip(samples, -1.0, 1.0) * 32767).astype(np.int16), out.sample_rate
89
+
90
+ def shutdown(self) -> None:
91
+ self._tts = None
@@ -1 +0,0 @@
1
- __version__ = "1.12.0"
File without changes