speaker_helper.engine module

Backend-agnostic text-to-speech engine interface and registry.

Module summary

speaker-helper’s value is a small, typed façade over a local TTS engine. The concrete engine (Voicebox today) is deliberately an implementation detail: the rest of the package — Speaker, the REST API, the CLI, the evaluation layer — talks only to the TTSEngine protocol defined here. Swapping or adding a backend is therefore a local change: write a class with four coroutines and register it.

Two backends ship in-tree:

  • voicebox — the real engine, VoiceboxClient.

  • mockMockEngine, a deterministic, dependency-light synthesiser that needs no server. It makes the test suite and the evaluation layer runnable in CI (and proves the abstraction: the same code paths drive any backend).

create_engine() dispatches on settings.backend. Third parties can add their own backend at runtime with register_backend().

Usage example

>>> from speaker_helper.config import Settings
>>> from speaker_helper.engine import create_engine
>>> eng = create_engine(Settings.from_mapping({"backend": "mock"}))
>>> type(eng).__name__
'MockEngine'

Author

Warith HARCHAOUI — https://linkedin.com/in/warith-harchaoui

class speaker_helper.engine.MockEngine(settings)[source]

Bases: object

A deterministic, serverless TTS backend for tests and evaluation.

It produces a real (decodable) WAV whose duration is proportional to the input length at a fixed speaking rate, and reports a compute time derived from a configured real-time factor. No audio model runs and no network call is made, so behaviour is reproducible and fast — ideal for CI, for exercising the evaluation layer’s anomaly checks, and for proving that engine-agnostic code paths work against any backend.

Parameters:

settings (Settings) – Used for language and engine labels and for the mock knobs (rtf, chars_per_sec, sample_rate, amplitude, frequency_hz) read via Settings.mock.

Notes

The audio is a quiet sine tone (not silence) so evaluation’s “empty audio” and “clipping” checks see a realistic, non-degenerate signal. Set mock.amplitude above 1.0 to deliberately synthesise a clipping signal for testing the anomaly detector.

async aclose()[source]

No-op: the mock holds no resources.

Return type:

None

async clone_voice(name, samples, *, language=None)[source]

Pretend to clone a voice; return a deterministic id (no audio model).

Validates that at least one sample is provided (matching the real engine’s contract) but performs no synthesis-model work, so cloning is testable without a server.

Parameters:
Return type:

str

async health()[source]

Return a static healthy document identifying the mock backend.

Return type:

dict

async list_voices(engine=None)[source]

Return a small fixed set of fake voices for engine.

Parameters:

engine (str | None)

Return type:

VoiceList

async synthesize(text, *, language=None)[source]

Synthesise text into a deterministic sine-tone WAV.

Parameters:
  • text (str) – Text to “speak” (must be non-empty). Only its length drives the output duration; content is not spoken.

  • language (str or None) – Override the configured language label on the result.

Returns:

A decodable WAV plus duration and a compute time equal to duration * rtf (so result.rtf reproduces the configured RTF).

Return type:

AudioResult

Raises:

ValueError – If text is empty or whitespace-only.

class speaker_helper.engine.TTSEngine(*args, **kwargs)[source]

Bases: Protocol

The minimal surface every speaker-helper backend must provide.

A backend is any object exposing these four coroutines. Everything else in the package is written against this protocol, never against a concrete engine, so the backend stays swappable.

Notes

@runtime_checkable lets isinstance(obj, TTSEngine) verify the method names are present (not their signatures) — handy in tests and factories.

async aclose()[source]

Release any held resources (HTTP clients, handles, …).

Return type:

None

async clone_voice(name, samples, *, language=None)[source]

Clone a voice from reference samples and return its id.

Implementations should be idempotent on name (reuse an existing clone rather than duplicating). A backend that cannot clone may raise NotImplementedError.

Parameters:
Return type:

str

async health()[source]

Return a backend health document (must include a status key).

Return type:

dict

async list_voices(engine=None)[source]

List preset voices for an engine (or the configured one).

Parameters:

engine (str | None)

Return type:

VoiceList

async synthesize(text, *, language=None)[source]

Synthesise text into one AudioResult.

Parameters:
  • text (str)

  • language (str | None)

Return type:

AudioResult

speaker_helper.engine.available_backends()[source]

Return the sorted names of all registered backends.

Return type:

list[str]

speaker_helper.engine.create_engine(settings)[source]

Instantiate the backend named by settings.backend.

Parameters:

settings (Settings) – Configuration; its backend field selects the engine.

Returns:

A ready-to-use engine instance.

Return type:

TTSEngine

Raises:

ValueError – If settings.backend names no registered backend.

speaker_helper.engine.register_backend(name, factory)[source]

Register (or override) a backend factory under name.

Parameters:
  • name (str) – Backend identifier used in settings.backend (case-insensitive).

  • factory (callable) – Settings -> TTSEngine. Called lazily by create_engine().

Return type:

None