Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Custom Backends and Registries

The requirements stated in the C API’s chapters on custom backends and shared library backends apply to backends implemented in Python.

prism.CustomBackend

Base class of backends implemented in Python.

Syntax

class CustomBackend

Attributes

EXTRA_FEATURES

Feature bits that no method determines: the silence-trimming bits and the classification bits. A subclass that presents messages through a screen reader SHOULD set is_screen_reader; one that synthesizes speech SHOULD set is_speech_engine. Setting a bit that corresponds to a method raises ValueError at registration, and setting both classification bits raises prism.common.PrismInvalidParamError.

Remarks

A subclass is registered with RegistryBuilder.add_backend and is then used like any other backend. Prism instantiates the subclass with no arguments for each backend instance it creates, so the subclass MUST be constructible without arguments, and state shared between instances MUST be synchronized.

A subclass overrides the operations it supports. The feature bit of an operation is set exactly when the subclass overrides the method of the same name; operations that are not overridden raise prism.common.PrismNotImplementedError without calling Python. Methods are named after the members of PrismBackendVTable: prism.Backend.speaking is implemented by is_speaking, and prism.Backend.volume by get_volume and set_volume.

Each method MUST behave as described for the corresponding member of prism.Backend. Prism validates arguments first, so a method never receives text that is not valid UTF-8 or a voice parameter outside [0.0, 1.0]. A method MAY receive empty text from callers of the C API.

An exception raised by a method is printed to standard error and reported as an error: a prism.PrismError as its own code, NotImplementedError as PRISM_ERROR_NOT_IMPLEMENTED, UnicodeError as PRISM_ERROR_INVALID_UTF8, ValueError as PRISM_ERROR_INVALID_PARAM, and any other exception as PRISM_ERROR_INTERNAL. An exception raised by is_supported is reported as False.

The methods of one instance are never called concurrently, but MAY be called on any thread. Methods of different instances MAY be called concurrently.

Example

A backend that writes speech requests to standard output:

import prism

class Printer(prism.CustomBackend):
    def speak(self, text, interrupt):
        print(text)

with prism.RegistryBuilder() as builder:
    printer_id = builder.add_backend("Printer", Printer, priority=10)
    registry = builder.freeze()
context = prism.Context(registry)
backend = context.create(prism.BackendId(printer_id))
backend.speak("Hello from a custom backend")

CustomBackend.initialize

Prepares the instance for use.

Syntax

def initialize() -> None

Remarks

Called once, after construction and before any operation other than is_supported. The default does nothing. An implementation whose screen reader or speech engine is unavailable SHOULD raise prism.common.PrismBackendNotAvailableError.

CustomBackend.is_supported

Reports whether the backend is available.

Syntax

def is_supported() -> bool

Return Value

Whether the backend is available. The default is True.

Remarks

The result is reported as prism.BackendFeatures.is_supported_at_runtime, and is sampled by availability monitoring. This method MAY be called before initialize, and MUST NOT assume the instance is initialized. It is called on the sampling thread of contexts that monitor availability, and SHOULD return quickly.

CustomBackend.speak

Speaks text.

Syntax

def speak(text: str, interrupt: bool) -> None

Parameters

text

The text to speak.

interrupt

Whether to interrupt speech in progress.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.speak.

CustomBackend.speak_to_memory

Synthesizes text and passes the audio to emit.

Syntax

def speak_to_memory(text: str, emit: AudioCallback) -> None

Parameters

text

The text to synthesize.

emit

A function that takes the next portion of audio, as described under prism.AudioCallback.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.speak_to_memory. The method MUST NOT return until all audio has been passed to emit. Prism clamps samples to [-1.0, 1.0] and replaces non-finite values with 0.0 before delivering them. The channel count and sample rate passed to emit SHOULD match get_channels and get_sample_rate.

CustomBackend.braille

Shows text on a braille display.

Syntax

def braille(text: str) -> None

Parameters

text

The text to show.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.braille.

CustomBackend.output

Presents text through speech and braille.

Syntax

def output(text: str, interrupt: bool) -> None

Parameters

text

The text to present.

interrupt

Whether to interrupt output in progress.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.output. Prism does not derive this operation from speak and braille; a backend that supports output MUST override it.

CustomBackend.stop

Stops speech and discards queued speech.

Syntax

def stop() -> None

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.stop.

CustomBackend.pause

Pauses speech.

Syntax

def pause() -> None

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.pause.

CustomBackend.resume

Resumes paused speech.

Syntax

def resume() -> None

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.resume.

CustomBackend.is_speaking

Reports whether speech is in progress.

Syntax

def is_speaking() -> bool

Return Value

Whether speech is in progress.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.speaking.

CustomBackend.set_volume

Sets the volume.

Syntax

def set_volume(volume: float) -> None

Parameters

volume

The volume, from 0.0 to 1.0.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.volume.

CustomBackend.get_volume

Returns the volume.

Syntax

def get_volume() -> float

Return Value

The volume, from 0.0 to 1.0.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.volume.

CustomBackend.set_rate

Sets the rate.

Syntax

def set_rate(rate: float) -> None

Parameters

rate

The rate, from 0.0 to 1.0.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.rate.

CustomBackend.get_rate

Returns the rate.

Syntax

def get_rate() -> float

Return Value

The rate, from 0.0 to 1.0.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.rate.

CustomBackend.set_pitch

Sets the pitch.

Syntax

def set_pitch(pitch: float) -> None

Parameters

pitch

The pitch, from 0.0 to 1.0.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.pitch.

CustomBackend.get_pitch

Returns the pitch.

Syntax

def get_pitch() -> float

Return Value

The pitch, from 0.0 to 1.0.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.pitch.

CustomBackend.refresh_voices

Reloads the list of voices.

Syntax

def refresh_voices() -> None

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.refresh_voices.

CustomBackend.count_voices

Returns the number of voices.

Syntax

def count_voices() -> int

Return Value

The number of voices.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.voices_count.

CustomBackend.get_voice_name

Returns the name of a voice.

Syntax

def get_voice_name(voice_id: int) -> str

Parameters

voice_id

The index of the voice.

Return Value

The name of the voice. It MUST NOT contain a NUL character.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.get_voice_name.

CustomBackend.get_voice_language

Returns the language of a voice.

Syntax

def get_voice_language(voice_id: int) -> str

Parameters

voice_id

The index of the voice.

Return Value

The language of the voice, preferably a BCP 47 tag. It MUST NOT contain a NUL character.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.get_voice_language.

CustomBackend.set_voice

Selects a voice.

Syntax

def set_voice(voice_id: int) -> None

Parameters

voice_id

The index of the voice.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.voice.

CustomBackend.get_voice

Returns the index of the selected voice.

Syntax

def get_voice() -> int

Return Value

The index of the selected voice.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.voice.

CustomBackend.get_channels

Returns the number of audio channels.

Syntax

def get_channels() -> int

Return Value

The number of channels.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.channels.

CustomBackend.get_sample_rate

Returns the audio sample rate.

Syntax

def get_sample_rate() -> int

Return Value

The sample rate, in hertz.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.sample_rate.

CustomBackend.get_bit_depth

Returns the native audio bit depth.

Syntax

def get_bit_depth() -> int

Return Value

The bit depth.

Exceptions

NotImplementedError

Unless overridden.

Remarks

Implements prism.Backend.bit_depth.

prism.RegistryBuilder

Builds a registry.

Syntax

class RegistryBuilder()

Exceptions

MemoryError

If the builder cannot be created.

Remarks

This class wraps prism_registry_builder_new and prism_registry_builder_free, and MAY be used as a context manager. A new builder contains the compiled-in backends, whose names and identifiers therefore cannot be used by other backends. Adding a backend creates no instance of it.

freeze moves the builder’s backends to a new registry and spends the builder; a spent builder can only be closed. The builder is closed by close, when the object is destroyed, or at the end of a with statement.

A builder is not thread-safe.

RegistryBuilder.add_backend

Adds a backend implemented in Python.

Syntax

def add_backend(
    name: str,
    backend: type[CustomBackend],
    *,
    priority: int = 100,
) -> int

Parameters

name

The name of the backend. It MUST be non-empty and unique in the builder. It determines the identifier.

backend

The subclass of CustomBackend that implements the backend.

priority

The priority of the backend. It MUST NOT be negative. The priorities of built-in backends are available from prism.Context.priority_of.

Return Value

The identifier of the backend, which prism.BackendId converts.

Exceptions

TypeError

If backend is not a subclass of CustomBackend.

RuntimeError

If the builder is spent or closed.

ValueError

If EXTRA_FEATURES sets a bit that corresponds to a method.

PrismInvalidParamError

If name is empty, priority is negative, or both classification bits are set.

PrismInvalidOperationError

If the builder already contains a backend with the same name or identifier.

Remarks

This method wraps prism_registry_builder_add_backend. The backend’s features are derived from backend as described under CustomBackend. backend is kept alive for as long as the registry or any instance created from it.

RegistryBuilder.add_library

Adds the backends of a shared library plugin.

Syntax

def add_library(
    path: str | os.PathLike[str],
    *,
    priority_override: int | None = None,
) -> int

Parameters

path

The path of the library.

priority_override

A non-negative priority for every backend in the library, or None to use the priorities the library declares.

Return Value

The number of backends added.

Exceptions

RuntimeError

If the builder is spent or closed.

TypeError

If path is not a str or a path-like object yielding one.

PrismInvalidParamError

If path is empty or contains a NUL character, or the library describes an inconsistent backend.

ValueError

If priority_override is negative.

PrismNotImplementedError

If the platform does not support shared library backends.

PrismLibraryLoadFailedError

If the library cannot be opened.

PrismLibraryInvalidError

If the library is not a Prism plugin.

PrismIncompatibleAbiError

If the plugin is incompatible with this version of Prism.

PrismInvalidOperationError

If the builder already contains a backend with the name or identifier of one in the library.

Remarks

This method wraps prism_registry_builder_add_library. If any error occurs, no backend is added and the library is unloaded. Otherwise one backend is added for each backend the plugin describes, in the plugin’s order. The library remains loaded while the registry or any instance created from it exists.

Loading a library runs its code with the privileges of the application, before Prism checks that it is a plugin. Applications MUST load only trusted libraries, from locations that less privileged users cannot write.

The identifiers of the added backends are determined by their names, and are available from prism.Context.id_of once the registry is frozen.

RegistryBuilder.freeze

Creates a registry from the builder.

Syntax

def freeze() -> Registry

Return Value

The registry.

Exceptions

RuntimeError

If the builder is spent or closed, or the registry cannot be created.

Remarks

This method wraps prism_registry_freeze. The builder’s backends are moved to the registry, and the builder is spent.

RegistryBuilder.close

Releases the builder.

Syntax

def close() -> None

Remarks

This method wraps prism_registry_builder_free. Backends in a builder closed without being frozen are discarded. Closing a closed builder does nothing.

prism.Registry

A registry produced by RegistryBuilder.freeze.

Syntax

class Registry(ptr: ffi.CData)

Parameters

ptr

A PrismRegistry * whose reference the object owns.

Remarks

A registry is passed to prism.Context, and cannot be changed. The object holds a reference to the registry, released through prism_registry_release by close, when the object is destroyed, or at the end of a with statement. Each context holds a reference of its own, so the object MAY be closed once the registry is in use by a context.

Used by RegistryBuilder.freeze.

Registry.close

Releases the object’s reference to the registry.

Syntax

def close() -> None

Remarks

Closing a closed registry does nothing.