{
  "markdown": "<!-- mcp-name: io.github.RudrenduPaul/ownvoice -->\n\n# OwnVoice\n\nTrain 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.\n\n[![PyPI](https://img.shields.io/pypi/v/ownvoice-cli)](https://pypi.org/project/ownvoice-cli/)\n[![npm](https://img.shields.io/npm/v/ownvoice-cli)](https://www.npmjs.com/package/ownvoice-cli)\n[![License: MIT](https://img.shields.io/github/license/RudrenduPaul/ownvoice)](https://github.com/RudrenduPaul/ownvoice/blob/main/LICENSE)\n\n![Terminal recording of installing ownvoice-cli with pip into a fresh virtual environment, then running `ownvoice --version` and `ownvoice --help` to show the real CLI and its three subcommands.](https://raw.githubusercontent.com/RudrenduPaul/ownvoice/main/docs/demo.gif)\n\n```bash\npip install ownvoice-cli\n```\n\nRequires Python 3.11 or newer. See [Install](#install) below for the npx / agent-sandbox path.\n\n## Table of Contents\n\n- [Install](#install)\n- [Quickstart](#quickstart)\n- [CLI Reference](#cli-reference)\n- [Features](#features)\n- [How It Works](#how-it-works)\n- [Setup-Time Benchmark vs Comparable Tools](#setup-time-benchmark-vs-comparable-tools)\n- [Why OwnVoice Exists](#why-ownvoice-exists)\n- [What OwnVoice Is Not](#what-ownvoice-is-not)\n- [Consent and Misuse](#consent-and-misuse)\n- [Implementation Status](#implementation-status)\n- [FAQ](#faq)\n- [Contributing](#contributing)\n- [License](#license)\n\n## Install\n\n```bash\npip install ownvoice-cli\n```\n\n**npx / agent-native environments:** OwnVoice is a Python/PyTorch CLI, so the [npm package](https://www.npmjs.com/package/ownvoice-cli) 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. The npm package was renamed to `ownvoice-cli` (from the old plain `ownvoice`, now deprecated) to match its PyPI counterpart.\n\n```bash\nnpx ownvoice-cli check\n```\n\nBoth the npm wrapper and the PyPI package (`ownvoice-cli`) are live, so the command above works today.\n\n**Torch and CUDA:** `ownvoice check` 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` run normally, `ownvoice train` just takes longer per epoch.\n\n## Quickstart\n\n### 1. `ownvoice check`, the free Day-0 validation\n\nBefore 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.\n\n```\n$ ownvoice check\n[ownvoice check] PASS: PEFT LoRA injection succeeded against pocket-tts's flow_lm module (target_modules=\"all-linear\").\n```\n\nIf 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:\n\n```\n$ ownvoice check\n[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.\n\nModule tree (for debugging / for the issue #30 blocker post):\n<root>: FlowLMModel\ninput_linear: Linear\ntransformer: StreamingTransformer\ntransformer.layers.0.self_attn.in_proj: Linear\ntransformer.layers.0.self_attn.out_proj: Linear\n...\n```\n\n### 2. `ownvoice train`\n\nRecord 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](#consent-and-misuse)), split into a few `.wav` clips in one directory, then point OwnVoice at it:\n\n```\n$ ownvoice train --voice-clips ./my-voice-clips\n[ownvoice train] USABLE ADAPTER\nUsable adapter (similarity 0.812 >= 0.75). Try it now:\n  ownvoice infer --adapter ownvoice-adapter/adapter.safetensors --text \"This is my own voice, trained with OwnVoice.\"\n```\n\nOnly `--voice-clips` is required. Every other flag has a sensible default (see the full [CLI Reference](#cli-reference) below).\n\nA 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:\n\n```\n$ ownvoice train --voice-clips ./my-voice-clips\n[ownvoice train] BELOW THRESHOLD\nBelow 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:\n  ownvoice infer --adapter ownvoice-adapter/adapter.safetensors --text \"This is my own voice, trained with OwnVoice.\"\n```\n\nOnly a data-loading problem (no usable clips) or a caught PEFT-injection failure exits non-zero. A finished run always writes `adapter.safetensors` and `metadata.json` (training config, similarity score, a timestamp) to the output directory: two files you keep, with no server round-trip needed to use them again.\n\n### 3. `ownvoice infer`\n\n```\n$ ownvoice infer --adapter ownvoice-adapter/adapter.safetensors --text \"Hello, this is my own voice.\"\n[ownvoice infer] Wrote ownvoice-output.wav\n```\n\nEvery 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:\n\n```\n$ ownvoice check --json\n{\"success\": true, \"message\": \"PEFT LoRA injection succeeded against pocket-tts's flow_lm module (target_modules=\\\"all-linear\\\").\", \"module_tree\": null}\n```\n\n![Terminal recording of running `ownvoice check --json` for structured, agent-parseable output, then `ownvoice train --help` to show the real training flags and their defaults.](https://raw.githubusercontent.com/RudrenduPaul/ownvoice/main/docs/usage.gif)\n\n## CLI Reference\n\nReference below is taken directly from each subcommand's real `--help` output (`ownvoice-cli` 0.1.2 on PyPI).\n\n### Global\n\n```\nownvoice [OPTIONS] COMMAND [ARGS]...\n```\n\n| Flag | Description |\n|---|---|\n| `--version` | Print the OwnVoice version and exit. |\n| `--help` | Show the help message and exit. |\n\n### `ownvoice check`\n\nFree, CPU-only compatibility check: load pocket-tts and dry-run the LoRA injection. No GPU and no training required.\n\n| Flag | Description |\n|---|---|\n| `--json` | Print machine-readable JSON instead of human-readable text. |\n| `--help` | Show the help message and exit. |\n\n### `ownvoice train`\n\nTrain a LoRA voice adapter from a directory of `.wav` voice clips. Only `--voice-clips` is required.\n\n| Flag | Type | Default | Description |\n|---|---|---|---|\n| `--voice-clips` | directory, required | – | Directory of `.wav` voice-clip recordings to train from. |\n| `--out` | path | `ownvoice-adapter` | Directory to write `adapter.safetensors` + `metadata.json` to. |\n| `--epochs` | int, `>=1` | `10` | Number of training epochs. |\n| `--lora-rank` | int, `>=1` | `8` | LoRA rank. |\n| `--lora-alpha` | int, `>=1` | `16` | LoRA alpha. |\n| `--lora-dropout` | float, `0.0`–`1.0` | `0.05` | LoRA dropout. |\n| `--learning-rate` | float | `0.0001` | Optimizer learning rate. |\n| `--eval-text` | string | `\"This is my own voice, trained with OwnVoice.\"` | Sentence synthesized after training to score against the reference voice. |\n| `--json` | flag | off | Print machine-readable JSON instead of human-readable text. |\n| `--help` | flag | – | Show the help message and exit. |\n\n### `ownvoice infer`\n\nGenerate speech in the trained voice from a saved adapter, and save it to a `.wav` file.\n\n| Flag | Type | Default | Description |\n|---|---|---|---|\n| `--adapter` | path, required | – | Path to a trained `adapter.safetensors` file. |\n| `--text` | string, required | – | Text to synthesize in the trained voice. |\n| `--out` | path | `ownvoice-output.wav` | Output `.wav` file path. |\n| `--reference-audio` | path | recorded reference | Override the reference clip OwnVoice recorded in `metadata.json` at train time. |\n| `--json` | flag | off | Print machine-readable JSON instead of human-readable text. |\n| `--help` | flag | – | Show the help message and exit. |\n\n## Features\n\n- **A free compatibility check before you spend anything on a GPU.** `ownvoice check` loads pocket-tts and dry-runs PEFT's LoRA injection against its real `flow_lm` module tree, CPU only, no training. On failure it prints the actual module tree instead of a stack trace, so a real blocker is reportable instead of silent.\n- **An objective usable/not-usable signal, not a guess.** Every training run resamples the generated test utterance to 16kHz mono and scores it against your reference clips with [Resemblyzer](https://github.com/resemble-ai/Resemblyzer) cosine similarity. `0.75` or higher is labeled `USABLE ADAPTER`; anything lower is `BELOW THRESHOLD`, a labeled outcome and not a crash, exit code `0` either way.\n- **Structured output on every subcommand.** `check`, `train`, and `infer` all accept `--json`, returning one machine-parseable object instead of colored terminal text: confirmed directly, `ownvoice check --json` returns `{\"success\": true, \"message\": \"...\", \"module_tree\": null}`.\n- **Two files you keep, no server round-trip.** A finished training run writes `adapter.safetensors` (the trained weights, a few megabytes at the default `--lora-rank 8`) and `metadata.json` (the full training config, similarity score, per-epoch loss, and a timestamp) to disk. Load them back any time later with `ownvoice infer`, no network call required.\n- **One base model, on purpose.** OwnVoice wraps pocket-tts only. There is no abstraction layer for a second base model, matching the codebase's own single-target-by-design architecture note: the LoRA injection path (`target_modules=\"all-linear\"` against pocket-tts's real `flow_lm` layers) stays exact instead of generic.\n\n## How It Works\n\n```\nvoice clips (wav)\n      |\n      v\n  data.py   --validate format/duration-->  clean clip set\n      |\n      v\n  train.py  --PEFT LoRA (target_modules=\"all-linear\")--> adapter.safetensors + metadata.json\n      |\n      v\n  infer.py  --generate test utterance--> synthesized audio\n      |\n      v\n  score.py  --resample to 16kHz mono--> Resemblyzer cosine similarity\n      |\n      v\n  CLI report (>= 0.75 = usable adapter, below triggers a labeled next-step message)\n```\n\n`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.\n\nOwnVoice is intentionally single-model: it wraps pocket-tts only, with no abstraction layer for a second base model, since none is in scope.\n\n## Setup-Time Benchmark vs Comparable Tools\n\n| Tool | Time to first working setup | Notable design choice | Source |\n|---|---|---|---|\n| [kokoro-tts](https://github.com/nazdridoy/kokoro-tts) | under 2 minutes | `pip install git+...`, instant CLI synthesis, no fine-tuning | kokoro-tts README |\n| [Unsloth](https://unsloth.ai) | under 1 minute to start a run | one-command training start (`uv pip install`) | Unsloth docs |\n| [pocket-tts](https://github.com/kyutai-labs/pocket-tts) | seconds | `--voice <wav>` zero-shot cloning, no training available | pocket-tts README |\n| **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 |\n\nOwnVoice'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.\n\n## Why OwnVoice Exists\n\npocket-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.\n\nIt 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.\n\n## What OwnVoice Is Not\n\npocket-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.\n\nOwnVoice 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.\n\n## Consent and Misuse\n\nThis 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.\n\n## Implementation Status\n\nThis is a young, early-stage release. `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 project this early.\n\n## FAQ\n\n**What is OwnVoice, and why not just use pocket-tts by itself?**\nOwnVoice trains a LoRA adapter for [pocket-tts](https://github.com/kyutai-labs/pocket-tts) and saves it to your own disk as `adapter.safetensors` plus `metadata.json`. It exists because pocket-tts's own maintainers have said fine-tuning code is not on their near-term roadmap (see [issue #30](https://github.com/kyutai-labs/pocket-tts/issues/30)). Once you have a trained adapter, you never need OwnVoice again to use it: `ownvoice infer` just loads the adapter back onto the base model.\n\n**How is this different from pocket-tts's own built-in `--voice <wav>` zero-shot cloning?**\npocket-tts already clones a voice from a single reference clip with no training step, `--voice <wav>` at the CLI or `get_state_for_audio_prompt()` in Python. OwnVoice trades that speed for a permanently trained adapter, so generation no longer depends on carrying around a reference clip at runtime, with (based on the training objective, not yet independently benchmarked at scale) more consistent output across repeated generations than a single-clip zero-shot embedding tends to give. If zero-shot is enough for your use case, use pocket-tts directly, it is simpler and faster.\n\n**What do I need to install it, and does it run on Apple Silicon or a CPU-only machine?**\nPython 3.11 or newer, then `pip install ownvoice-cli`. `ownvoice check` and `ownvoice infer` need no GPU at all and run fine on Apple Silicon or a CPU-only machine, matching pocket-tts's own CPU-capable design. `ownvoice train` runs on CPU too, it just takes longer per epoch; install the CUDA build of PyTorch first if you have an NVIDIA GPU and want training to go faster.\n\n**How does OwnVoice compare to kokoro-tts and Unsloth?**\n[kokoro-tts](https://github.com/nazdridoy/kokoro-tts) gets you synthesizing speech in under 2 minutes but has no fine-tuning step at all. [Unsloth](https://unsloth.ai) gets a training run started in under a minute but is a general LLM fine-tuning framework, not TTS-specific. OwnVoice is narrower than either: one base model (pocket-tts only), one job (a voice adapter), plus a free `ownvoice check` step that confirms PEFT's LoRA injection actually works against your environment before you spend anything on a GPU, a check neither of those tools has an equivalent of.\n\n**My training run finished but printed \"BELOW THRESHOLD\", is that a bug?**\nNo. It is a labeled outcome, not a crash, `ownvoice train` exits `0` either way. Below the 0.75 cosine-similarity bar, the adapter is still saved to disk and OwnVoice tells you plainly to try more or cleaner voice clips, more epochs, or a higher `--lora-rank`, then re-run. Only two things actually fail the command with a non-zero exit: no usable clips to load, or a caught PEFT-injection failure.\n\n**Can I use OwnVoice, and the adapters it produces, commercially?**\nOwnVoice's own code is MIT (see [LICENSE](https://github.com/RudrenduPaul/ownvoice/blob/main/LICENSE)). pocket-tts's code package is MIT too, but the model weights OwnVoice actually downloads and trains against, [`kyutai/pocket-tts-without-voice-cloning`](https://huggingface.co/kyutai/pocket-tts-without-voice-cloning) and the gated [`kyutai/pocket-tts`](https://huggingface.co/kyutai/pocket-tts), are licensed CC-BY-4.0, not MIT. CC-BY-4.0 permits commercial use but requires attribution to Kyutai. Since any adapter you train is derived from those weights, check that attribution requirement before shipping a commercial product built on it.\n\n**Whose voice can I actually clone with this?**\nOnly your own, or someone else's with their explicit, checked consent, never a public figure's voice without it. See [Consent and Misuse](#consent-and-misuse) above. OwnVoice ships no bulk-generation or auto-scaling feature in this version, which keeps the blast radius of any single misuse case small.\n\n## MCP Server\n\nOwnVoice ships a [Model Context Protocol](https://modelcontextprotocol.io) server, so an MCP-compatible agent can drive `ownvoice check` / `train` / `infer` directly over stdio instead of shelling out and parsing text itself.\n\n```bash\npip install \"ownvoice-cli[mcp]\"\n```\n\nAdd it to an MCP client's config (for example, Claude Desktop's `claude_desktop_config.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"ownvoice\": {\n      \"command\": \"ownvoice-mcp\"\n    }\n  }\n}\n```\n\nThe server exposes a single tool, `run(args: list[str]) -> dict`, that shells out to the real `ownvoice` CLI with the given argv and returns its result as structured JSON, so a caller gets the exact same behavior the human-facing CLI has, including `--json` mode. Example call: `run(args=[\"check\", \"--json\"])` returns `{\"result\": {\"success\": true, \"message\": \"...\", \"module_tree\": null}}`. A non-zero exit, a launch failure, or a subprocess timeout is always returned as `{\"error\": \"...\"}` rather than raised.\n\n## Contributing\n\nIssues 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).\n\n## License\n\nMIT. See [LICENSE](https://github.com/RudrenduPaul/ownvoice/blob/main/LICENSE).\n",
  "bytes": 21836,
  "sha": "123be7a2bdbd143c590b76fd8c0f6c910cb7c0843c6ed039d0f5da6ed40af6d7",
  "repo_slug": "rudrendupaul/ownvoice",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_rudrendupaul_ownvoice_5210773e/readme"
}