Files
VoiceStudio/backend/core/personalities.py
T
Palash Debnath 5cab8e0149 feat: rename the product to VoiceStudio (previously OmniVoice-Studio)
Renames what users see. The app, the installers, the window title, the
docs and all 21 locales now say VoiceStudio, with "(previously
OmniVoice-Studio)" noted near the title of each doc surface so people
recognise it.

Deliberately NOT renamed, because renaming any of them silently breaks
an existing install — there is no legacy-path fallback anywhere in this
codebase:

  - bundle identifier com.debpalash.omnivoice-studio (MSI UpgradeCode,
    macOS TCC grants, managed venv, WebView localStorage, the
    single-instance lock)
  - data directories OmniVoice / .omnivoice and omnivoice.db
  - the ~150 OMNIVOICE_* environment variables
  - the X-OmniVoice-* HTTP headers (a wire protocol)
  - the published Docker image paths
  - the OmniVoice ENGINE, which is a model name and not this product

tests/test_identity_paths_survive_the_rename.py pins every one of those
so a future well-meaning sweep cannot orphan a user's library.

Linux .deb users install a new package name and should apt remove
omnivoice-studio; that note is in the changelog.
2026-08-07 01:30:58 +05:30

231 lines
9.6 KiB
Python

"""Built-in voice personality presets.
Each personality is a named bundle of TTS-instruct taxonomy tokens (gender,
age, pitch, accent, dialect, style — see ``omnivoice.utils.voice_design``)
that users can pick from a strip in the Voice Design tab.
Why taxonomy tokens and not prose
=================================
VoiceStudio's ``model.generate(instruct=...)`` runs every instruct string
through ``_resolve_instruct``, which splits on commas and validates each
item against a fixed vocabulary (e.g. ``male``, ``middle-aged``,
``moderate pitch``, ``british accent``). Anything outside that vocabulary
raises ``ValueError`` and surfaces to the user as a generation failure.
Earlier versions of this file shipped prose like
``"Speak clearly and professionally like a television news presenter"``
which always failed the validator — clicking *any* personality button in
the Design tab made the next Synthesize call crash (issue #89). Each
personality below maps to a comma-separated list of valid taxonomy
tokens so the picked instruct string is accepted by the model.
The ``description`` field is the human-readable explanation kept for
parity with the old field and for any future UI tooltips; the frontend
today only renders ``name`` and ``icon``. ``instruct`` is what flows into
``setInstruct`` on click.
"""
PERSONALITIES = [
{
"id": "narrator",
"name": "Narrator",
# Calm documentary-narrator vibe: settled adult, mid-low pitch.
"instruct": "middle-aged, low pitch",
"description": "Calm, authoritative documentary narrator with measured pacing",
"icon": "📖",
},
{
"id": "casual",
"name": "Casual",
# Relaxed, conversational — younger speaker, neutral pitch.
"instruct": "young adult, moderate pitch",
"description": "Relaxed, conversational tone like talking to a friend",
"icon": "😊",
},
{
"id": "news_anchor",
"name": "News Anchor",
# Clear, professional broadcaster — adult voice with US accent.
"instruct": "middle-aged, moderate pitch, american accent",
"description": "Clear, professional television news presenter",
"icon": "📺",
},
{
"id": "storyteller",
"name": "Storyteller",
# Dramatic bedtime-story flair — British accent reads well here.
"instruct": "middle-aged, moderate pitch, british accent",
"description": "Dramatic flair and engaging pacing like reading a bedtime story",
"icon": "🧙",
},
{
"id": "corporate",
"name": "Corporate",
# Polished business-presentation register.
"instruct": "middle-aged, moderate pitch",
"description": "Polished, professional tone suitable for business presentations",
"icon": "💼",
},
{
"id": "energetic",
"name": "Energetic",
# High-energy podcast host — younger speaker, higher pitch.
"instruct": "young adult, high pitch",
"description": "High energy and enthusiasm like a podcast host",
"icon": "⚡",
},
# ─────────────────────────────────────────────────────────────────────
# Demo presets — render as a 7-card grid in the empty Design tab.
# Each preset bundles taxonomy `attrs` (drives the CATEGORIES sliders),
# a sample `script` (pre-fills the textarea), and a `preview_url`
# pointing at a pre-rendered WAV under /demo_audio.
#
# `is_demo: True` is the marker the frontend filters on; the legacy 6
# entries above stay as chips, the entries below render as full cards.
# WAVs are generated by scripts/build_demos.sh — keep slugs in sync.
# ─────────────────────────────────────────────────────────────────────
{
"id": "audiobook_uk_narrator",
"name": "The Librarian",
"icon": "📚",
"description": "Warm UK audiobook narrator — measured, atmospheric.",
"instruct": "female, middle-aged, low pitch, british accent",
"attrs": {
"Gender": "female", "Age": "middle-aged", "Pitch": "low pitch",
"Style": "Auto", "EnglishAccent": "british accent",
"ChineseDialect": "Auto",
},
"script": (
"The clock tower struck thirteen, and for the first time in her "
"life, Eleanor wondered if she had been counting wrong all along."
),
"preview_url": "/demo_audio/voice_design/demo_voice_design_audiobook_uk_narrator.wav",
"language": "English",
"is_demo": True,
},
{
"id": "us_news_anchor",
"name": "The Anchor",
"icon": "📺",
"description": "Clear American broadcaster — primetime evening news.",
"instruct": "male, middle-aged, moderate pitch, american accent",
"attrs": {
"Gender": "male", "Age": "middle-aged", "Pitch": "moderate pitch",
"Style": "Auto", "EnglishAccent": "american accent",
"ChineseDialect": "Auto",
},
"script": (
"Good evening. Topping our broadcast tonight: scientists at the "
"coastal observatory have confirmed the signal is, in fact, repeating."
),
"preview_url": "/demo_audio/voice_design/demo_voice_design_us_news_anchor.wav",
"language": "English",
"is_demo": True,
},
{
"id": "indian_support_agent",
"name": "The Helpdesk",
"icon": "🎧",
"description": "Patient Indian-English customer-service voice.",
"instruct": "female, young adult, moderate pitch, indian accent",
"attrs": {
"Gender": "female", "Age": "young adult", "Pitch": "moderate pitch",
"Style": "Auto", "EnglishAccent": "indian accent",
"ChineseDialect": "Auto",
},
"script": (
"Thank you for calling VoiceStudio support. I can see your account "
"here. Let's get this sorted out together."
),
"preview_url": "/demo_audio/voice_design/demo_voice_design_indian_support_agent.wav",
"language": "English",
"is_demo": True,
},
{
"id": "gravelly_villain",
"name": "Captain Crusty",
"icon": "☠️",
"description": "Gravelly old sailor — cartoon villain energy, original character.",
"instruct": "male, elderly, very low pitch",
"attrs": {
"Gender": "male", "Age": "elderly", "Pitch": "very low pitch",
"Style": "Auto", "EnglishAccent": "Auto",
"ChineseDialect": "Auto",
},
"script": (
"You came a long way for an answer you already had. Sit. The "
"fire is warm, and the truth is not."
),
"preview_url": "/demo_audio/voice_design/demo_voice_design_gravelly_villain.wav",
"language": "English",
"is_demo": True,
},
{
"id": "aussie_podcaster",
"name": "The Podcaster",
"icon": "🎙️",
"description": "Aussie explainer-show host — quick, punchy, friendly.",
"instruct": "female, young adult, high pitch, australian accent",
"attrs": {
"Gender": "female", "Age": "young adult", "Pitch": "high pitch",
"Style": "Auto", "EnglishAccent": "australian accent",
"ChineseDialect": "Auto",
},
"script": (
"Right, so here's the wild bit. Nobody told the engineers the "
"satellite was supposed to be in orbit by Tuesday. Tuesday came and went."
),
"preview_url": "/demo_audio/voice_design/demo_voice_design_aussie_podcaster.wav",
"language": "English",
"is_demo": True,
},
{
"id": "bedtime_storyteller",
"name": "Junior Quacks",
"icon": "🦆",
"description": "Anxious squawky sidekick — cartoon nephew energy, original character.",
"instruct": "young adult, high pitch",
"attrs": {
"Gender": "Auto", "Age": "young adult", "Pitch": "high pitch",
"Style": "Auto", "EnglishAccent": "Auto",
"ChineseDialect": "Auto",
},
"script": (
"Once, in a town where every street was named after a kind of "
"bread, a small fox decided she was going to learn to play the cello."
),
"preview_url": "/demo_audio/voice_design/demo_voice_design_bedtime_storyteller.wav",
"language": "English",
"is_demo": True,
},
{
"id": "mandarin_sichuan",
"name": "The Sichuan Friend",
"icon": "🌶️",
"description": "四川话 — non-English showcase, dialect-aware design.",
"instruct": "female, young adult, moderate pitch, 四川话",
"attrs": {
"Gender": "female", "Age": "young adult", "Pitch": "moderate pitch",
"Style": "Auto", "EnglishAccent": "Auto",
"ChineseDialect": "四川话",
},
"script": "今天天气巴适得很,我们去吃火锅嘛!记得多加点豆芽。",
"preview_url": "/demo_audio/voice_design/demo_voice_design_mandarin_sichuan.wav",
"language": "Chinese",
"is_demo": True,
},
]
def get_personalities():
"""Return the full list of built-in personality presets."""
return PERSONALITIES
def get_personality(personality_id: str):
"""Look up a single personality by ID, or None."""
for p in PERSONALITIES:
if p["id"] == personality_id:
return p
return None