feat(onboarding): refresh the bundled demo voice

This commit is contained in:
debpalash
2026-08-11 19:02:38 +00:00
parent 008c8a70a6
commit 5832a81bb6
6 changed files with 126 additions and 34 deletions
Binary file not shown.
Binary file not shown.
+38 -9
View File
@@ -3,10 +3,11 @@ isn't empty on initial launch. Runs once; skips silently if any
profiles already exist.
"""
import filecmp
import logging
import os
import shutil
import time
import logging
from core.db import get_db
from core.config import VOICES_DIR
@@ -23,16 +24,15 @@ DEMO_PROFILE_NAME = "VoiceStudio Demo Voice"
# Must match the actual spoken content of backend/assets/samples/demo_voice.wav.
# Regenerated by scripts/build_demos.sh — update both files in lockstep.
DEMO_REF_TEXT = (
"Hi, I'm the VoiceStudio demo voice. Everything you hear me say from now on "
"was synthesized on your own machine. No cloud, no account, just you and "
"the model."
"Hey. I'm the VoiceStudio demo voice. I was made right here, on your "
"machine: private, local, and ready whenever you are."
)
_DEMO_DESCRIPTION = (
"A neutral reference voice bundled with VoiceStudio. Clone it to hear how "
"the engine sounds on your machine, then replace it with your own "
"recording when you're ready."
"An original warm, low cinematic voice bundled with VoiceStudio. Clone it "
"to hear how the engine sounds on your machine, then replace it with your "
"own recording when you're ready."
)
@@ -43,8 +43,14 @@ def _backfill_demo_metadata(conn):
try:
conn.execute(
"UPDATE voice_profiles SET description=?, is_demo=1, ref_text=? "
"WHERE id=? AND (is_demo=0 OR description='' OR ref_text!=?)",
(_DEMO_DESCRIPTION, DEMO_REF_TEXT, DEMO_PROFILE_ID, DEMO_REF_TEXT),
"WHERE id=? AND (is_demo=0 OR description!=? OR ref_text!=?)",
(
_DEMO_DESCRIPTION,
DEMO_REF_TEXT,
DEMO_PROFILE_ID,
_DEMO_DESCRIPTION,
DEMO_REF_TEXT,
),
)
conn.commit()
except Exception as e:
@@ -52,11 +58,34 @@ def _backfill_demo_metadata(conn):
logger.debug("Demo backfill skipped: %s", e)
def _refresh_demo_audio(conn):
"""Keep the canonical demo profile in sync with the bundled render."""
try:
row = conn.execute(
"SELECT 1 FROM voice_profiles WHERE id=? AND is_demo=1",
(DEMO_PROFILE_ID,),
).fetchone()
if not row or not os.path.isfile(_DEMO_AUDIO):
return
os.makedirs(VOICES_DIR, exist_ok=True)
dest = os.path.join(VOICES_DIR, f"{DEMO_PROFILE_ID}.wav")
if not os.path.isfile(dest) or not filecmp.cmp(
_DEMO_AUDIO, dest, shallow=False
):
shutil.copy2(_DEMO_AUDIO, dest)
logger.info("Refreshed bundled demo voice audio")
except Exception as e:
# The demo must never make startup fail; a fresh seed below can still
# repair it once the schema and data directory are available.
logger.debug("Demo audio refresh skipped: %s", e)
def seed_sample_project():
"""Create the demo voice profile if no profiles exist yet."""
conn = get_db()
try:
_backfill_demo_metadata(conn)
_refresh_demo_audio(conn)
count = conn.execute("SELECT COUNT(*) FROM voice_profiles").fetchone()[0]
if count > 0:
return # Not first run — skip
+1 -1
View File
@@ -102,7 +102,7 @@ echo "── Voice cloning demo (24kHz mono 16-bit) ─────────
# Voice: Samantha (en_US adult female, the macOS default — clean, neutral,
# warm). Reference text from the cloning spec.
render "Samantha" \
"Hi, I'm the VoiceStudio demo voice. Everything you hear me say from now on was synthesized on your own machine. No cloud, no account, just you and the model." \
"Hey. I'm the VoiceStudio demo voice. I was made right here, on your machine: private, local, and ready whenever you are." \
"${SAMPLES_DIR}/demo_voice.wav" 24000
# Pre-rendered clone output — same voice, different text. Used when the user
+16 -8
View File
@@ -51,15 +51,20 @@ sys.path.insert(0, str(BACKEND_DIR))
# Cloning demo — must match scripts/build_demos.sh exactly so the manifest
# stays in sync with what the bootstrap script produced.
CLONE_REF_TEXT = (
"Hi, I'm the VoiceStudio demo voice. Everything you hear me say from now on "
"was synthesized on your own machine. No cloud, no account, just you and "
"the model."
"Hey. I'm the VoiceStudio demo voice. I was made right here, on your "
"machine: private, local, and ready whenever you are."
)
CLONE_OUTPUT_TEXT = (
"Welcome aboard. I was just a three-second clip a moment ago. Now I can "
"say anything you'd like, in your voice or mine."
)
# Original demo identity: a warm, smoky cinematic alto expressed only through
# OmniVoice's supported taxonomy. It is deliberately not based on, trained on,
# or named after a real performer.
CLONE_VOICE_INSTRUCT = "female, young adult, low pitch, american accent"
CLONE_RENDER_STEPS = 48
def _git_sha() -> str:
try:
@@ -103,17 +108,19 @@ def render_cloning(model, args):
"""
print("── Cloning demo ─────────────────────────────────────")
sr = getattr(model, "sampling_rate", 24000)
from omnivoice.utils.common import fix_random_seed
# 1) Reference clip — synthesized with a "neutral female narrator"
# instruct so the timbre is the same across re-renders.
# 1) Reference clip — synthesized as an original cinematic alto so the
# timbre is distinctive while remaining reproducible and rights-safe.
out_ref = SAMPLES_DIR / "demo_voice.wav"
if args.skip_existing and out_ref.exists():
print(f" · skip (exists): {out_ref.name}")
else:
fix_random_seed(42)
audios = model.generate(
text=CLONE_REF_TEXT,
instruct="female, middle-aged, moderate pitch, american accent",
num_step=24,
instruct=CLONE_VOICE_INSTRUCT,
num_step=CLONE_RENDER_STEPS,
)
_save_wav(audios[0], sr, out_ref)
print(f"{out_ref.name} ({sr} Hz, omnivoice)")
@@ -125,11 +132,12 @@ def render_cloning(model, args):
if args.skip_existing and out_clone.exists():
print(f" · skip (exists): {out_clone.name}")
else:
fix_random_seed(42)
audios = model.generate(
text=CLONE_OUTPUT_TEXT,
ref_audio=str(out_ref),
ref_text=CLONE_REF_TEXT,
num_step=24,
num_step=CLONE_RENDER_STEPS,
)
_save_wav(audios[0], sr, out_clone)
print(f"{out_clone.name} ({sr} Hz, cloned)")
+71 -16
View File
@@ -1,11 +1,8 @@
"""First-run onboarding seeds the demo voice profile (#621).
The demo clip `backend/assets/samples/demo_voice.wav` is a build artifact that
was never committed, so it shipped absent from installs — onboarding then logged
"Demo audio not found" and seeded nothing, leaving an empty Launchpad. The fix
commits the clip (it's un-ignored in .gitignore and bundled via the Tauri
`backend` resource). These tests pin that the asset is present + valid and that
onboarding actually seeds the demo profile from it.
The demo reference and pre-rendered clone are build artifacts. If either ships
absent, onboarding is empty or its no-engine preview fails. These tests pin that
both assets are present + valid and that onboarding seeds the demo profile.
"""
import os
import sqlite3
@@ -20,6 +17,10 @@ _WAV = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"backend", "assets", "samples", "demo_voice.wav",
)
_CLONE_WAV = os.path.join(
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
"backend", "assets", "samples", "demo_clone_output.wav",
)
def test_demo_clip_is_committed_and_valid():
@@ -34,6 +35,18 @@ def test_demo_clip_is_committed_and_valid():
assert w.getframerate() > 0
def test_demo_clone_preview_is_committed_and_valid():
"""The no-engine fallback must ship as real generated audio too."""
assert os.path.isfile(_CLONE_WAV), (
f"{_CLONE_WAV} is missing — regenerate with "
"scripts/render_demos_omnivoice.py --only cloning and commit it."
)
with wave.open(_CLONE_WAV, "rb") as w:
assert w.getnframes() > 0, "demo_clone_output.wav has no audio frames"
assert w.getframerate() == 24000
assert w.getnchannels() == 1
_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
@@ -49,16 +62,19 @@ def test_demo_clip_is_not_gitignored():
if shutil.which("git") is None:
import pytest
pytest.skip("git not available")
rel = "backend/assets/samples/demo_voice.wav"
# `git check-ignore` exits 0 (and echoes the path) when the path IS ignored.
res = subprocess.run(
["git", "check-ignore", rel], cwd=_ROOT,
capture_output=True, text=True,
)
assert res.returncode != 0 and not res.stdout.strip(), (
f"{rel} is gitignored — the un-ignore allowlist in .gitignore was lost. "
"Restore `!backend/assets/samples/*.wav` or the clip drops from builds."
)
for rel in (
"backend/assets/samples/demo_voice.wav",
"backend/assets/samples/demo_clone_output.wav",
):
# `git check-ignore` exits 0 (and echoes the path) when the path IS ignored.
res = subprocess.run(
["git", "check-ignore", rel], cwd=_ROOT,
capture_output=True, text=True,
)
assert res.returncode != 0 and not res.stdout.strip(), (
f"{rel} is gitignored — the un-ignore allowlist in .gitignore was lost. "
"Restore `!backend/assets/samples/*.wav` or the clip drops from builds."
)
def test_backend_is_a_bundled_tauri_resource():
@@ -127,3 +143,42 @@ def test_seed_is_noop_when_profiles_exist(tmp_path, monkeypatch):
n = c.execute("SELECT COUNT(*) FROM voice_profiles").fetchone()[0]
c.close()
assert n == 1 # unchanged — no demo seeded on a non-empty DB
def test_existing_demo_profile_receives_the_latest_bundled_voice(tmp_path, monkeypatch):
db_path = str(tmp_path / "ob.db")
conn = sqlite3.connect(db_path)
conn.execute(_table_sql())
conn.execute(
"INSERT INTO voice_profiles (id, name, is_demo, description, ref_text) "
"VALUES (?, ?, 1, 'old neutral description', ?)",
(
onboarding.DEMO_PROFILE_ID,
onboarding.DEMO_PROFILE_NAME,
onboarding.DEMO_REF_TEXT,
),
)
conn.commit()
conn.close()
bundled = tmp_path / "bundled.wav"
bundled.write_bytes(b"new omnivoice demo")
voices = tmp_path / "voices"
voices.mkdir()
copied = voices / f"{onboarding.DEMO_PROFILE_ID}.wav"
copied.write_bytes(b"old demo")
monkeypatch.setattr(onboarding, "get_db", lambda: sqlite3.connect(db_path))
monkeypatch.setattr(onboarding, "VOICES_DIR", str(voices))
monkeypatch.setattr(onboarding, "_DEMO_AUDIO", str(bundled))
onboarding.seed_sample_project()
assert copied.read_bytes() == bundled.read_bytes()
conn = sqlite3.connect(db_path)
description = conn.execute(
"SELECT description FROM voice_profiles WHERE id=?",
(onboarding.DEMO_PROFILE_ID,),
).fetchone()[0]
conn.close()
assert description == onboarding._DEMO_DESCRIPTION