Spaces:
Building
Building
Case Zero - initial public release (fully local: Qwen2.5-1.5B via llama.cpp + Supertonic, custom pixel-noir SPA via gradio.Server)
414dc55 | """Deterministic per-suspect voice assignment. | |
| A suspect maps to a stable Supertonic speaker (M1-M5 for men, F1-F5 for women) and | |
| prosody so they always sound the same - distinct cast voices from one on-device model. | |
| """ | |
| from __future__ import annotations | |
| import hashlib | |
| from ..schemas.suspect import Suspect, VoiceAssignment | |
| def _seed(*parts: str) -> int: | |
| return int.from_bytes(hashlib.sha256("|".join(parts).encode("utf-8")).digest()[:4], "big") | |
| def assign_voice(suspect: Suspect, *, engine: str = "supertonic") -> VoiceAssignment: | |
| """Assign a stable, GENDER-MATCHED voice. The Supertonic provider maps speaker_id | |
| 0-4 to male voices (M1-M5) and 5-9 to female voices (F1-F5).""" | |
| seed = _seed(suspect.sus_id, suspect.name) | |
| gender = ((suspect.visual.gender if suspect.visual else "") or "").lower() | |
| if gender.startswith("f"): | |
| speaker_id = 5 + (seed % 5) # female voices F1-F5 | |
| elif gender.startswith("m"): | |
| speaker_id = seed % 5 # male voices M1-M5 | |
| else: | |
| speaker_id = seed % 10 # unknown -> any of the 10 | |
| # Calmer, slower delivery for high-composure suspects; edgier for evasive ones. | |
| length_scale = round(0.95 + (seed % 20) / 100.0, 3) # 0.95 - 1.15 | |
| noise_w = round(0.70 + ((seed >> 8) % 20) / 100.0, 3) # 0.70 - 0.90 | |
| return VoiceAssignment( | |
| engine=engine, speaker_id=speaker_id, length_scale=length_scale, noise_w=noise_w, | |
| ) | |