Audio rendering¶
TriageSim can turn a finished dialogue into multi-speaker speech using XTTS-v2, giving nurse and patient distinct cloned voices. This closes the loop from structured EHR to spoken data.
Audio is entirely optional — the simulation and metrics work without it.
Installation¶
This pulls in torch and TTS, which are large. Install the extra only if you
need speech output.
Import path¶
triagesim/audio/__init__.py is empty, so SpeechRenderer must be imported
from the module itself:
Warning
from triagesim.audio import SpeechRenderer will raise ImportError. Use
the full module path above.
SpeechRenderer¶
| Parameter | Default | Meaning |
|---|---|---|
model_name | "tts_models/multilingual/multi-dataset/xtts_v2" | Any Coqui TTS model identifier. |
Construction downloads the model on first use and loads it into memory, so build one renderer and reuse it. Instantiating a renderer per utterance will reload the model every time.
Device selection¶
The device is chosen automatically at construction, in order of preference:
cuda— iftorch.cuda.is_available()mps— Apple Silicon; also setstorch.set_float32_matmul_precision("high")cpu— the fallback
GPU acceleration is only enabled for CUDA. On MPS the model runs but is not
passed the gpu=True flag, and on CPU expect synthesis to be several times
slower than real time.
render¶
path = renderer.render(
text="Can you tell me what happened this morning?",
speaker_wav="voices/nurse.wav",
out_path="audio/turn_000_nurse.wav",
language="en",
)
All arguments are keyword-only.
| Parameter | Type | Default | Meaning |
|---|---|---|---|
text | str | — | Text to synthesise. |
speaker_wav | str \| Path | — | Reference recording to clone the voice from. |
out_path | str \| Path | — | Destination WAV file. |
language | str | "en" | Language code passed to XTTS-v2. |
Returns the output Path. Parent directories are created automatically, so you
do not need to mkdir first.
Reference clips should be clean speech of roughly 6–20 seconds. Background noise in the reference is reproduced in the clone.
Rendering a full dialogue¶
Walk the artifact's history, giving each speaker its own reference voice:
from pathlib import Path
from triagesim.audio.renderer import SpeechRenderer
VOICES = {
"nurse": "voices/nurse.wav",
"patient": "voices/patient.wav",
}
renderer = SpeechRenderer()
out_dir = Path("audio") / artifact["run_id"]
rendered = []
for i, entry in enumerate(artifact["history"]):
actor = entry["actor"]
if actor not in VOICES:
continue # skip system events: released vitals, triage_end
rendered.append(
renderer.render(
text=entry["utterance"],
speaker_wav=VOICES[actor],
out_path=out_dir / f"{i:03d}_{actor}.wav",
language="en",
)
)
print(f"rendered {len(rendered)} utterances to {out_dir}")
Skipping entries whose actor is not in VOICES filters out system events —
released vitals and the triage_end marker have no utterance key and would
raise KeyError.
Numbering files by their index in history preserves turn order, which matters
when concatenating them into a single conversation.
Varying voices across runs¶
Persona attributes such as gender and ethnicity are a natural key into a
library of reference clips:
def voice_for(persona) -> str:
return f"voices/{persona.gender}_{persona.ethnicity}.wav".lower().replace(" ", "_")
This keeps the acoustic identity consistent with the persona that generated the text, which is the point of persona-conditioned generation in the first place.
Preparing text for synthesis¶
triagesim.utils.text_control provides enforce_response_budget(text,
response_length), which trims an utterance to a target length band. It is a
text-shaping helper rather than an audio one, but it is useful before synthesis
when you need utterance durations to stay within a budget.
Licensing and consent¶
Review the model licence and obtain voice consent
XTTS-v2 is distributed under the Coqui Public Model License, which carries its own restrictions — including on commercial use. TriageSim's Apache-2.0 licence does not extend to it. Review the model's terms before using synthesised output in a publication or product.
Voice cloning also raises consent questions independent of licensing. Use reference recordings only where the speaker has agreed to voice cloning for this purpose, prefer clips from datasets released for synthesis research, and never clone a real clinician's or patient's voice without explicit permission.
Synthetic data stays synthetic
Rendered dialogues are generated artefacts, not recordings of real encounters. Label them as such in any dataset you release so downstream users cannot mistake them for clinical recordings.