ownvoice 0.1.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.
@@ -0,0 +1,28 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .eggs/
6
+ build/
7
+ dist/
8
+
9
+ # Virtual environments
10
+ .venv/
11
+ venv/
12
+ env/
13
+
14
+ # Test / tooling caches
15
+ .pytest_cache/
16
+ .mypy_cache/
17
+ .ruff_cache/
18
+ .coverage
19
+ htmlcov/
20
+
21
+ # OwnVoice runtime output (adapters, generated audio) -- never commit real voice data or trained weights
22
+ ownvoice-adapter/
23
+ *.safetensors
24
+ ownvoice-output.wav
25
+ *.wav
26
+
27
+ # OS
28
+ .DS_Store
ownvoice-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rudrendu Paul
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,183 @@
1
+ Metadata-Version: 2.4
2
+ Name: ownvoice
3
+ Version: 0.1.0
4
+ Summary: Train a LoRA voice adapter for pocket-tts and own the resulting model, no API lock-in.
5
+ Project-URL: Homepage, https://github.com/RudrenduPaul/ownvoice
6
+ Project-URL: Repository, https://github.com/RudrenduPaul/ownvoice
7
+ Project-URL: Issues, https://github.com/RudrenduPaul/ownvoice/issues
8
+ Author: Rudrendu Paul
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: fine-tuning,lora,peft,pocket-tts,text-to-speech,tts,voice-cloning
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Multimedia :: Sound/Audio :: Speech
21
+ Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
+ Requires-Python: >=3.11
23
+ Requires-Dist: numba>=0.59
24
+ Requires-Dist: peft>=0.11.0
25
+ Requires-Dist: pocket-tts>=0.1.0
26
+ Requires-Dist: resemblyzer>=0.1.4
27
+ Requires-Dist: safetensors>=0.4.0
28
+ Requires-Dist: soundfile>=0.12.0
29
+ Requires-Dist: torch>=2.5
30
+ Requires-Dist: torchaudio>=2.5
31
+ Requires-Dist: typer>=0.12.0
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest-mock>=3.14.0; extra == 'dev'
34
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
35
+ Description-Content-Type: text/markdown
36
+
37
+ # OwnVoice
38
+
39
+ Train a LoRA voice adapter for [pocket-tts](https://github.com/kyutai-labs/pocket-tts) and keep the result: a file on your own disk, not an API subscription.
40
+
41
+ ## Why this exists
42
+
43
+ pocket-tts is a genuinely good, MIT-licensed, CPU-capable local text-to-speech model from Kyutai. Its own maintainers have been clear that fine-tuning code isn't coming any time soon: on [issue #30](https://github.com/kyutai-labs/pocket-tts/issues/30), maintainer @vvolhejn wrote "We are not planning to release fine-tuning code for our TTS and STT models in the near future," and 18 people reacted to that thread asking for exactly this. OwnVoice is a small, standalone CLI that fills that specific gap: point it at a handful of your own voice recordings, and it trains a LoRA adapter you keep and run yourself.
44
+
45
+ It is not a hosted service, it has no billing, and it does not track usage. It is a training script, an inference script, and a scoring script, wired together behind three CLI commands.
46
+
47
+ ## What OwnVoice is not
48
+
49
+ pocket-tts already ships zero-shot voice cloning out of the box: pass a `.wav` file to `--voice` (or call `get_state_for_audio_prompt()` from Python) and it clones that voice with no training step at all. If that is all you need, use pocket-tts directly, it is simpler and faster.
50
+
51
+ OwnVoice exists for a narrower case: baking a voice permanently into trained weights, so generation no longer depends on distributing or re-processing a reference audio clip at runtime, with (based on the training objective, not yet independently benchmarked at scale) more consistent output across many generations than a single-clip zero-shot embedding tends to produce. That is the specific gap the 18 reactors on issue #30 were describing, and it is the only thing OwnVoice adds on top of what pocket-tts already does well.
52
+
53
+ ## Install
54
+
55
+ Requires Python 3.11 or newer.
56
+
57
+ ```bash
58
+ pip install git+https://github.com/RudrenduPaul/ownvoice
59
+ ```
60
+
61
+ **npx / agent-native environments:** OwnVoice is a Python/PyTorch CLI, so the npm package below is a thin wrapper, not a Node reimplementation. It bootstraps into the real CLI via [`uv`](https://docs.astral.sh/uv/) or `pipx`, whichever is already on `PATH` -- useful for coding-agent sandboxes and CI runners that default to a Node toolchain. **Not yet published to the npm registry** (coming soon, tracked alongside the PyPI release):
62
+
63
+ ```bash
64
+ npx ownvoice check
65
+ ```
66
+
67
+ **Torch and CUDA:** `ownvoice check` (see below) needs no GPU at all and runs on CPU, matching pocket-tts's own CPU-capable design. Training a real adapter is much faster on an NVIDIA GPU. If you have one, install the CUDA build of PyTorch first by following [pytorch.org/get-started/locally](https://pytorch.org/get-started/locally/), then install OwnVoice on top of it, so `pip` does not silently pull the CPU-only wheel instead. On Apple Silicon or a CPU-only machine, the default `pip install` of torch is fine: `ownvoice check` and `ownvoice infer` will run normally, `ownvoice train` will just take longer per epoch.
68
+
69
+ ## Quickstart
70
+
71
+ ### 1. `ownvoice check`, the free Day-0 validation
72
+
73
+ Before recording anything or renting a GPU, confirm that PEFT's LoRA injection actually works against pocket-tts's real model structure. This is entirely free: CPU only, no training, no GPU.
74
+
75
+ ```
76
+ $ ownvoice check
77
+ [ownvoice check] PASS: PEFT LoRA injection succeeded against pocket-tts's flow_lm module (target_modules="all-linear").
78
+ ```
79
+
80
+ If it fails, OwnVoice prints the model's real module tree instead of a raw stack trace, so you can see exactly what did not match and report it precisely:
81
+
82
+ ```
83
+ $ ownvoice check
84
+ [ownvoice check] FAIL: PEFT LoRA injection failed against pocket-tts's flow_lm module structure: <error detail>. Please post an honest blocker (this error plus the module tree above) as a comment on https://github.com/kyutai-labs/pocket-tts/issues/30 rather than working around it silently, that issue is exactly where this gap needs to be visible.
85
+
86
+ Module tree (for debugging / for the issue #30 blocker post):
87
+ <root>: FlowLMModel
88
+ input_linear: Linear
89
+ transformer: StreamingTransformer
90
+ transformer.layers.0.self_attn.in_proj: Linear
91
+ transformer.layers.0.self_attn.out_proj: Linear
92
+ ...
93
+ ```
94
+
95
+ ### 2. `ownvoice train`
96
+
97
+ Record 5 to 10 minutes of clean audio of the voice you want to train (your own voice, with your own consent, see Consent and misuse below), split into a few `.wav` clips in one directory, then point OwnVoice at it:
98
+
99
+ ```
100
+ $ ownvoice train --voice-clips ./my-voice-clips
101
+ [ownvoice train] USABLE ADAPTER
102
+ Usable adapter (similarity 0.812 >= 0.75). Try it now:
103
+ ownvoice infer --adapter ownvoice-adapter/adapter.safetensors --text "This is my own voice, trained with OwnVoice."
104
+ ```
105
+
106
+ Only `--voice-clips` is required. Every other flag has a sensible default: `--out` (`./ownvoice-adapter/`), `--epochs` (10), `--lora-rank` (8), `--lora-alpha` (16), `--lora-dropout` (0.05), `--learning-rate` (1e-4), and `--eval-text` (the sentence synthesized to compute the similarity score).
107
+
108
+ A run that finishes but does not clear the similarity bar still exits `0`. It is a labeled result with a concrete next step, not a crash:
109
+
110
+ ```
111
+ $ ownvoice train --voice-clips ./my-voice-clips
112
+ [ownvoice train] BELOW THRESHOLD
113
+ Below threshold (similarity 0.612 < 0.75). The adapter was still saved, try more/cleaner voice clips, more epochs, or a higher --lora-rank, then re-run. You can still listen to it:
114
+ ownvoice infer --adapter ownvoice-adapter/adapter.safetensors --text "This is my own voice, trained with OwnVoice."
115
+ ```
116
+
117
+ Only a data-loading problem (no usable clips) or a caught PEFT-injection failure exits non-zero. Every successful run writes `adapter.safetensors` and `metadata.json` (training config, the similarity score, a timestamp) to the output directory: two files you keep, with no server round-trip needed to use them again.
118
+
119
+ ### 3. `ownvoice infer`
120
+
121
+ ```
122
+ $ ownvoice infer --adapter ownvoice-adapter/adapter.safetensors --text "Hello, this is my own voice."
123
+ [ownvoice infer] Wrote ownvoice-output.wav
124
+ ```
125
+
126
+ Every subcommand also supports `--json` for a structured, machine-parseable output mode, useful if a script or an agent is calling `ownvoice` programmatically instead of a person reading the terminal:
127
+
128
+ ```
129
+ $ ownvoice check --json
130
+ {"success": true, "message": "PEFT LoRA injection succeeded against pocket-tts's flow_lm module (target_modules=\"all-linear\").", "module_tree": null}
131
+ ```
132
+
133
+ ## How it works
134
+
135
+ ```
136
+ voice clips (wav)
137
+ |
138
+ v
139
+ data.py --validate format/duration--> clean clip set
140
+ |
141
+ v
142
+ train.py --PEFT LoRA (target_modules="all-linear")--> adapter.safetensors + metadata.json
143
+ |
144
+ v
145
+ infer.py --generate test utterance--> synthesized audio
146
+ |
147
+ v
148
+ score.py --resample to 16kHz mono--> Resemblyzer cosine similarity
149
+ |
150
+ v
151
+ CLI report (>= 0.75 = usable adapter, below triggers a labeled next-step message)
152
+ ```
153
+
154
+ `ownvoice/data.py` loads and validates the voice-clip directory. `ownvoice/train.py` loads pocket-tts's frozen base model, injects a LoRA adapter into its `flow_lm` transformer with PEFT (`target_modules="all-linear"`), runs the training loop, and saves the adapter plus a manifest. `ownvoice/infer.py` loads a saved adapter back onto the base model and generates speech. `ownvoice/score.py` resamples audio to 16kHz mono with `torchaudio.transforms.Resample` and scores speaker similarity with [Resemblyzer](https://github.com/resemble-ai/Resemblyzer).
155
+
156
+ OwnVoice is intentionally single-model: it wraps pocket-tts only, with no abstraction layer for a second base model, since none is in scope.
157
+
158
+ ## Consent and misuse
159
+
160
+ This tool clones a voice from audio you have the right to use. Do not clone someone else's voice, or a public figure's voice, without their explicit consent. OwnVoice ships no bulk-generation or auto-scaling feature in this version, keeping the blast radius of any single misuse case small.
161
+
162
+ ## Setup-time benchmark vs comparable tools
163
+
164
+ | Tool | Time to first working setup | Notable design choice | Source |
165
+ |---|---|---|---|
166
+ | [kokoro-tts](https://github.com/nazdridoy/kokoro-tts) | under 2 minutes | `pip install git+...`, instant CLI synthesis, no fine-tuning | kokoro-tts README |
167
+ | [Unsloth](https://unsloth.ai) | under 1 minute to start a run | one-command training start (`uv pip install`) | Unsloth docs |
168
+ | [pocket-tts](https://github.com/kyutai-labs/pocket-tts) | seconds | `--voice <wav>` zero-shot cloning, no training available | pocket-tts README |
169
+ | **OwnVoice** | under 2 minutes to a confirmed-working training environment | `ownvoice check`: free, instant, CPU-only PEFT-compatibility validation before spending anything on a GPU | this repo |
170
+
171
+ OwnVoice's own training run is real GPU time, honestly labeled and not hidden behind a fake progress bar, the same category norm Unsloth uses. What OwnVoice compresses to under two minutes is everything *before* that: confirming your environment actually works.
172
+
173
+ ## Implementation status
174
+
175
+ This is a young v0.1. `ownvoice check`, the CLI argument parsing, voice-clip validation, the similarity scoring math, and the adapter/manifest save and load path are implemented and covered by the test suite (`pytest`). LoRA injection was verified structurally against pocket-tts's real source and then confirmed for real: `ownvoice check` was run against pocket-tts's actual downloaded weights, on CPU, and PEFT's `target_modules="all-linear"` injection genuinely succeeded. The full training and generation path has since been verified end to end for real too: a real 2-epoch LoRA training run against loaded pocket-tts weights produced a finite, non-NaN flow-matching loss, and the resulting adapter produced a real, non-silent generated `.wav` file via `ownvoice infer`. That validation surfaced two real gaps in the naive approach and fixed them: (1) pocket-tts's published, inference-only PyPI package does not actually expose a way to compute the training loss through `FlowLMModel.forward()` despite its own docstring claiming otherwise, so OwnVoice computes the flow-matching loss directly from `flow_lm`'s real submodules instead; (2) swapping `base_model.flow_lm` to the PEFT-wrapped model before calling `generate_audio()` breaks pocket-tts's internal KV-cache state lookup -- no swap is needed at all, since PEFT's LoRA injection already mutates `base_model.flow_lm` in place. One real, external limitation to know about: the publicly downloadable pocket-tts weights (`kyutai/pocket-tts-without-voice-cloning`) refuse a raw reference-clip path/URL outright; OwnVoice works around this by pre-loading and resampling the clip itself, but voice-cloning fidelity from that checkpoint is a known limitation of the base model, not an OwnVoice bug -- for kyutai's best-quality cloning weights, request gated access at [huggingface.co/kyutai/pocket-tts](https://huggingface.co/kyutai/pocket-tts). Run `ownvoice check` yourself and read the source before trusting any of it further, that is the right amount of skepticism for a v0.1.
176
+
177
+ ## Contributing
178
+
179
+ Issues and PRs welcome, MIT licensed throughout. If you want to help close the actual gap this project targets, the most useful contribution is upstream: a lightweight LoRA-adapter training script contributed back to [kyutai-labs/pocket-tts](https://github.com/kyutai-labs/pocket-tts) itself, discussed on [issue #30](https://github.com/kyutai-labs/pocket-tts/issues/30).
180
+
181
+ ## License
182
+
183
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,147 @@
1
+ # OwnVoice
2
+
3
+ Train a LoRA voice adapter for [pocket-tts](https://github.com/kyutai-labs/pocket-tts) and keep the result: a file on your own disk, not an API subscription.
4
+
5
+ ## Why this exists
6
+
7
+ pocket-tts is a genuinely good, MIT-licensed, CPU-capable local text-to-speech model from Kyutai. Its own maintainers have been clear that fine-tuning code isn't coming any time soon: on [issue #30](https://github.com/kyutai-labs/pocket-tts/issues/30), maintainer @vvolhejn wrote "We are not planning to release fine-tuning code for our TTS and STT models in the near future," and 18 people reacted to that thread asking for exactly this. OwnVoice is a small, standalone CLI that fills that specific gap: point it at a handful of your own voice recordings, and it trains a LoRA adapter you keep and run yourself.
8
+
9
+ It is not a hosted service, it has no billing, and it does not track usage. It is a training script, an inference script, and a scoring script, wired together behind three CLI commands.
10
+
11
+ ## What OwnVoice is not
12
+
13
+ pocket-tts already ships zero-shot voice cloning out of the box: pass a `.wav` file to `--voice` (or call `get_state_for_audio_prompt()` from Python) and it clones that voice with no training step at all. If that is all you need, use pocket-tts directly, it is simpler and faster.
14
+
15
+ OwnVoice exists for a narrower case: baking a voice permanently into trained weights, so generation no longer depends on distributing or re-processing a reference audio clip at runtime, with (based on the training objective, not yet independently benchmarked at scale) more consistent output across many generations than a single-clip zero-shot embedding tends to produce. That is the specific gap the 18 reactors on issue #30 were describing, and it is the only thing OwnVoice adds on top of what pocket-tts already does well.
16
+
17
+ ## Install
18
+
19
+ Requires Python 3.11 or newer.
20
+
21
+ ```bash
22
+ pip install git+https://github.com/RudrenduPaul/ownvoice
23
+ ```
24
+
25
+ **npx / agent-native environments:** OwnVoice is a Python/PyTorch CLI, so the npm package below is a thin wrapper, not a Node reimplementation. It bootstraps into the real CLI via [`uv`](https://docs.astral.sh/uv/) or `pipx`, whichever is already on `PATH` -- useful for coding-agent sandboxes and CI runners that default to a Node toolchain. **Not yet published to the npm registry** (coming soon, tracked alongside the PyPI release):
26
+
27
+ ```bash
28
+ npx ownvoice check
29
+ ```
30
+
31
+ **Torch and CUDA:** `ownvoice check` (see below) needs no GPU at all and runs on CPU, matching pocket-tts's own CPU-capable design. Training a real adapter is much faster on an NVIDIA GPU. If you have one, install the CUDA build of PyTorch first by following [pytorch.org/get-started/locally](https://pytorch.org/get-started/locally/), then install OwnVoice on top of it, so `pip` does not silently pull the CPU-only wheel instead. On Apple Silicon or a CPU-only machine, the default `pip install` of torch is fine: `ownvoice check` and `ownvoice infer` will run normally, `ownvoice train` will just take longer per epoch.
32
+
33
+ ## Quickstart
34
+
35
+ ### 1. `ownvoice check`, the free Day-0 validation
36
+
37
+ Before recording anything or renting a GPU, confirm that PEFT's LoRA injection actually works against pocket-tts's real model structure. This is entirely free: CPU only, no training, no GPU.
38
+
39
+ ```
40
+ $ ownvoice check
41
+ [ownvoice check] PASS: PEFT LoRA injection succeeded against pocket-tts's flow_lm module (target_modules="all-linear").
42
+ ```
43
+
44
+ If it fails, OwnVoice prints the model's real module tree instead of a raw stack trace, so you can see exactly what did not match and report it precisely:
45
+
46
+ ```
47
+ $ ownvoice check
48
+ [ownvoice check] FAIL: PEFT LoRA injection failed against pocket-tts's flow_lm module structure: <error detail>. Please post an honest blocker (this error plus the module tree above) as a comment on https://github.com/kyutai-labs/pocket-tts/issues/30 rather than working around it silently, that issue is exactly where this gap needs to be visible.
49
+
50
+ Module tree (for debugging / for the issue #30 blocker post):
51
+ <root>: FlowLMModel
52
+ input_linear: Linear
53
+ transformer: StreamingTransformer
54
+ transformer.layers.0.self_attn.in_proj: Linear
55
+ transformer.layers.0.self_attn.out_proj: Linear
56
+ ...
57
+ ```
58
+
59
+ ### 2. `ownvoice train`
60
+
61
+ Record 5 to 10 minutes of clean audio of the voice you want to train (your own voice, with your own consent, see Consent and misuse below), split into a few `.wav` clips in one directory, then point OwnVoice at it:
62
+
63
+ ```
64
+ $ ownvoice train --voice-clips ./my-voice-clips
65
+ [ownvoice train] USABLE ADAPTER
66
+ Usable adapter (similarity 0.812 >= 0.75). Try it now:
67
+ ownvoice infer --adapter ownvoice-adapter/adapter.safetensors --text "This is my own voice, trained with OwnVoice."
68
+ ```
69
+
70
+ Only `--voice-clips` is required. Every other flag has a sensible default: `--out` (`./ownvoice-adapter/`), `--epochs` (10), `--lora-rank` (8), `--lora-alpha` (16), `--lora-dropout` (0.05), `--learning-rate` (1e-4), and `--eval-text` (the sentence synthesized to compute the similarity score).
71
+
72
+ A run that finishes but does not clear the similarity bar still exits `0`. It is a labeled result with a concrete next step, not a crash:
73
+
74
+ ```
75
+ $ ownvoice train --voice-clips ./my-voice-clips
76
+ [ownvoice train] BELOW THRESHOLD
77
+ Below threshold (similarity 0.612 < 0.75). The adapter was still saved, try more/cleaner voice clips, more epochs, or a higher --lora-rank, then re-run. You can still listen to it:
78
+ ownvoice infer --adapter ownvoice-adapter/adapter.safetensors --text "This is my own voice, trained with OwnVoice."
79
+ ```
80
+
81
+ Only a data-loading problem (no usable clips) or a caught PEFT-injection failure exits non-zero. Every successful run writes `adapter.safetensors` and `metadata.json` (training config, the similarity score, a timestamp) to the output directory: two files you keep, with no server round-trip needed to use them again.
82
+
83
+ ### 3. `ownvoice infer`
84
+
85
+ ```
86
+ $ ownvoice infer --adapter ownvoice-adapter/adapter.safetensors --text "Hello, this is my own voice."
87
+ [ownvoice infer] Wrote ownvoice-output.wav
88
+ ```
89
+
90
+ Every subcommand also supports `--json` for a structured, machine-parseable output mode, useful if a script or an agent is calling `ownvoice` programmatically instead of a person reading the terminal:
91
+
92
+ ```
93
+ $ ownvoice check --json
94
+ {"success": true, "message": "PEFT LoRA injection succeeded against pocket-tts's flow_lm module (target_modules=\"all-linear\").", "module_tree": null}
95
+ ```
96
+
97
+ ## How it works
98
+
99
+ ```
100
+ voice clips (wav)
101
+ |
102
+ v
103
+ data.py --validate format/duration--> clean clip set
104
+ |
105
+ v
106
+ train.py --PEFT LoRA (target_modules="all-linear")--> adapter.safetensors + metadata.json
107
+ |
108
+ v
109
+ infer.py --generate test utterance--> synthesized audio
110
+ |
111
+ v
112
+ score.py --resample to 16kHz mono--> Resemblyzer cosine similarity
113
+ |
114
+ v
115
+ CLI report (>= 0.75 = usable adapter, below triggers a labeled next-step message)
116
+ ```
117
+
118
+ `ownvoice/data.py` loads and validates the voice-clip directory. `ownvoice/train.py` loads pocket-tts's frozen base model, injects a LoRA adapter into its `flow_lm` transformer with PEFT (`target_modules="all-linear"`), runs the training loop, and saves the adapter plus a manifest. `ownvoice/infer.py` loads a saved adapter back onto the base model and generates speech. `ownvoice/score.py` resamples audio to 16kHz mono with `torchaudio.transforms.Resample` and scores speaker similarity with [Resemblyzer](https://github.com/resemble-ai/Resemblyzer).
119
+
120
+ OwnVoice is intentionally single-model: it wraps pocket-tts only, with no abstraction layer for a second base model, since none is in scope.
121
+
122
+ ## Consent and misuse
123
+
124
+ This tool clones a voice from audio you have the right to use. Do not clone someone else's voice, or a public figure's voice, without their explicit consent. OwnVoice ships no bulk-generation or auto-scaling feature in this version, keeping the blast radius of any single misuse case small.
125
+
126
+ ## Setup-time benchmark vs comparable tools
127
+
128
+ | Tool | Time to first working setup | Notable design choice | Source |
129
+ |---|---|---|---|
130
+ | [kokoro-tts](https://github.com/nazdridoy/kokoro-tts) | under 2 minutes | `pip install git+...`, instant CLI synthesis, no fine-tuning | kokoro-tts README |
131
+ | [Unsloth](https://unsloth.ai) | under 1 minute to start a run | one-command training start (`uv pip install`) | Unsloth docs |
132
+ | [pocket-tts](https://github.com/kyutai-labs/pocket-tts) | seconds | `--voice <wav>` zero-shot cloning, no training available | pocket-tts README |
133
+ | **OwnVoice** | under 2 minutes to a confirmed-working training environment | `ownvoice check`: free, instant, CPU-only PEFT-compatibility validation before spending anything on a GPU | this repo |
134
+
135
+ OwnVoice's own training run is real GPU time, honestly labeled and not hidden behind a fake progress bar, the same category norm Unsloth uses. What OwnVoice compresses to under two minutes is everything *before* that: confirming your environment actually works.
136
+
137
+ ## Implementation status
138
+
139
+ This is a young v0.1. `ownvoice check`, the CLI argument parsing, voice-clip validation, the similarity scoring math, and the adapter/manifest save and load path are implemented and covered by the test suite (`pytest`). LoRA injection was verified structurally against pocket-tts's real source and then confirmed for real: `ownvoice check` was run against pocket-tts's actual downloaded weights, on CPU, and PEFT's `target_modules="all-linear"` injection genuinely succeeded. The full training and generation path has since been verified end to end for real too: a real 2-epoch LoRA training run against loaded pocket-tts weights produced a finite, non-NaN flow-matching loss, and the resulting adapter produced a real, non-silent generated `.wav` file via `ownvoice infer`. That validation surfaced two real gaps in the naive approach and fixed them: (1) pocket-tts's published, inference-only PyPI package does not actually expose a way to compute the training loss through `FlowLMModel.forward()` despite its own docstring claiming otherwise, so OwnVoice computes the flow-matching loss directly from `flow_lm`'s real submodules instead; (2) swapping `base_model.flow_lm` to the PEFT-wrapped model before calling `generate_audio()` breaks pocket-tts's internal KV-cache state lookup -- no swap is needed at all, since PEFT's LoRA injection already mutates `base_model.flow_lm` in place. One real, external limitation to know about: the publicly downloadable pocket-tts weights (`kyutai/pocket-tts-without-voice-cloning`) refuse a raw reference-clip path/URL outright; OwnVoice works around this by pre-loading and resampling the clip itself, but voice-cloning fidelity from that checkpoint is a known limitation of the base model, not an OwnVoice bug -- for kyutai's best-quality cloning weights, request gated access at [huggingface.co/kyutai/pocket-tts](https://huggingface.co/kyutai/pocket-tts). Run `ownvoice check` yourself and read the source before trusting any of it further, that is the right amount of skepticism for a v0.1.
140
+
141
+ ## Contributing
142
+
143
+ Issues and PRs welcome, MIT licensed throughout. If you want to help close the actual gap this project targets, the most useful contribution is upstream: a lightweight LoRA-adapter training script contributed back to [kyutai-labs/pocket-tts](https://github.com/kyutai-labs/pocket-tts) itself, discussed on [issue #30](https://github.com/kyutai-labs/pocket-tts/issues/30).
144
+
145
+ ## License
146
+
147
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ const { spawnSync } = require('node:child_process');
5
+
6
+ function commandExists(cmd) {
7
+ const probe = process.platform === 'win32' ? 'where' : 'which';
8
+ const result = spawnSync(probe, [cmd], { stdio: 'ignore' });
9
+ return result.status === 0;
10
+ }
11
+
12
+ function run(cmd, args) {
13
+ const result = spawnSync(cmd, args, { stdio: 'inherit' });
14
+ if (result.error) {
15
+ return null;
16
+ }
17
+ return result.status;
18
+ }
19
+
20
+ const args = process.argv.slice(2);
21
+
22
+ // ownvoice is a Python/PyTorch package (LoRA fine-tuning on top of pocket-tts).
23
+ // This wrapper never bundles a platform binary -- there isn't one to bundle --
24
+ // it bootstraps into the real ownvoice Python CLI via whichever Python runner
25
+ // is already on PATH, preferring uv/uvx since that's the primary documented
26
+ // install path (`uvx ownvoice train ...`) and increasingly present by default
27
+ // in agent and CI sandboxes.
28
+ const runners = [
29
+ { cmd: 'uvx', build: (a) => ['ownvoice', ...a] },
30
+ { cmd: 'pipx', build: (a) => ['run', 'ownvoice', ...a] },
31
+ ];
32
+
33
+ for (const runner of runners) {
34
+ if (commandExists(runner.cmd)) {
35
+ const status = run(runner.cmd, runner.build(args));
36
+ if (status !== null) {
37
+ process.exit(status);
38
+ }
39
+ }
40
+ }
41
+
42
+ console.error(
43
+ [
44
+ 'ownvoice: no Python runner found (checked uvx, pipx).',
45
+ '',
46
+ 'ownvoice is a Python/PyTorch CLI; this npm package is a thin wrapper',
47
+ 'that bootstraps it, not a standalone Node reimplementation.',
48
+ '',
49
+ 'Install one of the following, then re-run this command:',
50
+ ' - uv (recommended): https://docs.astral.sh/uv/getting-started/installation/',
51
+ ' - pipx: https://pipx.pypa.io/stable/installation/',
52
+ '',
53
+ 'Or install ownvoice directly with pip:',
54
+ ' pip install ownvoice',
55
+ ].join('\n')
56
+ );
57
+ process.exit(1);
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "ownvoice",
3
+ "version": "0.1.0",
4
+ "description": "Train a LoRA voice adapter for pocket-tts and own the resulting model, no API lock-in. npx-installable wrapper around the ownvoice Python CLI, for agent and Node-first environments.",
5
+ "bin": {
6
+ "ownvoice": "bin/ownvoice.js"
7
+ },
8
+ "files": [
9
+ "bin"
10
+ ],
11
+ "keywords": [
12
+ "tts",
13
+ "text-to-speech",
14
+ "voice-cloning",
15
+ "lora",
16
+ "peft",
17
+ "pocket-tts",
18
+ "fine-tuning",
19
+ "cli",
20
+ "agent-native"
21
+ ],
22
+ "license": "MIT",
23
+ "author": "Rudrendu Paul",
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "git+https://github.com/RudrenduPaul/ownvoice.git"
27
+ },
28
+ "homepage": "https://github.com/RudrenduPaul/ownvoice",
29
+ "bugs": {
30
+ "url": "https://github.com/RudrenduPaul/ownvoice/issues"
31
+ },
32
+ "engines": {
33
+ "node": ">=18"
34
+ }
35
+ }
@@ -0,0 +1,10 @@
1
+ """OwnVoice: train a LoRA voice adapter for pocket-tts, own the resulting model.
2
+
3
+ OwnVoice wraps kyutai-labs/pocket-tts (MIT-licensed, CPU-capable local
4
+ text-to-speech) with a LoRA fine-tuning workflow. The output is an adapter
5
+ file you keep and run yourself, not an API subscription.
6
+ """
7
+
8
+ __version__ = "0.1.0"
9
+
10
+ __all__ = ["__version__"]