Skip to main content
Glama
Sunwood-ai-labs

irodori-anime

Official
README.md
<div align="center">
<img src="docs/public/logo.svg" alt="Irodori waveform logo" width="96" />
<h1>Irodori Anime Voice Studio</h1>
<p>Design a Japanese voice. Reuse it. Hear the difference.</p>
<p><a href="https://github.com/Sunwood-ai-labs/irodori-anime-voice-studio/actions/workflows/ci.yml"><img src="https://github.com/Sunwood-ai-labs/irodori-anime-voice-studio/actions/workflows/ci.yml/badge.svg" alt="CI" /></a> <img src="https://img.shields.io/badge/Python-3.12-3776AB" alt="Python 3.12" /> <a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green" alt="MIT" /></a></p>
<p><a href="README.md">English</a> · <a href="README.ja.md">日本語</a></p>
</div>

## 🎙️ What it does

A local Gradio app built on Irodori-TTS v4.1 Anime. Generate a voice from a Japanese description, reuse the generated clip as a reference, and compare the next result beside it.

- Five mature female voice presets and custom voice descriptions.
- One-click **この生成音声を参照にする** (use this generated voice as reference).
- Separate reference A and generated B players; multiple reference clips are supported.
- CPU inference with a uv lockfile, a Gradio API, and MCP support.

GitHub hosts the code; [the documentation site](https://sunwood-ai-labs.github.io/irodori-anime-voice-studio/) hosts instructions. Audio generation runs on your own computer.

## 🖼️ Screenshots and voice samples

![Irodori A/B comparison](docs/public/screenshots/app-comparison.png)

The actual app with the first voice and its reference-conditioned result loaded.

Play each voice below. Unmute the player if necessary. These MP4 attachments contain audio only.

<table>
<thead><tr><th>Character</th><th>Portrait</th><th>Audio · 10s</th></tr></thead>
<tbody>
<tr>
<td><strong>01 · 夜凪 紫苑</strong><br>Shion Yonagi<br><sub>夜の秘密</sub></td>
<td><img src="docs/public/samples/portrait-1.png" alt="夜凪 紫苑" width="130" /></td>
<td><video src="https://github.com/user-attachments/assets/f8473601-907e-4df6-a4ae-8fdc92001364" controls></video><br><a href="docs/public/samples/voice-1.wav">WAV · 10s</a></td>
</tr>
<tr>
<td><strong>02 · 氷室 怜</strong><br>Rei Himuro<br><sub>知的な駆け引き</sub></td>
<td><img src="docs/public/samples/portrait-2.png" alt="氷室 怜" width="130" /></td>
<td><video src="https://github.com/user-attachments/assets/e4707772-cfda-4cfa-8c5a-4222ee18971a" controls></video><br><a href="docs/public/samples/voice-2.wav">WAV · 10s</a></td>
</tr>
<tr>
<td><strong>03 · 月城 紗夜</strong><br>Saya Tsukishiro<br><sub>月夜の誘い</sub></td>
<td><img src="docs/public/samples/portrait-3.png" alt="月城 紗夜" width="130" /></td>
<td><video src="https://github.com/user-attachments/assets/10ae2f60-c6a0-4669-a76f-3cbadda2577e" controls></video><br><a href="docs/public/samples/voice-3.wav">WAV · 10s</a></td>
</tr>
<tr>
<td><strong>04 · 御影 凛香</strong><br>Rinka Mikage<br><sub>凛とした守護者</sub></td>
<td><img src="docs/public/samples/portrait-4.png" alt="御影 凛香" width="130" /></td>
<td><video src="https://github.com/user-attachments/assets/eb9fcc3e-92b3-49ee-ab8f-52929e3f23ab" controls></video><br><a href="docs/public/samples/voice-4.wav">WAV · 10s</a></td>
</tr>
<tr>
<td><strong>05 · 久遠 琴音</strong><br>Kotone Kuon<br><sub>耳元の約束</sub></td>
<td><img src="docs/public/samples/portrait-5.png" alt="久遠 琴音" width="130" /></td>
<td><video src="https://github.com/user-attachments/assets/346b7512-7141-4f65-91b3-c346c15a6f3a" controls></video><br><a href="docs/public/samples/voice-5.wav">WAV · 10s</a></td>
</tr>
</tbody>
</table>

[Portraits, voice prompts and standard audio players](https://sunwood-ai-labs.github.io/irodori-anime-voice-studio/samples.html)

## 🚀 Quick start

Install Git, [uv](https://docs.astral.sh/uv/getting-started/installation/), Python 3.12 and FFmpeg. The first launch downloads model weights and can take time. The main model alone is approximately 3 GB; dependencies and the codec require additional disk and memory.

```sh
git clone https://github.com/Sunwood-ai-labs/irodori-anime-voice-studio.git
cd irodori-anime-voice-studio
uv sync --locked --python 3.12
uv run app.py
```

Open the URL printed in the terminal (normally `http://127.0.0.1:7860`). Local inference defaults to CPU/fp32 with two threads. [Installation and configuration](docs/guide/install.md).

## 🎧 Voice workflow

1. Choose a preset or write a Japanese caption and text, then generate B.
2. Click **この生成音声を参照にする** to place B into reference A.
3. Change the text and generate again. A remains available for comparison.
4. Clear the reference file list to return to voice design without a reference.

With multiple references, A previews the first clip while inference uses the ordered list (up to the model's 120-second limit). Download clips you want to keep. The same caption or seed does not guarantee the same voice; reference audio guides similarity.

## 🔌 API and MCP

The app includes a **Gradio MCP server**. MCP agents can retrieve presets and generate speech on the CPU of the computer running the app. GitHub Pages hosts documentation, not an MCP endpoint.

### Start and connect

Run from the repository root and keep the terminal open:

```powershell
uv sync --locked --python 3.12
$env:INFERENCE_DEVICE = 'cpu'
uv run app.py
```

The app launches with `mcp_server=True`. Wait for model downloads and loading to finish. Add an HTTP MCP server in your client; configuration file keys vary by client.

| Setting | Value |
| --- | --- |
| Suggested name | `irodori-anime` |
| Preferred transport | Streamable HTTP |
| Server URL | `http://127.0.0.1:7860/gradio_api/mcp/` |
| Legacy SSE endpoint | `http://127.0.0.1:7860/gradio_api/mcp/sse` |
| Schema inspection, not a connection URL | `http://127.0.0.1:7860/gradio_api/mcp/schema` |

Match the port to `GRADIO_SERVER_PORT` if changed. This repository does not bundle a separate stdio MCP server. Installing the agent skill does not start the app or register the MCP connection.

### Tools and generation

Discover the tool list after connecting; clients may prefix names with the server name.

| Tool | Purpose | Inputs |
| --- | --- | --- |
| `apply_voice_preset` | Retrieve text, caption and seed | `name`, e.g. `01_夜の秘密`; use the preset ID, not the character name |
| `generate` | Synthesize speech | `text`, `caption`, `reference_audio`, `seconds_raw`, `seed_raw`, `num_steps` |
| `_on_schedule_change` | UI sampling-schedule helper | Not needed for ordinary generation |

Example request to your agent:

```text
Use the irodori-anime MCP server to retrieve preset 01_夜の秘密.
Generate a ten-second clip using its caption and seed, with 40 steps.
Save the WAV, use it as a reference for a different line, and preserve both A and B.
```

Example `generate` arguments:

```json
{
  "text": "こんな時間に、私を探していたの? いいわ、少しだけ付き合ってあげる。",
  "caption": "落ち着いた低めの声の大人女性。静かな自信と余裕があり、上品にゆっくり話す。",
  "reference_audio": [],
  "seconds_raw": "10",
  "seed_raw": "42",
  "num_steps": 40
}
```

Duration and seed use strings; empty strings mean automatic duration and random seed. Results include the audio file URL and run log in MCP content. Download generated files to a persistent output directory.

### Reference audio and A/B comparison

For MCP, `reference_audio` is an array of audio URLs reachable by the Gradio server. For example, add the following field alongside `text` and `caption`:

```json
{
  "reference_audio": [
    "https://sunwood-ai-labs.github.io/irodori-anime-voice-studio/samples/voice-1.wav"
  ]
}
```

You can reuse the Gradio file URL returned by a previous generation. For local files, use `upload_file_to_gradio` if your client supplies it; this repository does not expose that helper itself. Otherwise use [the Python API with `handle_file`](docs/guide/api.md). Do not substitute a Windows filesystem path for a URL.

Save reference A before generating B. MCP calls do not click the UI's reference button or automatically update its comparison panels. Preserve reference order; the model supports up to 120 seconds of reference audio.

### Local MCP example: pass an absolute WAV path

**A separate local stdio MCP server on the development PC generated speech with a local WAV path through `generate_speech`.** This is different from the Gradio MCP server above. That local server is not bundled in this repository; cloning this project does not install a `generate_speech` tool.

| Server | Generation tool | `reference_audio` | Duration and seed |
| --- | --- | --- | --- |
| This repository's Gradio MCP | `generate` | Array of audio URLs | `seconds_raw`: `"10"`, `seed_raw`: `"42"` |
| Separately configured local stdio MCP on the development PC | `generate_speech` | One absolute local WAV path | `seconds`: `10`, `seed`: `42` |

The following **local `generate_speech` request** is reconstructed from the saved metadata of the reference-conditioned generation using voice 1. It is not a captured wire request.

```json
{
  "text": "また会えたわね。あなたが来ること、なぜか分かっていたの。今夜は少しだけ、私の秘密に付き合って。",
  "preset": "01_夜の秘密",
  "seconds": 10,
  "steps": 40,
  "seed": 42,
  "reference_audio": "C:\\Prj\\Irodori-TTS-CPU\\outputs\\cool-beauty-five\\01_夜の秘密.wav"
}
```

This path is the development PC's example. If using an equivalent local server, replace it with an existing, readable PCM WAV absolute path on the server's computer. Escape Windows backslashes as `\\` in JSON. The preset supplies the caption, so this example omits `caption`. The saved generation metadata confirms CPU execution, enabled speaker conditioning, and a ten-second 48 kHz output.

**Do not pass this JSON unchanged to Gradio's `generate`.** Users of this repository should use the URL-array example above. For convenient local WAV uploads, [the Python API supports `handle_file`](docs/guide/api.md), but that call uses the Python API rather than MCP. Always inspect the connected server's tool names and argument schema first.

The development PC's local MCP now defaults to automatic duration when `seconds` is omitted, just as with explicit `null`. The ten-second value above reproduces a historical fixed-length run; normally remove that field. Check that the complete line is spoken, and adjust the text length for approximate duration requests.

### Verify the MCP connection

Save this as UTF-8 `check_mcp.py`, then run `uv run check_mcp.py` from the repository. It checks the handshake, tool list and preset call without generating audio.

```python
import asyncio
from mcp import ClientSession
from mcp.client.streamable_http import streamablehttp_client

async def main():
    url = "http://127.0.0.1:7860/gradio_api/mcp/"
    async with streamablehttp_client(url) as (read, write, _):
        async with ClientSession(read, write) as session:
            await session.initialize()
            print([tool.name for tool in (await session.list_tools()).tools])
            result = await session.call_tool(
                "apply_voice_preset", {"name": "01_夜の秘密"}
            )
            if result.isError:
                raise RuntimeError(result)
            print(result)

asyncio.run(main())
```

### Troubleshooting and validation scope

| Symptom | Check |
| --- | --- |
| Connection refused | App readiness, actual port, and whether its terminal is still running |
| 404 / transport error | Streamable HTTP `/gradio_api/mcp/` versus legacy `/sse` |
| Missing tools | Reconnect and discover tools. UI-only reference buttons are not MCP tools |
| Reference upload failure | A server-reachable URL or a completed upload is required |
| CPU generation timeout | A ten-second clip takes longer than ten seconds to generate; extend client timeouts and avoid duplicate jobs |
| Cloud agent cannot connect | `127.0.0.1` refers to the connecting machine; use an MCP client on the same PC |

The locked Gradio 6.26.0 local environment was checked with a Streamable HTTP handshake, tool discovery and preset call. Real speech generation was separately checked through the Python API. This does not claim generation or file-upload validation for every MCP client.

## 🛠️ Agent skill

[`irodori-voice-studio`](skills/irodori-voice-studio/SKILL.md) guides local CPU speech generation, the five presets, reference reuse, and A/B comparisons.

Ask Codex's skill-installer to install the skill:

```text
Install the skill from https://github.com/Sunwood-ai-labs/irodori-anime-voice-studio/tree/main/skills/irodori-voice-studio
```

Then invoke `$irodori-voice-studio`, for example: “Use voice 1 as a reference, generate a new ten-second line, and save A and B.” The app checkout and runtime are required separately. Keeping the skill in this repository does not register it globally.

## 📚 Documentation

- [Install and configure](docs/guide/install.md)
- [Use the API and MCP](docs/guide/api.md)
- [Troubleshooting and limitations](docs/guide/troubleshooting.md)
- [Contributing](CONTRIBUTING.md) · [Security](SECURITY.md) · [Changelog](CHANGELOG.md)

## 🧪 Development and validation

```sh
uv run python -m unittest test_voice_workflow -v
npm ci --prefix docs
npm run build --prefix docs
```

Workflow tests exercise real Gradio file processing with mocked inference; they do not benchmark voice quality. Local Windows CPU smoke tests also completed real reference-conditioned generation. CI does not download model weights.

## 🙏 Credits and license

Derived from [hugging-apps/irodori-tts-anime-demo](https://huggingface.co/spaces/hugging-apps/irodori-tts-anime-demo), revision `9d3707972aa237c0c2b895252563669a160e396b`. Model: [phasefield-audio/Irodori-TTS-v4.1-Anime](https://huggingface.co/phasefield-audio/Irodori-TTS-v4.1-Anime). Vendored inference code: [Aratako/Irodori-TTS](https://github.com/Aratako/Irodori-TTS). Codec: [Semantic-DACVAE](https://huggingface.co/Aratako/Semantic-DACVAE-Japanese-32dim). Generated audio retains SilentCipher watermarking.

Code is provided under the [MIT license](LICENSE). Refer to the model card for model usage conditions. Model weights and private credentials are not bundled. The samples directory contains the five explicitly published generated recordings and illustrations.

Maintenance

ActivityMaintained
ResponsivenessNo issues