Merlino Voice
Merlino Voice is Mike Merlino's custom-cloned voice on Fish Audio. Distinct from generic Fish Audio TTS because the voice model is identity-locked — it sounds like Mike. Used for branded agency communications where personal touch matters: outbound voicemails, video intros, podcast intros, branded IVR greetings, sales agent self-introductions.
For platform-level Fish Audio reference (auth, API surface, model selection), see Fish Audio TTS.
When to use Merlino Voice vs generic TTS
| Use case | Voice | |---|---| | Generic IVR ("press 1 for sales") | Generic ElevenLabs/Deepgram | | Mike's personal cold-call voicemail drop | Merlino Voice | | Agency podcast intro | Merlino Voice | | Client-specific AI receptionist | Custom voice per client | | Mike speaking at an event (recorded TTS) | Merlino Voice | | Mass automated alerts | Generic TTS (cheaper, more neutral) |
Cloned voice carries a personal-brand signature. Use sparingly — overuse dilutes the "this is from Mike personally" effect.
Configuration
| Parameter | Value |
|---|---|
| Voice ID | (Fish Audio model ID — kept in D:/Ecosystem/secrets/MASTER_API_KEYS.env) |
| Default speed | 1.0 |
| Default energy | 0.7 |
| Default chunk length | 200 |
| Format | mp3 (16-bit) for general use, wav for SignalWire play, opus for streaming |
API key lives at FISH_AUDIO_API_KEY in the secrets file.
Generation example
import os
import requests
FISH_API = "https://api.fish.audio/v1/tts"
FISH_KEY = os.environ["FISH_AUDIO_API_KEY"]
MERLINO_VOICE_ID = os.environ["MERLINO_VOICE_ID"]
def generate_merlino_audio(text, output_path, format="mp3", speed=1.0, energy=0.7):
response = requests.post(
FISH_API,
headers={
"Authorization": f"Bearer {FISH_KEY}",
"Content-Type": "application/json",
},
json={
"text": text,
"reference_id": MERLINO_VOICE_ID,
"format": format,
"mp3_bitrate": 128,
"chunk_length": 200,
"normalize": True,
"latency": "balanced", # or "normal" for higher quality, slower
}
)
response.raise_for_status()
with open(output_path, "wb") as f:
f.write(response.content)
return output_path
Typical generation time: 800ms - 2s for short utterances under 200 characters, 3-8s for paragraph-length text.
SignalWire integration via play
For SWML scripts that need a Merlino-voiced introduction or message, pre-generate the audio and host it, then reference via the SWML play verb:
version: 1.0.0
sections:
main:
- answer: {}
- play:
url: https://cdn.merlinoai.com/voice/intro-mike-v3.mp3
- connect:
to: sip:agent@pbx.merlinoai.com
Pre-generating is better than on-the-fly generation inside the call because:
- Eliminates Fish Audio API latency from the call path
- Allows audio quality tuning (multiple takes, normalization)
- CDN delivery is faster than re-rendering
For dynamic per-call content (using caller's name, etc.), use Fish Audio's WebSocket streaming endpoint and tap output directly into SignalWire — but this adds 1-2 seconds of perceived latency.
Voicemail drop with Merlino Voice
A signature use case. Mike's personal-sounding voicemail beats generic dialer voicemails.
# voicemail-drop-merlino.yaml
version: 1.0.0
sections:
main:
- answer:
answer_on: machine_end_beep
- play:
url: https://cdn.merlinoai.com/voice/cold-drop-v7.mp3
- hangup: {}
Outbound call with AMD targeting voicemail:
client.calls.create(
to=lead.phone,
from_=OUTBOUND_DID,
url="https://your.api/voicemail-drop-merlino.xml",
machine_detection="DetectMessageEnd",
)
See voicemail drop for the full pattern. Cloned-voice voicemail drops require even tighter TCPA discipline because they're personal-sounding — the recipient assumes a real person and reacts more strongly when they discover it's pre-recorded.
Generation patterns
Pattern 1: Pre-render variations to CDN
Common patterns rendered ahead of time and uploaded to CDN:
texts=(
"intro-mike-v1:Hey, this is Mike Merlino. Just calling about your business."
"intro-mike-v2:Hi, it's Mike at Merlino AI. Quick question for you."
"vmdrop-mike-v1:Hey, Mike Merlino here. Missed you — give me a call back when you have a sec."
)
for entry in "${texts[@]}"; do
name="${entry%%:*}"
text="${entry#*:}"
python generate.py --text "$text" --out "/tmp/$name.mp3"
aws s3 cp "/tmp/$name.mp3" "s3://cdn-merlinoai/voice/$name.mp3" --acl public-read
done
Pattern 2: Personalized per-recipient render
When the message needs to address the recipient by name:
def generate_personalized(recipient_first_name, output_path):
text = f"Hey {recipient_first_name}, it's Mike. Got a sec to talk?"
return generate_merlino_audio(text, output_path)
Cache renders by text-content hash to avoid re-generating identical messages.
Pattern 3: Live during-call generation (advanced)
For dynamic content during an active call, use the AI verb with Fish Audio configured as the TTS provider:
- ai:
prompt:
text: "You are Mike Merlino, agency owner. Be friendly and direct."
languages:
- name: English (Merlino)
code: en
voice: fishaudio.${MERLINO_VOICE_ID}
engine: fishaudio
SWAIG:
functions: []
SignalWire's AI verb supports Fish Audio as a TTS engine. Latency is higher than ElevenLabs/Deepgram. Best when the personal voice signature is more important than perfect responsiveness.
Quality tuning
| Issue | Cause | Fix |
|---|---|---|
| Robotic cadence | chunk_length too short | Increase to 200-300 |
| Word emphasis wrong | Missing punctuation hints | Add commas, em-dashes for natural pauses |
| Volume too low for phone playback | TTS output is line-level, phone expects -6 to -3 dB | Normalize with ffmpeg -af "loudnorm=I=-16:LRA=11:TP=-1.5" |
| Sibilance harsh | Source recording had it | De-ess with ffmpeg -af "highshelf=f=6000:g=-3" |
| Mismatched pitch across renders | Energy/speed varied between sessions | Lock parameters in a single config |
Audio post-processing for telephony
Phone networks downsample to 8 kHz. To make Merlino Voice sound consistent over phone:
# Render at 24 kHz
fish_audio_render --voice $MERLINO_ID --text "..." --format wav -o raw.wav
# Process for telephony
ffmpeg -i raw.wav \
-af "loudnorm=I=-16:LRA=11:TP=-1.5,highshelf=f=6000:g=-3" \
-ar 8000 -ac 1 -acodec pcm_mulaw \
telephony.wav
Result is 8 kHz mono mulaw, the exact format G.711 carriers use. Plays without resampling latency.
Cost
Fish Audio TTS pricing (as of 2025-11):
| Item | Cost | |---|---| | Per character | ~$0.000015 | | Per second of audio | ~$0.0008 | | Voice clone training (one-time) | $50-200 depending on tier | | Custom voice retention | Included in plan |
A 30-second cold-voicemail message: ~$0.025 per render. Cache aggressively.
Voice integrity guardrails
The Merlino Voice clone is a brand asset. Misuse damages the brand. Operational rules:
- Never use for impersonation — voice clones generating content "as Mike" without his approval is a hard no.
- Audit log every render —
D:/Ecosystem/logs/merlino-voice-renders.logrecords every API call with text content and use case. - Approved sequences only — production sequences using Merlino Voice are version-controlled in
D:/Ecosystem/voice-scripts/. - Time-bounded access — staff API key access to the Merlino voice ID has a clear approval chain.
Common pitfalls
- Generating on-the-fly during calls — adds 2-5 second latency. Pre-render to CDN.
- No normalization on phone audio — Merlino Voice generated at -23 LUFS sounds quiet on phone. Always loudnorm to -16 LUFS.
- Calling Fish Audio from inside SWML directly — works but not always reliable. Pre-render or use the AI verb with Fish Audio engine.
- Misusing for marketing voicemail without consent — see voicemail drop. TCPA is strict; cloned voice raises the stakes.
- Voice drift over Fish Audio version updates — Fish Audio occasionally updates models. Periodically re-render reference samples and compare to ensure consistency.
Related patterns
- Fish Audio TTS — generic Fish Audio API reference
- Voicemail drop — pre-recorded outbound voicemail
- SignalWire AI receptionist — AI voice agent with custom TTS
References
- Fish Audio API documentation
- SignalWire SWML
playverb - SignalWire AI verb language/voice configuration