How to Implement Voice Cloning with Reference Audio in Fish-Speech
Fish-Speech implements voice cloning by encoding reference audio into discrete acoustic tokens via the DAC encoder, caching them in ReferenceLoader, and injecting them as in-context examples into the LLAMA-based TTS model during inference.
Fish-Speech is an open-source text-to-speech (TTS) system that supports zero-shot voice cloning through in-context learning. To implement voice cloning with reference audio, you supply one or more short audio clips (5–10 seconds) that the model uses to extract speaker characteristics before generating new speech.
Architecture of Voice Cloning in Fish-Speech
The cloning pipeline consists of three core components that work sequentially during inference.
Reference Loading and Caching
The ReferenceLoader class in fish_speech/inference_engine/reference_loader.py manages the encoding and caching of reference audio.
# fish_speech/inference_engine/reference_loader.py
class ReferenceLoader:
def __init__(self):
self.ref_by_id: dict = {} # cache keyed by user-provided ID
self.ref_by_hash: dict = {} # cache keyed by SHA-256 of audio bytes
The loader provides two primary methods:
load_by_id(id, use_cache)– Reads all audio files underreferences/<id>/, encodes them via the DAC encoder, and stores the resulting tokens inself.ref_by_id.load_by_hash(references, use_cache)– Processes inline reference data passed directly in the API request. It computes SHA-256 hashes of the raw audio bytes to enable caching without filesystem I/O.
Both methods return a tuple of (prompt_tokens, prompt_texts) that is compatible with the downstream LLAMA request.
TTS Inference Loop
The TTSInferenceEngine class in fish_speech/inference_engine/__init__.py orchestrates the cloning process.
# fish_speech/inference_engine/__init__.py
class TTSInferenceEngine(ReferenceLoader, VQManager):
@torch.inference_mode()
def inference(self, req: ServeTTSRequest):
# 1️⃣ Load reference tokens
if req.reference_id:
prompt_tokens, prompt_texts = self.load_by_id(
req.reference_id, req.use_memory_cache
)
elif req.references:
prompt_tokens, prompt_texts = self.load_by_hash(
req.references, req.use_memory_cache
)
# 2️⃣ Send request to LLAMA with reference context
response_queue = self.send_Llama_request(req, prompt_tokens, prompt_texts)
# 3️⃣ Decode VQ tokens to audio
while True:
wrapped_result = response_queue.get()
# ... handle response
segment = self.decode_vq_tokens(wrapped_result.response.codes)
The reference tokens are merged with the prompt inside send_Llama_request, conditioning the model to reproduce the speaker characteristics from the reference audio.
How to Add Reference Audio
Method 1: REST API Endpoint
The server exposes /v1/references/add in tools/server/views.py for persistent reference storage.
| Parameter | Description |
|---|---|
id |
Human-readable identifier (alphanumerics, hyphens, underscores; max 255 chars) |
audio |
Audio file (WAV, MP3, etc. supported by torchaudio) |
text |
Transcript of the audio for alignment |
import requests
API_URL = "http://localhost:8000/v1/references/add"
files = {"audio": open("my_voice.wav", "rb")}
data = {
"id": "my_favorite_voice",
"text": "Hello, this is my reference voice."
}
resp = requests.post(API_URL, files=files, data=data)
print(resp.json())
The endpoint stores audio under ./references/<id>/sample.<ext> and creates sample.lab containing the transcript.
Method 2: Direct Filesystem Access
For scripting without HTTP overhead:
from fish_speech.inference_engine.reference_loader import ReferenceLoader
loader = ReferenceLoader()
loader.add_reference(
id="my_voice",
wav_file_path="path/to/my_voice.wav",
reference_text="This is the voice I want to clone."
)
How to Use Reference Audio for Voice Cloning
Option 1: Web UI (Gradio)
The Gradio interface in tools/webui/inference.py provides three relevant fields:
- Reference ID – Select a pre-saved reference folder
- Reference Audio – Upload a temporary clip for one-time use
- Reference Text – Optional transcript for the uploaded clip
When audio is uploaded, inference_wrapper calls get_reference_audio() (lines 58-66 in tools/webui/inference.py) to build a ServeReferenceAudio object:
# tools/webui/inference.py
def inference_wrapper(..., reference_audio, reference_text, ...):
if reference_audio:
references = get_reference_audio(reference_audio, reference_text)
else:
references = []
req = ServeTTSRequest(
text=text,
reference_id=reference_id if reference_id else None,
references=references,
...
)
Option 2: API with Stored Reference ID
curl -X POST http://localhost:8000/v1/tts \
-H "Content-Type: application/json" \
-d '{
"text": "Please read this sentence in my voice.",
"reference_id": "my_favorite_voice",
"format": "wav",
"streaming": false,
"max_new_tokens": 1024,
"top_p": 0.8,
"repetition_penalty": 1.1,
"temperature": 0.8
}' --output output.wav
The server retrieves the cached encoding from ref_by_id, avoiding redundant DAC encoding on subsequent requests.
Option 3: Inline Reference Without Persistence
import base64
import requests
API_TTS = "http://localhost:8000/v1/tts"
with open("ref.wav", "rb") as f:
ref_b64 = base64.b64encode(f.read()).decode()
payload = {
"text": "Clone my voice for this sentence.",
"references": [
{"audio": ref_b64, "text": "Short reference utterance."}
],
"format": "wav",
"use_memory_cache": "on"
}
resp = requests.post(API_TTS, json=payload)
with open("cloned.wav", "wb") as f:
f.write(resp.content)
The load_by_hash method computes SHA-256 hashes of the audio bytes, enabling cache hits when identical reference audio is submitted again.
Key Source Files for Voice Cloning
| File | Role | Direct Link |
|---|---|---|
fish_speech/inference_engine/reference_loader.py |
Loads, caches, and manages reference audio encodings | reference_loader.py |
fish_speech/inference_engine/__init__.py |
Core TTS inference engine that orchestrates reference injection | init.py |
fish_speech/utils/schema.py |
Pydantic models including ServeReferenceAudio and ServeTTSRequest |
schema.py |
tools/webui/inference.py |
Gradio interface wrapper for reference audio handling | inference.py |
tools/server/views.py |
HTTP endpoints for reference management and TTS generation | views.py |
Summary
- Voice cloning in Fish-Speech uses in-context learning by feeding reference audio tokens into the LLAMA-based TTS model.
- ReferenceLoader (
fish_speech/inference_engine/reference_loader.py) handles encoding via the DAC encoder and maintains two cache layers:ref_by_idfor filesystem references andref_by_hashfor inline audio data. - Two workflows exist: persistent references (uploaded via
/v1/references/addand referenced by ID) and one-off cloning (inline base64 audio in the TTS request). - Integration points include the Gradio Web UI (
tools/webui/inference.py) and the REST API (tools/server/views.py), both utilizing the coreTTSInferenceEngineclass.
Frequently Asked Questions
How long should the reference audio be for optimal voice cloning?
Use 5–10 seconds of clear, single-speaker speech. The ReferenceLoader encodes this audio into discrete tokens via the DAC encoder, and the LLAMA model uses these tokens as in-context examples. Audio shorter than 3 seconds may not capture sufficient speaker characteristics, while very long clips increase encoding time without proportional quality gains.
What is the difference between reference_id and inline references in the API?
reference_id refers to a pre-saved reference folder under ./references/<id>/ that was created via the /v1/references/add endpoint. The server caches these encodings in ref_by_id for reuse across multiple requests. Inline references are base64-encoded audio bytes sent directly in the JSON payload using the ServeReferenceAudio schema; these are processed by load_by_hash and cached by SHA-256 hash, making them ideal for one-off cloning without filesystem persistence.
Can I use multiple reference audio clips for a single voice cloning request?
Yes, both reference_id and inline references support multiple clips. When using reference_id, place multiple audio files in the ./references/<id>/ directory; load_by_id will encode all of them and concatenate their tokens. For inline mode, pass a JSON array with multiple ServeReferenceAudio objects. The model treats these as multiple in-context examples, potentially improving speaker similarity.
How does Fish-Speech handle caching to improve performance?
Fish-Speech implements a two-tier caching strategy in ReferenceLoader. The ref_by_id dictionary caches encodings for filesystem-based references keyed by the user-provided ID, while ref_by_hash caches inline references keyed by SHA-256 hashes of the raw audio bytes. When use_memory_cache is enabled (the default), subsequent requests with identical audio bypass the expensive DAC encoding step and reuse the cached tokens directly, reducing latency significantly for repeated cloning of the same voice.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →