Skip to main content

Prompt audio

The prompts under sounds/prompts/en/ are the fragments voicemail and the menus assemble — "you have" + "three" + "new messages". They are generated by an operator, committed, and played from disk.

Synthesis never happens during a call. Everything the dialplan plays is validated and preloaded when the PBX starts, so a bad file stops the process instead of a conversation; putting a third-party API in that path would trade that for dead air on an outage. It also means the API key never has to exist in production.

Generating a set

LYNO_ELEVENLABS_API_KEY=… ./bin/lyno -tts -manifest configs/prompts-en.yaml

-tts is a mode flag on the main binary, not a subcommand. It takes precedence over everything else, including -configure, and exits when it is done.

FlagPurpose
-ttsEnter prompt-generation mode and exit.
-manifestA YAML prompt set to generate in bulk.
-textOne sentence to synthesise. Requires -out.
-outDestination WAV for -text.
-voiceVoice id, overriding the configuration and the manifest.
-modelModel id, same.
-forceRegenerate even when nothing changed.
-voicesList the voices the key can use, and exit.
./bin/lyno -tts -voices # what the key can use
./bin/lyno -tts -text "We are closed" -out sounds/closed.wav

-tts with none of -text+-out, -manifest or -voices is an error. Within -tts, -voices wins over -manifest, which wins over -text. The whole run is bounded at 30 minutes.

The manifest

configs/prompts-en.yaml
voice: mwkFMsRX1kc5niMwNsbT
model: eleven_multilingual_v2
out_dir: ../sounds/prompts/en
prompts:
- {name: digit-3, text: "three"}
- {name: vm-youhave, text: "You have"}
- {name: vm-options, text: "To replay this message, press 1. To save it, press 2. …"}
KeyRequiredMeaning
out_diryesWhere the WAVs land. A relative path resolves against the manifest.
voice, modelnoDefaults for the whole set.
settingsnoVoice settings for the whole set.
prompts[].nameyesThe filename, without .wav.
prompts[].textyesWhat it says.
prompts[].voice, .settingsnoOverride the set wholesale, not field by field.

The manifest is decoded strictly, so a misspelt key is an error rather than a setting that quietly does nothing.

The shipped set holds 75 prompts: digit-0digit-20 and the tens, eight feature-* confirmations, goodbye, invalid, transfer, and the vm-* set the retrieval menu needs.

beep.wav is deliberately not in the manifest — it is a 1 kHz tone, not speech.

Reruns are cheap

Each prompt writes <name>.wav plus a <name>.wav.tts.json sidecar recording the text, voice, model, format and settings it was generated from. Only fragments whose inputs changed are synthesised again; the rest are recognised and skipped, so a rerun costs nothing.

-force regenerates regardless.

The API key

Read in this order:

  1. LYNO_ELEVENLABS_API_KEY in the environment;
  2. tts.apiKeyFile in the configuration — the contents of the file;
  3. tts.apiKey in the configuration.

The third is the worst of the three, because configs/ is usually under version control.

configVersion: lyno/v1
kind: System
spec:
tts:
provider: elevenlabs
apiKeyFile: /etc/lyno/elevenlabs.key
voice: mwkFMsRX1kc5niMwNsbT
model: eleven_multilingual_v2

ElevenLabs is the only provider. provider is validated against that one name; nothing dispatches on it.

What comes out

8 kHz mono 16-bit PCM WAV — exactly what the PBX plays. The format is checked three times: the response is validated before it is accepted, the file is tidied, and what actually landed on disk is validated again. That catches a provider quietly switching sample rate, and an HTML error page returned behind a 200.

After synthesis each fragment is trimmed and levelled to a common loudness. That is what keeps "you have" + "three" + "new messages" sounding like one reader across three files rather than three. Changing the target level invalidates the whole set.

Fragments are concatenated into a single playback at call time, so there is no audible gap between them.

Which prompts are actually used

Voicemail loads its fragments by name from voicemail.promptsDir. A fragment that is absent is skipped rather than treated as an error, so a partial set still announces something; one that is present and unplayable fails at startup.

Ten prompts in the shipped manifest are generated and never loaded: vm-menu, vm-login, vm-mailboxfull, vm-toolong, vm-greetingsaved, vm-recordgreeting, vm-message, vm-messages, vm-new and vm-old. They describe an Asterisk-style menu Lyno does not implement — the real menu prompt is vm-options. Removing them from your own manifest saves the synthesis cost.

Conversely the manifest has no weekday, month or ordinal fragments, which is why voicemail does not announce a date for anything older than yesterday.

Prompts for menus and greetings

Nothing forces you to use -tts. Any 8 kHz mono 16-bit PCM WAV works, from any source:

ffmpeg -i input.mp3 -ar 8000 -ac 1 -acodec pcm_s16le output.wav

-tts -text … -out … is a convenience for producing one of those in the same voice as the rest of the set.