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

Introduction

Prism is the Platform-Agnostic Reader Interface for Speech and Messages. The actual expansion is a mouthful, so we just call it Prism.

Prism aims to take all the TTS and screen reader engines out there and to combine them into a single, unified library, with a single, easy to understand interface. Prism does this by being comprised of back-ends. The front-end is the C library, documented in the next section of this reference manual; back-ends are the actual text-to-speech or screen reader communication interfaces themselves. Back-ends may choose what to implement as they see fit; almost nothing is required.

Conventions

The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “NOT RECOMMENDED”, “MAY”, and “OPTIONAL” in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.

Getting Started

This chapter describes how to obtain Prism and how to produce speech from an application. It is an introduction and does not state the complete contract of any function; the C API Reference remains authoritative. Requirements that apply when an application is distributed to other systems are described in the Application Deployment chapter.

Obtaining Prism

C and C++ applications obtain Prism from the GitHub releases page, where each platform has its own archive and the SDK archive contains every platform along with this manual. Prism is also available as the ethindp-prism port in vcpkg, whose speech-dispatcher feature enables the Speech Dispatcher backend on Linux, and can be built from source using the options described in the README.

Python applications use the prismatoid package on PyPI. Godot applications use the prismatoid-gdextension archive on the releases page, which contains the add-on for every supported platform. Bindings for other languages are maintained separately and are listed in the README.

The name of the Python package differs from the name of its module because the name prism on PyPI belongs to an unrelated project. Applications MUST therefore declare a dependency on prismatoid, and MUST use import prism in their source code. Installing the unrelated prism package in the same environment causes the two modules to conflict.

A first program

The following C program speaks a single sentence through the best backend available:

#include <prism.h>
#include <stdio.h>

int main(void) {
    PrismContext *ctx = prism_init(NULL);
    if (ctx == NULL) {
        fprintf(stderr, "Failed to initialize Prism\n");
        return 1;
    }
    PrismBackend *backend = prism_registry_create_best(ctx);
    if (backend == NULL) {
        fprintf(stderr, "No speech backend is available\n");
        prism_shutdown(ctx);
        return 1;
    }
    PrismError err = prism_backend_speak(backend, "Hello from Prism.", true);
    if (err != PRISM_OK) {
        fprintf(stderr, "Speech failed: %s\n", prism_error_string(err));
    }
    printf("Using %s. Press Enter to exit.\n", prism_backend_name(backend));
    getchar();
    prism_backend_free(backend);
    prism_shutdown(ctx);
    return err == PRISM_OK ? 0 : 1;
}

prism_init creates a context, through which the backend registry is accessed. prism_registry_create_best creates and initializes the highest-priority backend that is able to initialize, so the program does not need to know which screen readers or speech engines are present on the system. prism_backend_speak submits the text, with the final argument requesting that any speech in progress be interrupted. Finally, prism_backend_free and prism_shutdown release the resources the program obtained.

prism_registry_create_best returns NULL if no backend can be initialized. This happens in ordinary use, for example in a headless environment with no session services and no speech engine, and applications MUST handle it.

The program waits for input before exiting because prism_backend_speak MAY return before speech has finished, and because some backends discard queued and playing speech when the backend instance is freed. An application with a longer lifetime does not need to take any such measure, provided that it retains the backend for as long as it expects speech to continue. Waiting by polling prism_backend_is_speaking is not portable, as not all backends implement that function.

Compiling the program

Using a release package on Linux, the program can be compiled as follows, where $PRISM is the directory into which the archive was extracted:

cc hello.c -o hello -I"$PRISM/include" -L"$PRISM/dynamic/release/lib" -lprism -Wl,-rpath,"$PRISM/dynamic/release/lib"

Using a release package on Windows, the program can be compiled from a Developer Command Prompt as follows, where %PRISM% is the directory into which the archive was extracted:

cl hello.c /I"%PRISM%\include" /link "%PRISM%\dynamic\release\lib\prism.lib"

The file prism.dll from the package’s dynamic\release\bin directory MUST then be placed beside hello.exe or otherwise be resolvable through the process’s library search order.

A CMake project can locate Prism as a package, whether it was obtained through vcpkg, installed from source, or extracted from a release package:

find_package(prism CONFIG REQUIRED)
add_executable(hello hello.c)
target_link_libraries(hello PRIVATE prism::prism)

In the case of a release package, the directory of the desired configuration is supplied when the project is configured, as in -DCMAKE_PREFIX_PATH="$PRISM/dynamic/release".

This program is included in the Prism source tree as demos/hello_demo.c, and is built along with the other demonstration programs when PRISM_ENABLE_DEMOS is enabled.

Prism’s implementation is written in C++. A program which links the static form of the library MUST therefore also link the C++ standard library, most easily by using the C++ compiler driver for the link step. A CMake project does so automatically, provided that it enables the CXX language in its project command, even if the project’s own sources are written in C.

Python

The equivalent program in Python is as follows:

import prism

context = prism.Context()
backend = context.create_best()
backend.speak("Hello from Prism.", interrupt=True)
input(f"Using {backend.name}. Press Enter to exit.")

This program is included in the Prism source tree as demos/hello_demo.py.

Functions report failure by raising exceptions. create_best raises ValueError if no backend can be initialized, and other failures raise subclasses of prism.PrismError. Prism’s resources are released when the corresponding Python objects are destroyed, so an application MUST retain a reference to the backend for as long as it expects speech to continue.

Godot

The add-on is installed by extracting the archive into the root of a Godot project, which places it in addons/prismatoid. The add-on requires Godot 4.5 or later. No further configuration is required, and the extension registers a global singleton named Prism:

extends Node

var backend: GodotPrismBackend

func _ready() -> void:
	backend = Prism.create_best()
	if backend == null or not backend.is_valid():
		push_error("No speech backend is available")
		return
	var err := backend.speak("Hello from Prism.", true)
	if err != GodotPrismBackend.PRISM_OK:
		push_error("Speech failed: %s" % Prism.get_error_string(err))

Operations that can fail return a PrismError value, which applications SHOULD inspect. The backend is held in a member variable because a backend held only by a local variable is released when the function returns, which, on some backends, discards the speech it has queued.

Next steps

The Screen Readers and Speech Engines chapter explains which backend a program will receive and how an application should use it. The API Reference documents the C, Python, and Godot interfaces, and the Application Deployment chapter describes what is required for the application to function on other systems.

Screen Readers and Speech Engines

Prism’s backends fall into two categories, which behave differently and which serve different users. This chapter describes the distinction and the consequences it has for an application. The requirements of each individual backend are given in the chapter on backend-specific notes.

Definitions

A screen reader is an assistive technology which presents the contents of the screen to a user who cannot see it, generally through synthesized speech, braille, or both. A user of a screen reader has chosen the voice, speaking rate, verbosity, and braille configuration the screen reader employs, and relies on those choices for all of the software on the system. Examples include NVDA, JAWS, VoiceOver, Orca, and the Android screen readers.

A speech engine synthesizes audio from text on behalf of whichever program requests it. It has no knowledge of what is on the screen and does not make a system accessible on its own. Examples include SAPI, OneCore, AVSpeech, Speech Dispatcher, and the Web Speech API.

A backend reports which of the two it is through the PRISM_BACKEND_IS_SCREEN_READER and PRISM_BACKEND_IS_SPEECH_ENGINE bits of prism_backend_get_features. Every built-in backend sets exactly one of them. A custom backend MAY set neither, and MUST NOT set both.

An application that communicates with a screen reader asks that screen reader to present a message. The message is then subject to the screen reader’s own queue and configuration. An application that communicates with a speech engine instead requests that specific audio be produced, independently of any screen reader that may be running. The two can therefore speak over one another if an application uses a speech engine while the user’s screen reader is active.

Applications that present their interface through a platform’s accessibility API, such as UI Automation on Windows, NSAccessibility on Apple platforms, or AT-SPI on Linux, are read by the screen reader without using Prism at all. Prism is intended for those circumstances in which that is insufficient or impossible, such as announcements of events which do not coincide with a change of focus, and applications such as games that draw their own interface and expose nothing to the accessibility API.

Backend selection

Each backend in a registry has a priority, which can be retrieved through prism_registry_priority. Screen reader backends generally have a higher priority than speech engine backends. prism_registry_create_best and prism_registry_acquire_best attempt the backends in descending priority order and return the first which initializes successfully. A screen reader backend is unable to initialize when its screen reader is not running. Consequently, a program which uses these functions communicates with the user’s screen reader if there is one, and otherwise falls back on a speech engine. This is the appropriate behavior for most applications, and applications SHOULD NOT replace it without a specific reason.

An application MAY present a choice of backend to its users. The application SHOULD enumerate the registry for this purpose rather than assuming that a particular backend is present, since the set of backends varies according to the platform and the options with which Prism was built. Membership in a registry does not imply that a backend can be used; applications SHOULD inspect PRISM_BACKEND_IS_SUPPORTED_AT_RUNTIME before offering a backend, and MUST be prepared for its initialization to fail regardless.

The selected backend is not updated if the circumstances change. An application which obtained a speech engine backend continues to use it if a screen reader is later started, and a backend whose screen reader has exited begins to return errors. A long-running application that wishes to respond to such changes SHOULD use the facilities described in the chapter on background availability enumeration.

Speech, braille, and combined output

prism_backend_speak produces speech, and prism_backend_braille presents text on a braille display. prism_backend_output uses every modality that the backend supports. Since a user of a screen reader may be relying on braille alone, applications SHOULD use prism_backend_output for messages meant for the user unless they have a specific reason to confine a message to one modality. On a built-in backend without braille support, prism_backend_output is equivalent to prism_backend_speak. A custom backend implements output itself, and need not implement it at all.

Respecting the user’s configuration

Applications SHOULD NOT alter the rate, pitch, volume, or voice of a screen reader backend on their own initiative, even where the backend permits it. Experienced users commonly listen at rates which seem unintelligibly fast to others, and a value which an application considers comfortable may be unusable to them. Such changes SHOULD be made only in response to an explicit request by the user. The considerations are different for a speech engine backend, for which the application has itself assumed responsibility for these settings and SHOULD offer a means of adjusting them.

The interrupt parameter of prism_backend_speak and prism_backend_output likewise affects the user directly. Interrupting discards whatever the user was listening to, which may be something other than a message from the application. Applications SHOULD reserve it for messages which supersede the preceding speech, such as a change of selection in a menu, and SHOULD NOT interrupt in order to deliver messages which the user would otherwise hear in sequence.

Differences in capability

Backends differ considerably in the functions they implement. Most screen readers offer no means of reporting when an announcement has finished, for example, and the standard announcement facility on macOS cannot be cancelled. An application MUST NOT assume that an operation is implemented because it is implemented on another backend. It SHOULD inspect the bits returned by prism_backend_get_features where its behavior depends on a capability, and MUST handle PRISM_ERROR_NOT_IMPLEMENTED.

Applications whose logic depends on the end of an utterance, such as those which advance a sequence once a line of dialogue has been spoken, therefore cannot rely upon every backend to provide this information, and SHOULD provide an alternative means of proceeding, such as a key press.

Application Deployment

This chapter describes what is required for an application using Prism to function on systems other than the one on which it was built. It distinguishes between three kinds of dependency:

  • Build dependencies are required only when Prism itself is compiled from source. Applications using a release package or a binding’s package do not require them.
  • Load dependencies MUST be present on the end user’s system, or the Prism library cannot be loaded at all.
  • Backend dependencies are required only for a particular backend to function. If one is absent, that backend is unavailable and every other backend continues to function.

The screen readers and speech engines themselves are backend dependencies. Applications do not distribute them, and SHOULD NOT instruct users to replace their screen reader in order to use an application. An application that handles the absence of a backend as described in the Getting Started chapter functions with whichever backends the user’s system provides.

Licensing

Prism is licensed under the Mozilla Public License, version 2.0, and incorporates code from other projects under their own licenses. The files NOTICE and LICENSES are included in every release package. A distributor is responsible for complying with these licenses, and SHOULD include those files with the application.

Windows

Prism requires Windows 10 or later. The shared form of the library, prism.dll, MUST be deployed with the application, normally in the directory containing its executable. The static form requires no additional files. Neither form of the release package requires the Visual C++ Redistributable; the Godot add-on, whose C runtime is required to match that of godot-cpp, does.

The PC-Talker, ZDSR, and Boy PC Reader backends load a library at run time (PCTKUSR.dll, ZDSRAPI.dll or ZDSRAPI_x64.dll, and byctrl.dll or byctrl-x64.dll respectively), which is installed with the screen reader and which the application does not distribute. The GDK Speech backend loads xgameruntime.thunks.dll from the Microsoft GDK. Prism does not distribute this library; an application that uses the backend MUST deploy it, and its redistribution is governed by the GDK license.

An application that runs under Wine or Proton and wishes to use the speech services of the host system MUST also deploy the Wine bridges, as described in the Wine Bridges chapter.

Linux

Load dependencies

The release packages and the Godot add-on for Linux require glibc 2.34 or later and libstdc++.so.6 and libgcc_s.so.1 from GCC 12 or later. They also link libdbus-1.so.3, for the Orca backend and power management, and libglib-2.0.so.0, libgobject-2.0.so.0, and libgio-2.0.so.0, for the Spiel backend. The names of the packages which provide these libraries vary between distributions.

These libraries are present on nearly all desktop Linux systems, but they MAY be absent from a minimal container image. The Python wheels bundle their dependencies other than glibc and libstdc++, and consequently have no dependency on D-Bus or GLib being installed. The manylinux wheels require glibc 2.34 or later, and the musllinux wheels require musl 1.2 or later.

The release packages and the Godot add-on require glibc, and do not function on distributions based on musl, such as Alpine Linux. Applications that require this configuration SHOULD use the musllinux wheels or build Prism from source.

Backend dependencies

The Orca backend requires a session D-Bus connection and a running Orca 49 or later, the first release to implement Orca’s remote-control interface. The Speech Dispatcher backend requires libspeechd.so.2 from Speech Dispatcher 0.11.1 or later and a reachable Speech Dispatcher service. The Spiel backend requires libspiel-1.0.so.1, a session D-Bus connection, and at least one speech provider.

None of these are required for Prism to load, and Prism does not require that any particular one be present. A system on which only Speech Dispatcher is available, for instance because its distribution packages an older release of Orca, is a fully supported configuration. Applications therefore SHOULD NOT state that any of these backends is required, unless the application has selected a specific backend deliberately.

Spiel is not widely packaged at present. Application authors SHOULD NOT require their users to build it from source.

Diagnosing a failure to load

MessageMeaning
libdbus-1.so.3: cannot open shared object fileA load dependency is absent. The corresponding package MUST be installed. The same applies to the other libraries listed under load dependencies.
version 'GLIBC_2.xx' not foundThe system’s glibc is older than the build requires. The system is not supported by the release packages, and Prism MUST be built from source on a system at least as old as the oldest intended target.
version 'GLIBCXX_3.4.xx' not found or version 'CXXABI_1.3.xx' not foundThe system’s C++ runtime is older than the build requires. The remedy is the same as for glibc.

A build of Prism inherits its requirements from the system on which it is compiled. A build produced on a recent distribution MAY therefore fail to load on an older one, even when the release packages function on it.

Apple platforms

Prism has no load dependencies beyond system frameworks on Apple platforms. Both backends are subject to the requirements on the host process described in the chapter on backend-specific notes; in particular, a VoiceOver backend on macOS requires that the application own an NSWindow and run a main run loop, which a console program does not.

An application on macOS that wishes to use the legacy VoiceOver automation path MUST declare NSAppleEventsUsageDescription in its Info.plist. A notarized application MUST additionally hold the com.apple.security.automation.apple-events entitlement, and the path is not available to a sandboxed application. An application that does not meet these conditions uses the accessibility-announcement path, and no action is necessary.

Android

Prism for Android requires Android 8 (API level 26) or later. It SHOULD be consumed in a manner that preserves the library’s merged manifest, as the application context is otherwise not initialized automatically; the chapter on backend-specific notes describes this further.

Web

The WebAssembly build requires a browser that implements the Web Speech API. The first request for speech MUST originate from a user-initiated event in browsing contexts that enforce an autoplay policy.

Prism is built with thread support, so a program that links it MUST be compiled and linked with -pthread, and, when the Web Speech backend is present, linked with -lembind. The CMake package supplies these flags to targets that link prism::prism. Because threads in WebAssembly require SharedArrayBuffer, the page MUST be served cross-origin isolated, with the headers Cross-Origin-Opener-Policy: same-origin and Cross-Origin-Embedder-Policy: require-corp.

Godot

The addons/prismatoid directory MUST be included in the exported project. The add-on contains binaries for every supported platform and architecture, and an application MAY remove those for platforms it does not target. The load and backend dependencies of the platform apply as described in this chapter.

Python

An application SHOULD declare prismatoid as a dependency, which also installs cffi. No further action is necessary on platforms for which wheels are published. Where an application is packaged with a tool that collects its dependencies, the contents of the prism/_native directory and, on Linux, the prismatoid.libs directory MUST be preserved.

Building from source

A source build requires a C++23 compiler, of which GCC 12 is the oldest version of GCC known to function, and CMake 3.24 or later. On Linux, the development files are located through pkg-config: dbus-1 for the Orca backend and power management, speech-dispatcher for the Speech Dispatcher backend, and gio-2.0 and spiel-1.0 for the Spiel backend. Only the headers of speech-dispatcher and spiel-1.0 are used, since those libraries are loaded at run time.

Under the default setting of PRISM_BACKEND_DEFAULT, a backend whose development files are absent is omitted from the build. The build does not fail, and the resulting library lacks the backend. The outcome is reported in the Prism backends: line of the CMake output, which distributors SHOULD inspect. A distributor that expects a particular backend to be present SHOULD set its option, such as PRISM_ENABLE_ORCA_BACKEND, to ON, in which case the absence of its development files causes the configuration to fail.

Verifying a deployment

Distributors SHOULD test an application on a clean installation of each operating system they intend to support, since a development system will generally have libraries installed that an end user’s does not.

Setting the PRISM_LOG environment variable to trace, debug, info, warn, or error causes Prism to write log messages of that level and above to standard error. The messages include the reason a backend could not be loaded or initialized, and are the first thing to consult when a backend is unexpectedly unavailable. Applications MAY instead install a handler, as described in the chapter on logging.

Wine Bridges

Prism provides Wine bridges so that a Windows application using Prism under Wine or Proton can use speech and accessibility services supplied by the Linux or BSD host. The bridge is transparent to the application. A bridged backend has the same backend name, backend identifier, feature reporting, initialization rules, and public C API as the corresponding backend on a native platform. Applications MUST NOT call or load a bridge component directly; they select the backend through the registry and use the ordinary prism_backend_* functions.

The bridge mechanism applies only to Windows builds of Prism running under Wine or Proton. It does not make a Linux backend available to a program running on native Windows nor does it change the behavior of the corresponding native Linux or BSD backend. A backend may be present in a Windows registry even when its bridge cannot be used in the current process. Applications SHOULD therefore treat registry membership and runtime availability as separate questions, as they do for every other Prism backend.

Supported backends

The following backends can operate through a Wine bridge:

BackendHost requirementBridged capabilities
OrcaA usable desktop session bus and a running Orca release exposing its remote-control speech interface.Speech, combined output, and stop.
Speech DispatcherA Speech Dispatcher service reachable from the Unix session in which Wine is running.Speech, combined output, stop, pause and resume, rate, pitch and volume control, and voice enumeration, selection, and refresh.
SpielA usable desktop session bus and at least one reachable Spiel speech provider.Speech, combined output, stop, pause and resume, speaking-state queries, rate, pitch and volume control, and voice enumeration, selection, and refresh.

The host requirement is evaluated at run time. A bridge being installed does not imply that the service behind it is available, and a successful availability probe does not guarantee that the service will remain available until initialization or for the lifetime of the backend instance. Applications MUST handle PRISM_ERROR_BACKEND_NOT_AVAILABLE and other ordinary backend errors in the same manner as they would on a native platform.

Wine and Proton may run applications inside environments that restrict access to the host user’s session services. In such an environment, a bridge is unavailable if the required service cannot be reached from the Wine process, even if that service is running elsewhere on the host. Prism does not require applications to distinguish this case from the service simply not running.

Building the bridges

Wine bridges are built on an x86 or x86-64 Linux or BSD host by enabling PRISM_BUILD_WINELIBS. The build host MUST provide a Wine development toolchain. A bridge is produced only for a backend whose host-side development dependency is available to the bridge build; consequently, a build MAY contain some of the bridges listed above and omit others.

A normal source build can request the bridge payload as follows:

cmake -S . -B build -DPRISM_BUILD_WINELIBS=ON
cmake --build build

PRISM_BUILD_WINELIBS builds the Wine-side payload in addition to the ordinary build performed on that Unix host. A Windows application still requires a Windows Prism library of the appropriate architecture. The Wine bridge payload and the Windows Prism library SHOULD come from the same Prism release; applications and distributors MUST NOT assume that bridge components from different Prism releases are interchangeable, and the APIs of the bridges is intentionally unspecified.

The installed bridge payload is placed under the install tree’s wine directory. Distributors SHOULD preserve that payload as a unit rather than renaming individual files or combining files from different builds. The Wine-visible portions of the payload MUST be discoverable by Wine when the application starts. How a distributor arranges Wine’s module search path is outside Prism’s API contract and may vary between a system Wine installation, a private prefix, and Proton. Common deployment scenarios are discussed in the following sections.

Deploying the bridge

A Wine bridge consists of two files which serve different purposes and MUST be deployed together. The file whose name ends in .dll is the Windows-visible placeholder. Wine must be able to find this file through the ordinary Windows DLL search path. The corresponding file whose name ends in .dll.so is the host-side Wine module. Wine must be able to find this file through WINEDLLPATH.

The installed Prism payload places the host-side .dll.so files in the wine directory and the corresponding placeholder .dll files in wine/placeholders. Only bridges which were successfully built will be present. The files MUST NOT be renamed. A placeholder and its corresponding .dll.so MUST come from the same Prism build and MUST have an architecture compatible with the Windows Prism library and application using them.

The placeholder is not a replacement for the .dll.so, nor is the .dll.so a replacement for the placeholder. Copying only the placeholder allows Wine to locate the Windows-visible module name but does not provide the bridge itself. Making only the .dll.so available through WINEDLLPATH is likewise insufficient if Wine cannot locate the corresponding .dll through its Windows DLL search path.

Application-local deployment

For an application distributed as a self-contained directory, the RECOMMENDED arrangement is to copy the placeholder DLLs from Prism’s wine/placeholders directory into the directory containing the application’s executable. The matching .dll.so files SHOULD be kept in a separate Unix-visible directory, such as a wine subdirectory of the application installation.

For example, an application using all three bridged backends would place prism_orca_bridge.dll, prism_speech_dispatcher_bridge.dll, and prism_spiel_bridge.dll beside application.exe and prism.dll, or if the application statically links Prism, just next to application.exe. The corresponding prism_orca_bridge.dll.so, prism_speech_dispatcher_bridge.dll.so, and prism_spiel_bridge.dll.so files could be placed in a wine subdirectory.

The application is then launched with the directory containing the .dll.so files included in WINEDLLPATH. A launcher script might resemble the following:

#!/bin/sh

APPDIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
export WINEDLLPATH="$APPDIR/wine${WINEDLLPATH:+:$WINEDLLPATH}"
exec wine "$APPDIR/application.exe" "$@"

If the application uses a particular Wine prefix, the launcher MAY set WINEPREFIX in the ordinary manner as well:

#!/bin/sh

APPDIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
export WINEPREFIX="$APPDIR/prefix"
export WINEDLLPATH="$APPDIR/wine${WINEDLLPATH:+:$WINEDLLPATH}"
exec wine "$APPDIR/application.exe" "$@"

An existing WINEDLLPATH SHOULD be preserved as shown above rather than replaced outright, as it may contain other libraries the application requires. The bridge directory may be located anywhere on the Unix filesystem; it does not need to reside inside the Wine prefix.

Application-local deployment is generally preferable because it keeps the Prism version, bridge payload, and application together. It also permits two applications requiring different Prism releases to coexist without replacing one another’s bridge files.

Deployment into a private Wine prefix

An application which owns an entire Wine prefix MAY instead install the placeholder files into the prefix’s Windows system directories. The .dll.so files remain Unix files and SHOULD be stored in a separate directory which is added to WINEDLLPATH.

For a 64-bit application in a 64-bit Wine prefix, the 64-bit placeholders may be placed in $WINEPREFIX/drive_c/windows/system32. For a 32-bit application in a 64-bit Wine prefix, the 32-bit placeholders belong in $WINEPREFIX/drive_c/windows/syswow64. In a purely 32-bit prefix, the 32-bit placeholders are placed in system32.

The corresponding host-side .dll.so files MAY, for example, be stored in a prism-wine directory beneath the prefix or in another package-owned directory on the Unix filesystem. Whichever directory is chosen must then be included in WINEDLLPATH when the application is launched:

WINEPREFIX="/path/to/prefix" \
WINEDLLPATH="/path/to/prefix/prism-wine${WINEDLLPATH:+:$WINEDLLPATH}" \
wine application.exe

Placing the placeholders in the prefix system directories makes them available to applications throughout that prefix. A distributor SHOULD therefore use this arrangement only when it controls the prefix or deliberately wishes to make one Prism bridge payload shared by every application in it. Application-local deployment is safer when unrelated programs share a prefix.

The .dll.so files SHOULD NOT be copied into drive_c, system32, or syswow64, as they are host-side modules and are located through WINEDLLPATH, not through the Windows filesystem search path.

Wine launchers and desktop applications

A desktop launcher, package wrapper, shell script, or other program which starts the Windows application MAY set WINEDLLPATH instead of requiring the user to configure it globally. This is generally preferable to placing Prism’s bridge modules into Wine’s own installation directories.

For example:

export WINEDLLPATH="/opt/example/lib/prism/wine${WINEDLLPATH:+:$WINEDLLPATH}"
exec wine "/opt/example/share/example/application.exe"

A system package might therefore install the host-side bridge modules beneath a package-owned directory such as /usr/lib/example/prism-wine, while placing the placeholders in the application’s directory or private Wine prefix. Its launcher would add the host-side directory to WINEDLLPATH.

Distributors SHOULD prefer a package-owned bridge directory over copying the .dll.so files into Wine’s own library directories. Modifying Wine’s installation couples the Prism package to the layout and package-management policy of a particular Wine distribution and makes coexistence of different Prism versions substantially more difficult.

Proton and game-local deployment

The application-local arrangement is also suitable for Proton. The placeholder DLLs SHOULD normally be placed beside the Windows executable, or otherwise in a location visible through that application’s Windows DLL search path. The .dll.so files remain on the Unix side and the directory containing them must be present in WINEDLLPATH when Proton starts the application.

For launch systems which use %command% to represent the application’s normal Proton command, an appropriate launch option can generally be written as:

WINEDLLPATH="/absolute/path/to/prism-wine:$WINEDLLPATH" %command%

The precise location of a Proton prefix is insignificant to Prism. A distributor SHOULD NOT require the bridge .dll.so files to be installed into a particular Steam or Proton prefix directory when an application-local placeholder and an explicit WINEDLLPATH can be used instead. Proton versions and launchers may arrange their prefixes differently, whereas the bridge requirement remains simply that Wine can locate both halves of the payload.

Using a bridged backend

No bridge-specific initialization function exists. An application uses the same registry and backend functions it uses everywhere else. In particular, it SHOULD query runtime support before initialization when selecting a specific bridged backend:

PrismBackend *backend = prism_registry_create(ctx, PRISM_BACKEND_SPIEL);
if (backend != NULL) {
    const uint64_t features = prism_backend_get_features(backend);
    if ((features & PRISM_BACKEND_IS_SUPPORTED_AT_RUNTIME) != 0) {
        PrismError err = prism_backend_initialize(backend);
        if (err == PRISM_OK && err != PRISM_ERROR_ALREADY_INITIALIZED) {
            /* Use the backend through the ordinary Prism API. */
        }
    }
    prism_backend_free(backend);
}

An application that has no reason to prefer one of the bridged backends SHOULD use the ordinary best-backend selection functions instead of adding Wine-specific selection policy. The registry’s priority ordering and initialization fallback rules apply normally. Code SHOULD NOT infer that it is running under Wine solely because one of these backend identifiers is present in the registry.

Deployment requirements

The architecture of the Windows Prism library, the Windows application, the Wine environment, and the bridge payload MUST be compatible. Bridge generation is supported only on x86-family hosts. A 32-bit Windows application requires a bridge payload suitable for its Wine architecture, and a 64-bit Windows application requires the corresponding 64-bit payload.

The host services used by Orca and Spiel are normally associated with the logged-in graphical session, while Speech Dispatcher is normally associated with the user’s speech session. A Wine application launched outside that session MAY therefore be unable to use a bridge that works when the same application is launched from the desktop. Service startup, D-Bus session configuration, Wine prefix policy, and Proton sandbox policy are deployment concerns and are not modified by Prism, and as such are outside the scope of this manual.

Applications do not need to ship Linux speech engines inside the Windows application. They do, however, need the matching Prism bridge payload to be available to Wine, and the host system needs the service required by the selected backend. If these requirements are not met, the backend remains usable as a registry entry but reports itself as not runtime-supported or fails initialization with an ordinary Prism error.

C API Reference

Introduction

The C API is how Prism clients consume the Prism library. The C++ interface is internal and never exposed to front-end users.

The C API consists of three layers: context management, the backend registry, and individual backends.

A Prism context encapsulates access to the backend registry and serves as the root object for all Prism operations. Applications MUST create a context before performing any other Prism operations and SHOULD destroy it when Prism functionality is no longer needed.

The backend registry is the central authority for which backends are available on the platform Prism is running on. Backends are sorted in priority order, with highest-priority backends coming before lower-priority ones. Every context is bound to a registry when it is created. By default this is the global registry, which contains the backends compiled into the library. An application MAY instead construct its own registry containing the compiled-in backends plus custom backends of its own, and bind contexts to that. See the chapter on custom backends.

Backends are individual TTS engines or screen readers. Backends are retrieved from the backend registry. Backends are identified by a unique 64-bit ID, distinct from all other backends. The invalid ID, 0, or PRISM_BACKEND_INVALID, is an always-invalid backend ID and will never be returned by the registry or used by a backend, and is used to signify internal errors where an API does not return a PrismError.

Conventions

API Conventions

Naming Conventions

Within the Prism API, the following conventions are always constant:

  • Context management functions use the prefix prism_.
  • Backend registry functions use the prefix prism_registry_.
  • Registry builder functions use the prefix prism_registry_builder_.
  • Backend functions use the prefix prism_backend_.
  • Error utility functions use the prefix prism_error_.
  • Types use the prefix Prism followed by PascalCase naming.
  • Macros and constants use the prefix PRISM_ followed by SCREAMING_SNAKE_CASE.

Return Value Conventions

The vast majority of functions return a PrismError. However, some don’t, and in those circumstances, a sentinel value is returned to indicate failure:

  • For boolean functions, false is returned when errors occur.
  • For functions returning size_t, SIZE_MAX is returned on error.
  • For functions returning signed integers, -1 is returned on error.
  • For functions returning pointers or character strings, NULL is returned on error.
  • For functions returning PrismBackendId, PRISM_BACKEND_INVALID is returned on error.

Functions returning PrismError return PRISM_OK on success. Any other value indicates an error condition, unless explicitly specified.

Static Analysis Support

Prism employs compiler-specific annotations to assist developers in catching errors at compile time. These annotations are transparent to application code, but compilers that recognize them will issue warnings or errors when code violates the documented contracts.

All functions returning PrismError are annotated such that the compiler will warn if the return value is discarded. Since nearly every Prism function can fail, ignoring a return value almost always represents a programming error. Similarly, functions that allocate resources (such as prism_init or prism_registry_create) are annotated to warn if their return value is ignored, since failing to capture the returned pointer results in a resource leak.

Functions that require non-null pointer arguments are annotated with the indices of those parameters. Compilers that support this annotation will warn if a null pointer is passed to such a parameter. Note that passing NULL to any function where NULL is prohibited is always a programming error and results in undefined behavior. Compilers MAY make optimization assumptions based on these invariants being upheld.

Functions that return newly allocated memory are annotated to inform the compiler that the returned pointer does not alias any other pointer. This enables certain optimizations and also serves as documentation that the caller is responsible for freeing the returned memory.

Functions that accept printf-style format strings (reserved for future diagnostic functions) are annotated such that the compiler can validate format specifiers against the provided arguments.

Functions or types that are deprecated are annotated with a message explaining the deprecation and suggesting alternatives. The compiler will warn when deprecated elements are used.

Functions that take NULL-terminated strings are annotated to assist in static analysis. Programmers MAY use these when using more advanced analysis features (such as GCC’s -fanalyzer command-line argument) to ensure that strings are always NULL-terminated.

No function of the C API lets a C++ exception escape; a failure inside Prism, including a failure to allocate memory, is reported through the function’s return value. When prism.h is compiled as C++, its functions are accordingly declared noexcept.

These annotations are supported on GCC, Clang, and MSVC to varying degrees. On compilers that do not support a particular annotation, that annotation has no effect. Applications SHOULD enable compiler warnings and treat warnings from these annotations as errors during development.

Ownership and lifetime requirements

The library does it’s best to avoid copying unless absolutely required. On some back-ends, this is unavoidable. Programmers MUST be aware that, when passing in strings or other data, the caller owns the passed-in argument. The lifetime of the argument must exceed that of the function call. Although some back-ends may copy strings, this MUST NOT be assumed to occur.

When the library returns pointers to data in memory, the library owns that data. The caller MUST NOT free or otherwise deallocate the returned memory unless explicitly specified in this manual (for example, a context requires explicit deallocation, as does a back-end, but a back-end name MUST NOT be freed).

Custom backends introduce two further ownership rules:

  1. The vtable passed to prism_registry_builder_add_backend is copied during the call, so the caller may discard it afterwards, but the function pointers it contains MUST remain valid for as long as any registry or backend instance refers to them.
  2. If a userdata_free function is supplied, ownership of userdata transfers to Prism the moment prism_registry_builder_add_backend is called, and Prism invokes userdata_free exactly once, regardless of whether registration succeeds. If userdata_free is NULL, the caller retains ownership and MUST keep userdata valid for as long as any registry or backend instance refers to it.

The precise lifetime rules are given in the chapter on custom backends.

Thread Safety

Thread Safety

Prism provides the following thread safety guarantees:

  • The functions prism_init and prism_shutdown are thread-safe. Multiple threads MAY call prism_init concurrently; each call returns an independent context. However, prism_shutdown MUST only be called once per context, and the context MUST NOT be used from any thread after prism_shutdown has been called on that context. If one thread calls prism_shutdown while another thread is using the same context, the behavior is undefined.
  • All prism_registry_* functions are thread-safe when called on the same context from multiple threads. The registry maintains an internal backend cache: a partial mapping from backend identifiers to live backend instances, shared across all contexts bound to that registry. Contexts bound to different registries share no cache state, even for the same compiled-in backend. Each registry function is defined as an ordered sequence of one or more cache operations (lookup or install), zero or more unsynchronized factory invocations, and, for prism_registry_acquire_best, zero or more initialization attempts. A factory invocation, for the purposes of this section, is an invocation which creates a backend without initializing it.
  • Cache operations are atomic with respect to each other. Cache operations affecting the same backend identifier are totally ordered; the order of cache operations affecting distinct backend identifiers is unspecified. Factory invocations are not ordered with respect to cache operations or other factory invocations and MAY proceed concurrently with arbitrary other registry activity. Initialization of a particular cached backend object is serialized internally with prism_backend_initialize on that same underlying object. This serialization does not make any other operation on the backend thread-safe.
  • The functions prism_registry_count, prism_registry_id_at, prism_registry_id, prism_registry_name, prism_registry_priority, prism_registry_exists, and prism_registry_get consist of cache operations only. They MAY execute concurrently with each other and with the unsynchronized portions of prism_registry_acquire and prism_registry_acquire_best.
  • The functions prism_registry_acquire and prism_registry_acquire_best consist of a sequence of cache operations interleaved with factory and initialization work outside the cache lock. Consequently, two threads requesting the same uncached backend MAY each perform an independent construction; in such cases, the cache retains exactly one of the constructed instances, and all callers receive that instance. If a live cached instance exists but is not yet initialized, that instance is retained and initialized in a serialized manner. Discarded speculative instances are destroyed when their reference counts reach zero. Applications MUST NOT rely on prism_registry_acquire or prism_registry_acquire_best being atomic in their entirety with respect to other registry operations.
  • Individual backend instances are NOT thread-safe. Applications MUST NOT call functions on the same PrismBackend instance from multiple threads concurrently without external synchronization. This restriction applies even to logically independent operations; for example, calling prism_backend_get_rate from one thread while another thread calls prism_backend_set_volume on the same backend instance produces undefined behavior.
  • Different backend instances MAY be used from different threads concurrently without restriction. For example, if an application creates two backends using prism_registry_create, those two backends may be used from separate threads without synchronization.
  • The prism_registry_create, prism_registry_create_best, prism_registry_acquire, and prism_registry_acquire_best functions are thread-safe with respect to the registry. However, the returned backend instances are subject to the single-threaded constraint described above.
  • Audio callbacks passed to prism_backend_speak_to_memory MAY be invoked from a thread other than the calling thread, depending on the backend. Callback implementations MUST be prepared for this possibility and MUST provide their own synchronization if they access shared state. The callback MUST NOT call any Prism function on the backend instance that initiated the synthesis, as this would violate the single-threaded backend constraint and may also cause deadlocks.
  • When using prism_registry_acquire or prism_registry_acquire_best, multiple calls may return handles to the same underlying backend instance if a cached instance exists. In this case, all handles share the same backend state, and the single-threaded constraint applies across all handles. Applications that acquire backends from the cache and use them from multiple threads MUST synchronize access externally.
  • A registry’s backend set is fixed when the registry is created. No registration ever occurs on a live registry, so no registration operation is ordered against any registry operation described above.
  • A PrismRegistryBuilder is NOT thread-safe. A builder is a transient configuration object, so all operations on a given builder MUST be externally synchronized if it is somehow reachable from more than one thread.
  • prism_registry_retain and prism_registry_release are thread-safe. Note that the final prism_registry_release (or the final prism_shutdown on a context bound to the registry, whichever comes last) finalizes the registry on the calling thread; any userdata_free functions belonging to custom backends may therefore be invoked on that thread.
  • Custom backend implementations are subject to the same single-instance constraint as callers: because applications MUST NOT call functions on one backend instance from multiple threads concurrently, a custom backend’s vtable functions are never invoked concurrently for the same instance. Distinct instances of the same custom backend MAY be invoked concurrently, and any state they share (for example, state reachable through userdata when no create function is supplied) MUST be synchronized by the implementation.
  • The functions prism_availability_poll_pause and prism_availability_poll_resume are thread-safe and MAY be called from any thread, including concurrently with each other and with the poll thread’s own activity. The availability callback and the baseline callback configured through PrismConfig are invoked from Prism’s internal poll thread, not from a thread owned by the application; callback implementations MUST synchronize any shared state they access, and MUST NOT call prism_shutdown on the owning context. The single-threaded backend constraint continues to apply to any backend instance the callback creates or acquires. prism_availability_auto_power_supported is thread-safe and MAY be called at any time.
  • The logging functions prism_set_log_handler, prism_set_log_level, prism_log, and prism_log_flush are thread-safe and MAY be called from any thread concurrently, including before prism_init and after prism_shutdown. A log handler is invoked only from Prism’s internal logging thread and is never invoked concurrently with itself; handler implementations MUST synchronize any shared state and MUST NOT call any logging function. prism_log_shutdown is thread-safe with respect to other logging functions, but the application MUST ensure it is not called from within a log handler.

Applications requiring concurrent speech synthesis from multiple threads SHOULD create separate backend instances per thread using prism_registry_create or prism_registry_create_best.

Error Codes

Error Codes

When a function returns a PrismError, the prism_error_string function can be used to return a human-readable string error message which can be bubbled up to applications. The numeric values of error codes are part of the ABI and will not change in future versions. Errors MAY, however, be added in future.

ErrorValueDescription
PRISM_OK0Operation completed without error
PRISM_ERROR_NOT_INITIALIZED1The backend was not initialized; prism_backend_initialize was not called or failed
PRISM_ERROR_INVALID_PARAM2An invalid parameter was passed to a function
PRISM_ERROR_NOT_IMPLEMENTED3The operation is not supported by the selected backend
PRISM_ERROR_NO_VOICES4No voices are available for this backend
PRISM_ERROR_VOICE_NOT_FOUND5The specified voice was not found
PRISM_ERROR_SPEAK_FAILURE6Speech synthesis failed
PRISM_ERROR_MEMORY_FAILURE7Memory allocation failed
PRISM_ERROR_RANGE_OUT_OF_BOUNDS8A parameter value exceeded its valid range
PRISM_ERROR_INTERNAL9An internal backend error occurred
PRISM_ERROR_NOT_SPEAKING10Attempted to stop or pause when not speaking
PRISM_ERROR_NOT_PAUSED11Attempted to resume when not paused
PRISM_ERROR_ALREADY_PAUSED12Attempted to pause when already paused
PRISM_ERROR_INVALID_UTF813A string parameter contained invalid UTF-8
PRISM_ERROR_INVALID_OPERATION14The operation is invalid in the current state
PRISM_ERROR_ALREADY_INITIALIZED15Attempted to initialize an already-initialized backend
PRISM_ERROR_BACKEND_NOT_AVAILABLE16The backend is not available on this system
PRISM_ERROR_UNKNOWN17An unspecified error occurred
PRISM_ERROR_INVALID_AUDIO_FORMAT18Either the backend speech engine or backend voice have an audio format that Prism does not know how to handle, or the parameters that the underlying speech engine provided to Prism were nonsensical.
PRISM_ERROR_INTERNAL_BACKEND_LIMIT_EXCEEDED19The backend possesses an internal hard ceiling as to how many instances may be instantiated at any given time, and this limit would be exceeded were another to be initialized.
PRISM_ERROR_BACKEND_ENTERED_UNDEFINED_STATE20An error occured when the backend was executing a function which has caused the backend to enter an undefined state. The caller should re-initialize the backend from scratch.
PRISM_ERROR_LIBRARY_LOAD_FAILED21A shared library could not be opened, because no file exists at the given path, it is not a loadable image, it was built for a different architecture, or its initialization code failed
PRISM_ERROR_LIBRARY_INVALID22A shared library was opened but does not export the plugin entry point
PRISM_ERROR_INCOMPATIBLE_ABI23A plugin declined the host, or a backend descriptor declared an ABI generation this build of Prism does not accept

The constant PRISM_ERROR_COUNT equals the total number of error codes and MAY be used for bounds checking or table sizing. This constant may increase in future versions as new error codes are added.

Backend limits

Backends MAY contain hard ceilings as to the number of them that can be active at any given moment. This hard ceiling is only imposed in circumstances where a dynamic number of instances is impossible to accommodate or because reaching the limit would exhaust system resources. When a backend limit is reached, prism_backend_initialize SHALL return PRISM_ERROR_INTERNAL_BACKEND_LIMIT_EXCEEDED, and consumers of the Prism library MUST NOT attempt re-initialization of the backend.

At the time of writing, all backend limits are sufficiently high that they are unlikely to be reached under normal usage. As such, no API function exists to query a backend’s limit. Consumers SHOULD still handle PRISM_ERROR_INTERNAL_BACKEND_LIMIT_EXCEEDED gracefully, but are not expected to design around it.

Backend Identifiers

Predefined Backend Identifiers

Prism defines constants for all known backend identifiers. These constants are 64-bit unsigned integers computed at compile time using a deterministic hash function. Applications MAY use these constants to request specific backends by ID rather than by name, which avoids the overhead of a string lookup.

ConstantBackend
PRISM_BACKEND_INVALIDInvalid/sentinel value (always 0)
PRISM_BACKEND_SAPIMicrosoft SAPI (Windows)
PRISM_BACKEND_AV_SPEECHAVSpeechSynthesizer (macOS, iOS, tvOS, WatchOS, VisionOS)
PRISM_BACKEND_VOICE_OVERVoiceOver screen reader (macOS, MacCatalyst, iOS, WatchOS, tvOS, VisionOS)
PRISM_BACKEND_SPEECH_DISPATCHERSpeech Dispatcher (Linux/BSD, win32 via Wine)
PRISM_BACKEND_NVDANVDA screen reader (Windows)
PRISM_BACKEND_JAWSJAWS screen reader (Windows)
PRISM_BACKEND_ONE_COREWindows OneCore speech API (Windows 10+)
PRISM_BACKEND_ORCAOrca screen reader (Linux/BSD, win32 via Wine)
PRISM_BACKEND_ANDROID_TTSAndroid TTS engine (Android)
PRISM_BACKEND_ANDROID_SCREEN_READERAndroid screen readers (Android)
PRISM_BACKEND_WEB_SPEECHWeb SpeechSynthesis API (web)
PRISM_BACKEND_UIAUIAutomation backend (Windows only)
PRISM_BACKEND_ZDSRZhengdu Screen Reader (Windows)
PRISM_BACKEND_ZOOM_TEXTZoomText (Windows)
PRISM_BACKEND_BOY_PC_READERBoyPCReader (windows only)
PRISM_BACKEND_PC_TALKERPCTalker (windows only)
PRISM_BACKEND_SENSE_READERSense Reader screen reader (Windows)
PRISM_BACKEND_SYSTEM_ACCESSSystemAccess screen reader (windows) (only available if explicitly enabled at build time)
PRISM_BACKEND_WINDOW_EYESWindowEyes screen reader (windows) (only available if explicitly enabled at build time)
PRISM_BACKEND_GDK_SPEECHMicrosoft GDK speech synthesis (Windows, Xbox)
PRISM_BACKEND_SPIELSpiel (Linux/BSD, win32 via Wine)

The availability of any given backend depends on the platform Prism is running on and how Prism was compiled. The presence of a constant does not guarantee that the corresponding backend is available at runtime. Use prism_registry_exists to check availability.

Custom backend identifiers are computed from the backend’s name with the same hash function, so an identifier obtained from prism_registry_builder_add_backend is stable across runs for the same name.

Context Management

Context Management

PrismConfig

A struct containing configuration parameters for Prism or it’s back-ends to use.

Syntax

typedef struct {
  uint8_t version;
  PrismRegistry *registry;
  PrismAvailabilityCallback availability_callback;
  void *availability_userdata;
  uint32_t availability_poll_interval_ms;
  uint32_t availability_debounce_samples;
  uint32_t availability_backoff_max_ms;
  bool availability_auto_power_manage;
  PrismAvailabilityBaselineCallback availability_baseline_callback;
} PrismConfig;

Members

version

The version of this structure. This field MUST NOT be modified.

registry

The registry the created context will be bound to. This MAY be NULL, in which case the context uses the global registry. If non-null, it MUST be a registry obtained from prism_registry_freeze. This field was added in version 3 of this structure.

availability_callback

A function invoked when a backend’s runtime availability changes, or NULL. When this field is NULL, the context performs no background availability polling and creates no poll thread. When it is non-null, the context runs an internal thread that samples backend availability and invokes this callback on each confirmed transition. The behavior of this callback and the polling model are described in the chapter on background availability enumeration. This field was added in version 3 of this structure.

availability_userdata

An opaque pointer passed unmodified to availability_callback on each invocation. Prism does not interpret or take ownership of this value. It is ignored when availability_callback is NULL. This field was added in version 3 of this structure.

availability_poll_interval_ms

The base interval, in milliseconds, between availability scans. A value of 0 selects the default of 1000 milliseconds. It is ignored when availability_callback is NULL. This field was added in version 3 of this structure.

availability_debounce_samples

The number of consecutive agreeing samples required before a change in a backend’s availability is confirmed and reported. A value of 0 selects the default of 2. A value of 1 confirms every observed change immediately, without debouncing. It is ignored when availability_callback is NULL. This field was added in version 3 of this structure.

availability_backoff_max_ms

The upper bound, in milliseconds, for adaptive backoff of the sampling interval. While availability is unchanging, the interval doubles from availability_poll_interval_ms toward this bound, and returns to the base interval as soon as any change is observed. A value of 0, or any value not greater than the base interval, disables backoff and holds the interval constant. It is ignored when availability_callback is NULL. This field was added in version 3 of this structure.

availability_auto_power_manage

When true, and when the library was built with power-management support, the poll thread is paused automatically when the operating system suspends and resumed when it wakes. When false, or on builds and platforms without power-management support, this field has no effect and the application MAY drive pausing itself. Use prism_availability_auto_power_supported to determine whether this field is honored. It is ignored when availability_callback is NULL. This field was added in version 3 of this structure.

availability_baseline_callback

A function invoked exactly once, after the poll thread has completed its first scan, or NULL. It receives availability_userdata. It notifies the application that the baseline against which the availability callback reports changes has been established, and carries no information about any backend. The chapter on background availability enumeration describes the conditions under which an application requires this notification. It is ignored when availability_callback is NULL. This field was added in version 4 of this structure.

Remarks

This struct contains configuration information for Prism. The version field will be incremented by 1 whenever a new field is added or removed.

Certain fields in this structure may only be available on certain platforms. Attempting to read or write to them on platforms where these fields are not useful is a compilation error.

The registry field is how custom backends are made visible to a context. The context takes its own reference to the registry; the caller’s reference is unaffected. Any number of contexts MAY be bound to the same registry, and all of them share that registry’s backend cache.

prism_config_init

Creates a new configuration structure which can be passed to prism_init.

Syntax

PrismConfig prism_config_init(void);

Parameters

This function has no parameters.

Returns

A stack-allocated PrismConfig struct, returned by value.

Remarks

This function can be used to create a PrismConfig struct which can later be passed to prism_init. It is not an error to not call this function or to pass NULL to prism_init. This struct can be used to perform configuration of Prism before the library is initialized; for example, it is used to pass platform-specific data to the library for back-ends to use.

prism_init

Creates a new Prism context.

Syntax

PrismContext *prism_init(PrismConfig* cfg);

Parameters

cfg

The configuration struct created by prism_config_init. This MAY be NULL.

Return Value

Returns a pointer to a newly allocated PrismContext on success. Returns NULL if memory allocation fails, or if the configuration’s version field is zero or greater than the version Prism was built against.

Remarks

prism_init is the entry point to the Prism library. Before calling any other Prism function (except prism_error_string and the functions to construct a registry for custom backends), an application MUST call prism_init to obtain a context.

Each call to prism_init creates an independent context. Multiple contexts MAY exist simultaneously. A context is bound at creation to the registry named by the configuration’s registry field, or to the global registry when that field is NULL or no configuration is supplied at all. Contexts bound to the same registry share its cache: a cached backend instance created through one such context is visible from the others. Contexts bound to different registries share nothing.

Configurations from older versions of this library are accepted; fields that a caller’s version of the structure does not contain are treated as absent. A configuration whose version is newer than the library itself is rejected, since the library cannot know what the unknown fields mean.

The returned context is owned by the caller. When the context is no longer needed, it MUST be passed to prism_shutdown to release resources. Failure to call prism_shutdown results in a memory leak.

Example

PrismContext *ctx = prism_init(NULL);
if (!ctx) {
    fprintf(stderr, "Failed to initialize Prism\n");
    return 1;
}
/* Use Prism... */
prism_shutdown(ctx);

prism_shutdown

Destroys a Prism context and releases associated resources.

Syntax

void prism_shutdown(PrismContext *ctx);

Parameters

ctx

The context to destroy. This parameter MAY be NULL.

Return Value

This function does not return a value.

Remarks

prism_shutdown releases all resources associated with a Prism context. After this function returns, the ctx pointer is invalid and MUST NOT be used for any purpose, including being passed to prism_shutdown again.

If ctx is NULL, this function has no effect. This allows applications to unconditionally call prism_shutdown in cleanup code without checking whether initialization succeeded.

This function does not automatically free backend instances that were obtained from the context. If any backends obtained via prism_registry_create, prism_registry_create_best, prism_registry_acquire, or prism_registry_acquire_best are still alive when prism_shutdown is called, those backends remain valid and may continue to be used. However, once prism_shutdown returns, no new backends can be obtained using that context.

The relationship between contexts and backends is asymmetric: contexts do not own backends. The registry the context was bound to is released when the context is destroyed; the global registry continues to exist as long as the process runs, while an application-constructed registry is finalized once the last context bound to it has been shut down and the application has released its own reference.

If multiple threads are using the same context, the application MUST ensure that no other thread is calling any Prism function on that context when prism_shutdown is called. Calling prism_shutdown while another thread is in the middle of a registry operation results in undefined behavior.

Example

/* At application shutdown */
if (backend) {
    PrismError err = prism_backend_stop(backend);
    if (err != PRISM_OK && err != PRISM_ERROR_NOT_IMPLEMENTED) {
        fprintf(stderr, "Failed to stop speech: %s\n", prism_error_string(err));
    }
    prism_backend_free(backend);
    backend = NULL;
}
prism_shutdown(ctx);
ctx = NULL;

Backend Registry Functions

Backend Registry Functions

The backend registry maintains the list of available backends and provides facilities for creating and managing backend instances. The functions in this chapter operate on whichever registry the supplied context is bound to. A registry’s contents are fixed when it is created: compiled-in backends register themselves at library load time and appear in every registry, and custom backends are added while a registry is under construction, as described in the chapter on custom backends. There is no way to add or remove a backend from a registry that already exists.

prism_registry_count

Returns the number of backends registered in the registry.

Syntax

size_t prism_registry_count(PrismContext *ctx);

Parameters

ctx

The Prism context. This parameter MUST NOT be NULL.

Return Value

Returns the number of registered backends.

Remarks

The return value represents the number of backends in the registry the context is bound to. For a context using the global registry, this is the number of backends that were compiled into the Prism library and registered themselves at load time, and for a context bound to an application-constructed registry, it additionally includes any custom backends. The count is constant for the lifetime of the registry.

A return value of zero indicates that no backends are available, which typically means Prism was compiled without any backend support or is running on a platform for which no backends were configured. Applications SHOULD check for this condition and report an appropriate error to the user.

Note that the number of registered backends does not necessarily equal the number of usable backends. Some backends may fail to initialize because their underlying system component is not installed. Use prism_registry_create_best or prism_registry_acquire_best to find a backend that actually works.

prism_registry_id_at

Returns the backend ID at the specified index in the registry.

Syntax

PrismBackendId prism_registry_id_at(PrismContext *ctx, size_t index);

Parameters

ctx

The Prism context. This parameter MUST NOT be NULL.

index

The zero-based index into the backend list. This value MUST be less than the value returned by prism_registry_count.

Return Value

Returns the backend ID at the specified index on success. Returns PRISM_BACKEND_INVALID if index is out of range.

Remarks

Backends are stored in the registry in descending priority order. Index 0 corresponds to the highest-priority backend, index 1 to the second-highest, and so on. This ordering is determined by the priority values, which are assigned at compile time for built-in backends and at registration time for custom backends.

The priority ordering reflects a general preference hierarchy. Screen reader backends (such as NVDA, JAWS, and Orca) typically have higher priority than standalone TTS backends (such as SAPI and OneCore) because, when a screen reader is running, applications generally want to route speech through it rather than speaking independently. However, applications that have specific requirements MAY ignore this ordering and select backends by ID or name.

The mapping between indices and IDs is stable for the lifetime of the registry. The ID at a given index will not change unless the registry is recreated. In the case of built-in backends, the index is constant for each build of Prism.

prism_registry_id

Looks up a backend by name and returns its ID.

Syntax

PrismBackendId prism_registry_id(PrismContext *ctx, const char *name);

Parameters

ctx

The Prism context. This parameter MUST NOT be NULL.

name

The backend name to look up. This parameter MUST NOT be NULL and MUST be a valid null-terminated string.

Return Value

Returns the backend ID corresponding to the given name on success. Returns PRISM_BACKEND_INVALID if no backend with the given name exists.

Remarks

Backend names are human-readable strings such as “SAPI”, “NVDA”, “Speech Dispatcher”, etc. Name comparison is exact and case-sensitive. The name “sapi” will not match a backend named “SAPI”.

If the application knows the backend it wants at compile time, using the predefined PRISM_BACKEND_* constants is more efficient than calling this function, as it avoids the string comparison overhead.

prism_registry_name

Returns the human-readable name of a backend given its ID.

Syntax

const char *prism_registry_name(PrismContext *ctx, PrismBackendId id);

Parameters

ctx

The Prism context. This parameter MUST NOT be NULL.

id

The backend ID to look up.

Return Value

Returns a pointer to a null-terminated string containing the backend name on success. Returns NULL if the ID is not found in the registry.

Remarks

The returned string is owned by the registry and remains valid for the lifetime of the registry (i.e., for as long as a context bound to that registry remains valid). Applications MUST NOT retain the pointer after the last context bound to the registry has been shut down and the registry released. Applications MUST consider the returned string read-only and owned by Prism.

This function is the inverse of prism_registry_id. Given an ID obtained from any source (such as prism_registry_id_at, a predefined constant, or storage), this function returns the corresponding human-readable name.

prism_registry_priority

Returns the priority value of a backend.

Syntax

int prism_registry_priority(PrismContext *ctx, PrismBackendId id);

Parameters

ctx

The Prism context. This parameter MUST NOT be NULL.

id

The backend ID to look up.

Return Value

Returns the priority value of the backend on success. Returns -1 if the ID is not found in the registry.

Remarks

Priority values are non-negative integers. Built-in backends are assigned priorities at compile time; custom backends supply theirs at registration, where a negative value is rejected. Higher values indicate higher priority. When prism_registry_create_best or prism_registry_acquire_best is called, Prism attempts to create backends in descending priority order until one succeeds.

The priority system is designed to select the most appropriate backend automatically. Screen reader backends are assigned higher priorities because, when a user is running a screen reader, they generally expect all speech to go through it. If no screen reader is running, those backends will fail to initialize, and Prism will fall back to a standalone TTS backend.

Applications MAY use priority values to inform user interface decisions, such as grouping backends by type or indicating which backend will be selected by default. However, applications SHOULD NOT rely on specific priority values, as they may change in future versions of Prism.

prism_registry_exists

Checks whether a backend with the given ID exists in the registry.

Syntax

bool prism_registry_exists(PrismContext *ctx, PrismBackendId id);

Parameters

ctx

The Prism context. This parameter MUST NOT be NULL.

id

The backend ID to check.

Return Value

Returns true if a backend with the given ID exists in the registry. Returns false if the backend does not exist or if id is PRISM_BACKEND_INVALID.

Remarks

This function checks only whether the backend is registered, not whether it can be successfully initialized. A backend may exist in the registry but fail to initialize because its underlying system component is not installed or not running.

For the predefined PRISM_BACKEND_* constants, this function effectively checks whether Prism was compiled with support for that backend and whether it is available on the current platform.

prism_registry_get

Retrieves a cached backend instance if one exists.

Syntax

PrismBackend *prism_registry_get(PrismContext *ctx, PrismBackendId id);

Parameters

ctx

The Prism context. This parameter MUST NOT be NULL.

id

The backend ID to retrieve.

Return Value

Returns a pointer to the cached backend instance if one exists and is still alive. Returns NULL if no cached instance exists, if the cached instance has been freed, or if the ID is not found in the registry.

Remarks

This function provides read-only access to the backend cache. It does not create a new instance; it only returns an existing instance that was previously created by prism_registry_acquire or prism_registry_acquire_best and has not yet been freed.

The cache uses weak references internally. When all handles to a cached backend have been passed to prism_backend_free, the backend is destroyed and removed from the cache. Subsequent calls to prism_registry_get will return NULL until a new instance is created via prism_registry_acquire.

Note that the returned handle shares ownership with other handles to the same instance. The caller SHOULD call prism_backend_free when done, but the underlying backend will not be destroyed until all handles have been freed.

prism_registry_create

Creates a new backend instance.

Syntax

PrismBackend *prism_registry_create(PrismContext *ctx, PrismBackendId id);

Parameters

ctx

The Prism context. This parameter MUST NOT be NULL.

id

The backend ID to instantiate.

Return Value

Returns a pointer to a newly created backend instance on success. Returns NULL if the ID is not found in the registry or if memory allocation fails, or if the backend is a custom backend, if the backend’s creation callback fails to produce an instance.

Remarks

Each call to prism_registry_create creates a new, independent backend instance regardless of whether other instances of the same backend exist. This is in contrast to prism_registry_acquire, which returns a cached instance if one is available.

The returned backend is not yet initialized. Applications MUST call prism_backend_initialize before using any other backend function except prism_backend_name and prism_backend_free. Attempting to use an uninitialized backend will result in PRISM_ERROR_NOT_INITIALIZED.

The caller assumes ownership of the returned backend and MUST eventually pass it to prism_backend_free to release resources. Failure to do so results in a resource leak.

Creating a backend instance does not guarantee that the backend will successfully initialize. Some backends require system components that may not be installed or may not be running.

If an application wants to create whichever backend will work without specifying a particular one, use prism_registry_create_best instead.

Example

PrismBackend *backend = prism_registry_create(ctx, PRISM_BACKEND_SAPI);
if (!backend) {
    fprintf(stderr, "Failed to create SAPI backend\n");
    return;
}
PrismError err = prism_backend_initialize(backend);
if (err != PRISM_OK) {
    fprintf(stderr, "Failed to initialize: %s\n", prism_error_string(err));
    prism_backend_free(backend);
    return;
}
/* Backend is ready to use */

prism_registry_create_best

Creates a new instance of the highest-priority backend that successfully initializes.

Syntax

PrismBackend *prism_registry_create_best(PrismContext *ctx);

Parameters

ctx

The Prism context. This parameter MUST NOT be NULL.

Return Value

Returns a pointer to a newly created and initialized backend instance on success. Returns NULL if no backend could be created and initialized.

Remarks

This function iterates through all registered backends in descending priority order. For each backend, it attempts to create an instance and initialize it. If initialization succeeds, that backend is returned immediately. If initialization fails, the instance is discarded and the next backend is tried.

Unlike prism_registry_create, the returned backend is already initialized. Applications do not need to call prism_backend_initialize and SHOULD NOT do so, as it will return PRISM_ERROR_ALREADY_INITIALIZED.

This function is the recommended way to obtain a backend when the application does not have a specific preference. It automatically handles the common case where screen reader backends should be preferred when a screen reader is running but TTS backends should be used as a fallback

The caller assumes ownership of the returned backend and MUST eventually pass it to prism_backend_free.

prism_registry_acquire

Acquires a backend instance, reusing a cached instance if available or creating a new one otherwise.

Syntax

PrismBackend *prism_registry_acquire(PrismContext *ctx, PrismBackendId id);

Parameters

ctx

The Prism context. This parameter MUST NOT be NULL.

id

The backend ID to acquire.

Return Value

Returns a pointer to a backend instance (either existing or newly created) on success. Returns NULL if the ID is not found in the registry or if creation fails.

Remarks

Note: A program that does not have a specific reason to share backend state with other callers SHOULD invoke prism_registry_create instead.

When invoked for a backend identifier whose cache entry holds a live instance, this function performs a single cache lookup and returns the instance. Otherwise, the function performs a cache lookup, an unsynchronized construction via the registered factory, and a cache install of the constructed instance. Concurrent invocations for the same uncached identifier MAY each perform unsynchronized construction; the cache retains a single instance, and all callers receive that instance. Constructed instances not retained in the cache are destroyed when their reference counts reach zero.

Unlike prism_registry_create_best, this function does not automatically initialize the backend. The returned backend may be uninitialized (if it was just created) or initialized (if it was retrieved from the cache and a previous caller initialized it). Applications SHALL call prism_backend_initialize and handle PRISM_ERROR_ALREADY_INITIALIZED gracefully.

The returned handle shares ownership with other handles to the same cached instance. When prism_backend_free is called, it releases the caller’s reference; the underlying backend is destroyed only when all references have been released.

Multiple calls to prism_registry_acquire with the same ID may return handles to the same underlying instance. This means that changes made through one handle (such as setting the voice or rate) will be visible through other handles. Applications that require isolated backend state SHOULD use prism_registry_create instead.

prism_registry_acquire_best

Acquires the highest-priority backend that successfully initializes, reusing a cached instance if available.

Syntax

PrismBackend *prism_registry_acquire_best(PrismContext *ctx);

Parameters

ctx

The Prism context. This parameter MUST NOT be NULL.

Return Value

Returns a pointer to an initialized backend instance on success. Returns NULL if no backend could be acquired and initialized.

Remarks

Note: A program that does not have a specific reason to share backend state with other callers SHOULD invoke prism_registry_create_best instead.

This function combines the automatic backend selection of prism_registry_create_best with the caching behavior of prism_registry_acquire.

The function examines registered backend identifiers in descending priority order. If a cache entry contains a live instance, Prism ensures that retained instance is initialized before returning it. This includes an instance that was originally created by prism_registry_acquire and subsequently initialized through prism_backend_initialize. If the cached instance cannot be initialized, selection continues with the next backend.

For an uncached identifier, construction MAY occur without holding the cache lock. If another caller installs a live instance for the same identifier while that construction or initialization is in progress, the cache entry determines the shared identity: Prism returns or initializes the retained cached instance rather than replacing it with a second live instance. Speculatively constructed instances that lose this race are destroyed when their references are released. Backends whose construction or initialization fails are not installed as new cache entries.

The returned backend is always initialized. Applications SHOULD NOT call prism_backend_initialize on the returned backend.

This is the most convenient function for applications that simply want a working TTS backend without any specific preferences.

Repeated invocations from a single thread will return the same instance, provided the instance has not been freed. Concurrent invocations from multiple threads MAY each perform unsynchronized construction; in such cases, the cache retains at most one instance per backend identifier, and all callers receive an initialized backend.

Backend Functions

Backend Functions

Backend functions operate on individual backend instances obtained from the registry.

prism_backend_free

Releases a backend instance.

Syntax

void prism_backend_free(PrismBackend *backend);

Parameters

backend

The backend to free. This parameter MAY be NULL.

Return Value

This function does not return a value.

Remarks

prism_backend_free releases the caller’s handle to a backend instance. The behavior depends on how the backend was obtained:

  • For backends obtained via prism_registry_create or prism_registry_create_best, calling prism_backend_free immediately destroys the backend and releases all associated resources. After the call returns, the backend pointer is invalid and MUST NOT be used.
  • For backends obtained via prism_registry_acquire or prism_registry_acquire_best, the backend uses reference counting. Calling prism_backend_free decrements the reference count. The backend is destroyed only when the reference count reaches zero. If other handles to the same instance exist (from other calls to prism_registry_acquire), the backend remains alive and those handles remain valid.

If backend is NULL, this function has no effect.

This function does not explicitly stop any speech that may be in progress. Whether this function stops speech or not is backend-dependent. Applications that want to ensure speech is stopped before releasing the backend SHOULD call prism_backend_stop first.

After calling prism_backend_free, the backend pointer MUST NOT be used for any purpose. Passing a freed pointer to any Prism function results in undefined behavior.

prism_backend_get_features

Returns a bitmask of all features supported by this backend, as well as other information.

Syntax

uint64_t prism_backend_get_features(PrismBackend *backend);

Parameters

backend

The backend to query. This parameter MUST NOT be NULL.

Returns

A bitmask of feature information.

Remarks

This function MAY be called regardless of backend initialization state.

The returned bitmask indicates which functions are implemented by the backend, whether the underlying engine is currently available, and whether the backend is a screen reader or a speech engine. If a bit that names a function is set, that function is implemented. If clear, calls to that function return PRISM_ERROR_NOT_IMPLEMENTED.

To determine runtime availability, this function MAY perform lightweight probes such as COM class factory lookups, RPC endpoint queries, D-Bus name ownership checks, or process enumeration. These operations are minimized but are not cost-free. Callers SHOULD cache the result and re-query only when the PRISM_BACKEND_IS_SUPPORTED_AT_RUNTIME bit is relevant.

Bit 1 is reserved.

The following bits are defined:

BitDescription
PRISM_BACKEND_IS_SUPPORTED_AT_RUNTIMEThe underlying engine or service is available. This determination is advisory; prism_backend_initialize MAY still fail.
PRISM_BACKEND_SUPPORTS_SPEAKprism_backend_speak is implemented.
PRISM_BACKEND_SUPPORTS_SPEAK_TO_MEMORYprism_backend_speak_to_memory is implemented.
PRISM_BACKEND_SUPPORTS_BRAILLEprism_backend_braille is implemented.
PRISM_BACKEND_SUPPORTS_OUTPUTprism_backend_output is implemented.
PRISM_BACKEND_SUPPORTS_IS_SPEAKINGprism_backend_is_speaking is implemented.
PRISM_BACKEND_SUPPORTS_STOPprism_backend_stop is implemented.
PRISM_BACKEND_SUPPORTS_PAUSEprism_backend_pause is implemented.
PRISM_BACKEND_SUPPORTS_RESUMEprism_backend_resume is implemented.
PRISM_BACKEND_SUPPORTS_SET_VOLUMEprism_backend_set_volume is implemented.
PRISM_BACKEND_SUPPORTS_GET_VOLUMEprism_backend_get_volume is implemented.
PRISM_BACKEND_SUPPORTS_SET_RATEprism_backend_set_rate is implemented.
PRISM_BACKEND_SUPPORTS_GET_RATEprism_backend_get_rate is implemented.
PRISM_BACKEND_SUPPORTS_SET_PITCHprism_backend_set_pitch is implemented.
PRISM_BACKEND_SUPPORTS_GET_PITCHprism_backend_get_pitch is implemented.
PRISM_BACKEND_SUPPORTS_REFRESH_VOICESprism_backend_refresh_voices is implemented.
PRISM_BACKEND_SUPPORTS_COUNT_VOICESprism_backend_count_voices is implemented.
PRISM_BACKEND_SUPPORTS_GET_VOICE_NAMEprism_backend_get_voice_name is implemented.
PRISM_BACKEND_SUPPORTS_GET_VOICE_LANGUAGEprism_backend_get_voice_language is implemented.
PRISM_BACKEND_SUPPORTS_GET_VOICEprism_backend_get_voice is implemented.
PRISM_BACKEND_SUPPORTS_SET_VOICEprism_backend_set_voice is implemented.
PRISM_BACKEND_SUPPORTS_GET_CHANNELSprism_backend_get_channels is implemented.
PRISM_BACKEND_SUPPORTS_GET_SAMPLE_RATEprism_backend_get_sample_rate is implemented.
PRISM_BACKEND_SUPPORTS_GET_BIT_DEPTHprism_backend_get_bit_depth is implemented.
PRISM_BACKEND_PERFORMS_SILENCE_TRIMMING_ON_SPEAKThe backend trims leading and trailing silence from each utterance before playing it.
PRISM_BACKEND_PERFORMS_SILENCE_TRIMMING_ON_SPEAK_TO_MEMORYThe backend trims leading and trailing silence from the audio stream before delivering it to the audio callback.
PRISM_BACKEND_SUPPORTS_SPEAK_SSMLReserved.
PRISM_BACKEND_SUPPORTS_SPEAK_TO_MEMORY_SSMLReserved.
PRISM_BACKEND_IS_SCREEN_READERThe backend sends messages to a screen reader.
PRISM_BACKEND_IS_SPEECH_ENGINEThe backend synthesizes speech without a screen reader.

At most one of PRISM_BACKEND_IS_SCREEN_READER and PRISM_BACKEND_IS_SPEECH_ENGINE is set. Screen Readers and Speech Engines describes the distinction.

prism_backend_name

Returns the human-readable name of a backend.

Syntax

const char *prism_backend_name(PrismBackend *backend);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

Return Value

Returns a pointer to a null-terminated string containing the backend name on success.

Remarks

This function may be called on a backend that has not been initialized.

The returned string is owned by the backend and remains valid for the lifetime of the backend instance. Applications MUST NOT modify or free the returned string.

The name returned by this function is the same as would be returned by prism_registry_name for the backend’s ID. It is provided as a convenience so that applications do not need to maintain a separate mapping from backend instances to IDs.

Example

PrismBackend *backend = prism_registry_create_best(ctx);
if (backend) {
    printf("Selected backend: %s\n", prism_backend_name(backend));
}

prism_backend_initialize

Initializes a backend instance.

Syntax

PrismError prism_backend_initialize(PrismBackend *backend);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

Return Value

ValueMeaning
PRISM_OKInitialization succeeded.
PRISM_ERROR_ALREADY_INITIALIZEDThe backend was already initialized.
PRISM_ERROR_BACKEND_NOT_AVAILABLEThe backend’s underlying system component is unavailable.
PRISM_ERROR_INTERNALAn internal error occurred during initialization.
PRISM_ERROR_MEMORY_FAILUREMemory allocation failed during initialization.
PRISM_ERROR_UNKNOWNAn unspecified error occurred.
PRISM_ERROR_INTERNAL_BACKEND_LIMIT_EXCEEDEDThe backend possesses an internal hard ceiling as to how many instances may be instantiated at any given time, and this limit would be exceeded were another to be initialized.

Remarks

This function prepares a backend instance for use. It MUST be called after creating a backend with prism_registry_create and before calling any function that performs speech synthesis or queries backend state (except prism_backend_name, prism_backend_free, and prism_backend_get_features).

For backends obtained via prism_registry_create_best or prism_registry_acquire_best, the backend is already initialized when returned. Calling prism_backend_initialize on such a backend returns PRISM_ERROR_ALREADY_INITIALIZED. Applications MAY treat this error as non-fatal.

The PRISM_ERROR_BACKEND_NOT_AVAILABLE error indicates that the backend cannot function because a required system component is missing or not running.

When a backend returns PRISM_ERROR_BACKEND_NOT_AVAILABLE, the backend instance is still valid but cannot be used for speech. Applications SHOULD free the backend and try a different one, or use prism_registry_create_best to automatically find a working backend.

Initialization may involve allocating system resources, connecting to services, or performing other setup operations. These operations may take a noticeable amount of time on some systems. Applications that require responsive UI SHOULD perform initialization on a background thread.

Once initialized, a backend remains initialized until it is freed. There is no function to explicitly uninitialize a backend.

prism_backend_speak

Synthesizes speech from the given text and plays it through the default audio output.

Syntax

PrismError prism_backend_speak(PrismBackend *backend, const char *text, bool interrupt);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

text

The text to speak. This parameter MUST NOT be NULL and MUST be a valid null-terminated UTF-8 string.

interrupt

Specifies whether to interrupt currently playing speech. If true, any speech in progress is stopped before the new speech begins. If false, behavior is backend-dependent.

Return Value

ValueMeaning
PRISM_OKSpeech synthesis was successfully initiated.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_INVALID_UTF8text contains invalid UTF-8 sequences.
PRISM_ERROR_SPEAK_FAILURESpeech synthesis failed.
PRISM_ERROR_BACKEND_NOT_AVAILABLEThe backend’s system component is no longer available.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support speech synthesis.

Remarks

This is the primary function for producing speech output. The text is synthesized using the backend’s current voice, rate, pitch, and volume settings, and played through the system’s default audio output device.

Depending on the backend, the function MAY return as soon as speech synthesis has been initiated, and it MAY not wait for the speech to complete. Applications that need to know when speech has finished SHOULD poll prism_backend_is_speaking or use prism_backend_speak_to_memory with a callback.

The interrupt parameter controls behavior when speech is already in progress:

  • If interrupt is true, any current speech is stopped immediately (equivalent to calling prism_backend_stop) before the new speech begins. The new speech will be the only speech playing.
  • If interrupt is false, behavior depends on the backend. Most backends will queue the new speech to play after the current speech finishes. Some backends may mix the new speech with the current speech. Screen reader backends typically honor their own interrupt settings.

The text parameter MUST be valid UTF-8. Prism validates the encoding before passing text to the backend. If the text contains invalid UTF-8 sequences (such as overlong encodings, surrogate halves, or sequences that decode to values above U+10FFFF), the function returns PRISM_ERROR_INVALID_UTF8 without initiating speech.

Some backends impose length limits on text. Extremely long strings may be truncated or may cause errors. Applications that need to speak very long texts SHOULD break them into smaller segments.

Screen reader backends may route speech through the screen reader’s own speech system, which may apply additional processing such as rate boosting or punctuation handling. This is generally the desired behavior when a screen reader is in use.

Example

/* Speak with interrupt */
PrismError err = prism_backend_speak(backend, "Alert: system error detected", true);
if (err != PRISM_OK) {
    fprintf(stderr, "Speech failed: %s\n", prism_error_string(err));
}
/* Queue additional speech */
err = prism_backend_speak(backend, "Please check the log for details.", false);
if (err != PRISM_OK) {
    fprintf(stderr, "Speech failed: %s\n", prism_error_string(err));
}

prism_backend_speak_to_memory

Synthesizes speech from the given text and delivers the audio samples to a callback function.

Syntax

PrismError prism_backend_speak_to_memory(
    PrismBackend *backend,
    const char *text,
    PrismAudioCallback callback,
    void *userdata
);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

text

The text to synthesize. This parameter MUST NOT be NULL and MUST be a valid null-terminated UTF-8 string.

callback

The function to receive audio data. This parameter MUST NOT be NULL. See the “Audio Callback” section for the callback signature and semantics.

userdata

An arbitrary pointer passed to the callback. This parameter MAY be NULL.

Return Value

ValueMeaning
PRISM_OKSynthesis completed and audio was delivered to the callback.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_INVALID_UTF8text contains invalid UTF-8 sequences.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support memory synthesis.
PRISM_ERROR_INTERNALAn internal error occurred during synthesis.
PRISM_ERROR_INVALID_AUDIO_FORMATThe audio format which the underlying engine returned to Prism cannot be understood by Prism, or it’s parameters are nonsensical.

Remarks

This function synthesizes speech but instead of playing it through the audio device, it delivers the raw audio samples to a callback function. This allows applications to process, analyze, store, or transmit the audio data.

Unlike prism_backend_speak, this function is always synchronous, regardless of the backend selected: it does not return until synthesis is complete and all audio data has been delivered to the callback. For long texts, this may take a significant amount of time. Applications that require responsive UI SHOULD call this function from a background thread.

The callback may be invoked multiple times during a single call to prism_backend_speak_to_memory, with each invocation delivering a portion of the audio. Alternatively, some backends may invoke the callback exactly once with all the audio data. Applications MUST be prepared for either pattern.

The callback may be invoked from a different thread than the one that called prism_backend_speak_to_memory. This depends on the backend; some backends synthesize in the calling thread, while others use background threads. Callback implementations MUST be thread-safe.

Audio samples are delivered as 32-bit floating-point values normalized to the range [-1.0, 1.0], regardless of the backend’s native format. Multi-channel audio is interleaved: for stereo, samples alternate left-right-left-right.

Not all backends support this function.

To determine the audio format before synthesis, use prism_backend_get_channels, prism_backend_get_sample_rate, and prism_backend_get_bit_depth. Note that the bit depth returned by prism_backend_get_bit_depth reflects the native format; samples delivered to the callback are always 32-bit float.

Example

void audio_callback(void *userdata, const float *samples, 
                    size_t sample_count, size_t channels, size_t sample_rate) {
    FILE *file = (FILE *)userdata;
    /* Write samples to file or process them */
    fwrite(samples, sizeof(float), sample_count, file);
}
FILE *output = fopen("speech.raw", "wb");
PrismError err = prism_backend_speak_to_memory(backend, "Hello", audio_callback, output);
fclose(output);

prism_backend_braille

Outputs text to a connected braille display.

Syntax

PrismError prism_backend_braille(PrismBackend *backend, const char *text);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

text

The text to display. This parameter MUST NOT be NULL and MUST be a valid null-terminated UTF-8 string.

Return Value

ValueMeaning
PRISM_OKText was successfully sent to the braille display.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_INVALID_UTF8text contains invalid UTF-8 sequences.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support braille output.
PRISM_ERROR_BACKEND_NOT_AVAILABLEThe backend’s system component is no longer available.
PRISM_ERROR_INTERNALAn internal error occurred.

Remarks

This function sends text to a braille display connected to the system. It is primarily supported by screen reader backends, which have their own braille display drivers, although not all screen reader backends support this function. Standalone TTS backends typically return PRISM_ERROR_NOT_IMPLEMENTED.

Braille output is independent of speech output. Calling this function does not affect any speech that may be in progress, and calling speech functions does not affect braille output.

The text is displayed on the braille display according to the screen reader’s braille settings, which may include translation to contracted braille. The exact behavior depends on the screen reader’s configuration.

If no braille display is connected, some backends return success (the text is simply discarded), while others return an error. Applications SHOULD NOT rely on the return value to determine whether a braille display is present.

For applications that want to output both speech and braille simultaneously, use prism_backend_output instead of calling prism_backend_speak and prism_backend_braille separately.

prism_backend_output

Outputs text using all available modalities supported by the backend.

Syntax

PrismError prism_backend_output(PrismBackend *backend, const char *text, bool interrupt);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

text

The text to output. This parameter MUST NOT be NULL and MUST be a valid null-terminated UTF-8 string.

interrupt

Specifies whether to interrupt current output. If true, any output in progress is stopped.

Return Value

ValueMeaning
PRISM_OKOutput was successfully initiated.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_INVALID_UTF8text contains invalid UTF-8 sequences.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support this operation.
PRISM_ERROR_SPEAK_FAILUREOutput failed.

Remarks

This function is a convenience wrapper that outputs text through all modalities supported by the backend. For TTS-only backends, it is equivalent to prism_backend_speak. For screen reader backends, it outputs to both speech and braille.

The interrupt parameter has the same semantics as in prism_backend_speak: if true, current output is stopped before new output begins.

If either speech or braille output fails, the function may still return PRISM_OK if the other modality succeeded. Applications that require confirmation that both modalities succeeded SHOULD call prism_backend_speak and prism_backend_braille separately and check both return values.

prism_backend_stop

Immediately stops any currently playing speech.

Syntax

PrismError prism_backend_stop(PrismBackend *backend);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

Return Value

ValueMeaning
PRISM_OKSpeech was stopped or no speech was playing.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support stopping.
PRISM_ERROR_INTERNALAn internal error occurred.

Remarks

This function immediately halts any speech currently being produced by the backend. Any queued speech is also discarded. After this function returns, prism_backend_is_speaking will return false.

Most backends return PRISM_OK even if no speech was playing when the function was called. However, applications SHOULD NOT rely on this behavior; some backends may return PRISM_ERROR_NOT_SPEAKING.

For screen reader backends, this function stops speech being produced through that backend’s API. It does not stop speech initiated by the screen reader itself (such as when the user navigates with keyboard commands).

This function is synchronous: when it returns, speech has stopped. However, due to audio buffering, a brief moment of audio may play after the function returns on some systems.

Calling prism_backend_stop while synthesis is in progress via prism_backend_speak_to_memory has undefined behavior. Applications SHOULD NOT mix synchronous memory synthesis with asynchronous playback operations.

prism_backend_pause

Pauses currently playing speech.

Syntax

PrismError prism_backend_pause(PrismBackend *backend);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

Return Value

ValueMeaning
PRISM_OKSpeech was paused.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_NOT_SPEAKINGNo speech is currently playing.
PRISM_ERROR_ALREADY_PAUSEDSpeech is already paused.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support pausing.
PRISM_ERROR_INTERNALAn internal error occurred.

Remarks

This function pauses speech at its current position. The speech can be resumed from the same position by calling prism_backend_resume. Unlike prism_backend_stop, pausing does not discard queued speech.

Not all backends support pause and resume functionality.

The function returns PRISM_ERROR_NOT_SPEAKING if no speech is currently playing. Applications that want to pause unconditionally (pausing if speaking, doing nothing otherwise) SHOULD check for this error.

The function returns PRISM_ERROR_ALREADY_PAUSED if speech is already paused. Attempting to pause multiple times without an intervening resume is not allowed.

While paused, the backend remains in a paused state until either prism_backend_resume or prism_backend_stop is called. Calling prism_backend_speak with interrupt set to true while paused will stop the paused speech and start new speech. Calling prism_backend_speak with interrupt set to false while paused will queue the new speech; it will be spoken after the paused speech is resumed and completes.

prism_backend_resume

Resumes previously paused speech.

Syntax

PrismError prism_backend_resume(PrismBackend *backend);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

Return Value

ValueMeaning
PRISM_OKSpeech was resumed.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_NOT_PAUSEDSpeech is not currently paused.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support resuming.
PRISM_ERROR_INTERNALAn internal error occurred.

Remarks

This function resumes speech that was previously paused by prism_backend_pause. Speech continues from the position where it was paused.

The function returns PRISM_ERROR_NOT_PAUSED if speech is not currently paused. This occurs if prism_backend_pause was never called or if prism_backend_stop was called after pausing.

After resuming, prism_backend_is_speaking will return true (assuming speech has not finished in the brief time since resuming).

prism_backend_is_speaking

Queries whether the backend is currently producing speech output.

Syntax

PrismError prism_backend_is_speaking(PrismBackend *backend, bool *out_speaking);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

out_speaking

Pointer to receive the speaking state. This parameter MUST NOT be NULL. On success, set to true if speech is currently playing, false otherwise.

Return Value

ValueMeaning
PRISM_OKThe speaking state was retrieved.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend cannot report speaking state.

Remarks

This function queries whether the backend is currently producing speech output. It returns true if speech is playing, false if speech has finished or no speech was started.

Paused speech is not considered “speaking”. If speech is paused, this function sets out_speaking to false.

Not all backends can accurately report their speaking state. Some backends, particularly screen reader backends, may return PRISM_ERROR_NOT_IMPLEMENTED. For these backends, applications cannot determine when speech has finished.

Due to the asynchronous nature of audio playback, there may be a brief delay between when the backend finishes generating audio and when this function returns false. Applications SHOULD NOT assume precise timing.

Backend Availability Enumeration

Background Availability Enumeration

The set of backends registered in a registry is fixed, but whether a given backend is usable at this moment is not. A screen reader may start or stop while an application is running, and Prism reflects this through the runtime availability bit PRISM_BACKEND_IS_SUPPORTED_AT_RUNTIME, which a backend reports through its feature flags. Because an application holds backend instances directly rather than routing every call through the library, a backend that was available when it was acquired may quietly become unavailable, and nothing in the backend portion of the API, besides the PRISM_BACKEND_IS_SUPPORTED_AT_RUNTIME bit, will announce that change, unless the application explicitly asks for this information.

Background availability enumeration closes that gap. When configured, a Prism context runs an internal thread that periodically samples each backend’s runtime availability and invokes an application-supplied callback whenever a backend transitions between available and unavailable. The feature is opt-in: a context whose configuration supplies no availability callback runs no such thread and incurs no cost.

The polling thread, the callback, and the sampling policy are all configured through the PrismConfig structure passed to prism_init; the relevant members are described in the chapter on context management. This chapter describes the callback type, the sampling model, and the functions that control the polling thread at runtime.

Sampling model

The polling thread performs a scan at a configurable interval. In each scan it samples the runtime availability of every backend in the registry and compares each sample against the last state it confirmed for that backend. The model has the following properties:

  1. The callback is invoked only when a backend’s confirmed availability changes. A backend that remains available or remains unavailable across many scans produces no callbacks.
  2. The first scan after the thread starts establishes a baseline without invoking the availability callback. A backend that is already available when the context is created therefore does not produce a spurious notification. An application that needs to know the initial availability of a backend MUST query it directly. The first scan runs on the poll thread after prism_init has returned, so it is not ordered with respect to such direct queries. A change that occurs between a direct query and the first scan’s sample of the same backend becomes part of the baseline and is never reported. The section on PrismAvailabilityBaselineCallback describes how an application detects this condition.
  3. A transition is confirmed only after the new state has been observed on a configurable number of consecutive scans. This absorbs momentary glitches that would otherwise produce a pair of spurious notifications.
  4. When configured with an upper bound above the base interval, the sampling interval grows while availability is unchanging and returns to the base interval the instant any sample disagrees with the confirmed state or a transition is confirmed. Backoff reduces the frequency of wakeups during long periods of inactivity without delaying the detection of a change once one begins to occur.
  5. The interval between scans is realized using the most efficient timer facility the platform provides, and the thread permits the operating system to align its wakeups with other timer activity. This allows a mostly-idle poll to avoid forcing dedicated wakeups. Coalescing applies whether or not backoff is enabled.

PrismAvailabilityCallback

The type of a function invoked when a backend’s runtime availability changes.

Syntax

typedef void(PRISM_CALL *PrismAvailabilityCallback)(void *userdata,
                                                    PrismBackendId backend,
                                                    const char *name,
                                                    bool available);

Parameters

userdata

The opaque pointer supplied as availability_userdata in the PrismConfig that configured polling. Prism does not interpret this value.

backend

The identifier of the backend whose availability changed.

name

The name of the backend, as a null-terminated string. This string points into the registry and is valid only for the duration of the call.

available

true if the backend has become available, false if it has become unavailable.

Remarks

The callback is invoked from Prism’s internal poll thread, not from a thread owned by the application. Callback implementations MUST provide their own synchronization if they touch shared state. A callback SHOULD do as little work as possible and MUST NOT block: the poll thread cannot perform further scans until the callback returns.

The callback MUST NOT call prism_shutdown on the context that owns the poll thread. Shutting a context down joins its poll thread, so a callback that does this deadlocks against itself. Read-only registry queries and backend acquisition on the same context are safe to call re-entrantly from within the callback.

The name pointer is owned by Prism and is valid only for the duration of the call; therefore, a callback that needs to retain it MUST copy it. The value passed as backend is a stable identifier and MAY be retained freely.

An application typically responds to a callback by discarding a backend instance it can no longer use and, when a preferred backend becomes available, acquiring it. The callback is a notification that the application’s cached choice of backend may be stale, and does not itself change any backend instance the application holds.

PrismAvailabilityBaselineCallback

The type of a function invoked once the poll thread has established its baseline.

Syntax

typedef void(PRISM_CALL *PrismAvailabilityBaselineCallback)(void *userdata);

Parameters

userdata

The opaque pointer supplied as availability_userdata in the PrismConfig that configured polling. Prism does not interpret this value.

Remarks

The baseline callback is invoked exactly once per context, on the poll thread, after the first scan has completed and before the first invocation of the availability callback. The first scan and the baseline callback occur even if polling is paused before the first scan begins. The baseline callback is not invoked if the context is shut down before the first scan completes.

The baseline callback conveys no information about any backend. It identifies neither which backends were sampled nor the state observed for any of them. Its only meaning is that the baseline now exists, which fixes the point from which the availability callback reports changes.

During the first scan, the poll thread samples each backend once, one backend after another, and records the sample as that backend’s confirmed state. A backend that cannot be instantiated, or whose sample cannot be taken, retains a confirmed state of unavailable. After the baseline callback has been invoked, any change in a backend’s availability relative to its confirmed state is reported through the availability callback, subject to the debouncing described in the section on the sampling model and to the rules described in the section on interaction with pausing. A change that occurred before the first scan sampled a particular backend is not reported, because the sample already reflects it.

The baseline callback is therefore useful only to an application that meets both of the following conditions:

  1. Before the baseline callback is invoked, the application observes the availability of one or more backends directly, for example by testing PRISM_BACKEND_IS_SUPPORTED_AT_RUNTIME in the result of prism_backend_get_features, and retains a decision derived from that observation, such as which backend to use.
  2. The application relies on the availability callback, rather than on observations made each time the decision is used, to learn when that decision has become stale.

Such an application cannot determine from the availability callback alone whether a backend changed state between its observation and the first scan’s sample of that backend. When the baseline callback is invoked, the application SHOULD repeat every direct observation made before the callback that underlies a retained decision, and revise the decision accordingly. Because the callback does not identify any backend, no observation can be omitted on the grounds that the corresponding backend did not change. A repeated observation is made after the first scan sampled the backend in question, so any later change relative to the baseline is reported through the availability callback. The application MAY consequently receive a notification for a transition that it has already observed through a repeated observation, and SHOULD treat such a notification as harmless.

An application that makes no direct observations before the baseline callback is invoked, or that does not retain decisions derived from them, or that observes availability each time it acts on it, has no use for the baseline callback and MAY leave availability_baseline_callback set to NULL. An application MAY also avoid the condition entirely by deferring its first direct observation until the baseline callback has been invoked, at the cost of waiting for the first scan to complete.

The constraints that apply to the availability callback apply equally to the baseline callback: it MUST NOT block, it MUST synchronize any shared state that it accesses, and it MUST NOT call prism_shutdown on the owning context. Because the baseline callback runs on the poll thread, it MUST NOT repeat observations on a backend instance that is used from another thread without external synchronization, as described in the chapter on thread safety. An application typically records in the callback that its observations are to be repeated, and repeats them on the thread that owns the affected backend instances.

Interaction with pausing

The poll thread MAY be paused and resumed at runtime, either automatically in response to operating-system power transitions or under explicit application control. While paused, the thread performs no scans and consumes no processor time.

When the thread resumes, it performs an immediate re-synchronizing scan rather than waiting for the next interval. Because an arbitrary amount of time may have passed while the thread was paused, this scan is not debounced: any backend whose availability differs from the state last reported to the application produces a callback at once. Pausing and resuming therefore never causes a real change to be missed, though a change that occurs and then reverses entirely within a paused interval is not reported, since only the net difference is observed on resume.

prism_availability_poll_pause

Pauses the availability poll thread.

Syntax

void prism_availability_poll_pause(PrismContext *ctx);

Parameters

ctx

The Prism context. This parameter MUST NOT be NULL.

Return Value

This function does not return a value.

Remarks

This function suspends availability polling for the given context. While paused, the poll thread parks and consumes no processor time. A scan already in progress when this function is called is allowed to complete; the pause takes effect at the following scan.

This function is safe to call from any thread. It is a no-op if the context was not configured with an availability callback or if polling is already paused.

Pausing is intended for applications that wish to suppress polling while they are backgrounded or otherwise idle, particularly on platforms where automatic power management is unavailable (see prism_availability_auto_power_supported). Such applications typically drive prism_availability_poll_pause and prism_availability_poll_resume from operating-system lifecycle events.

prism_availability_poll_resume

Resumes the availability poll thread.

Syntax

void prism_availability_poll_resume(PrismContext *ctx);

Parameters

ctx

The Prism context. This parameter MUST NOT be NULL.

Return Value

This function does not return a value.

Remarks

This function resumes availability polling for a context previously paused with prism_availability_poll_pause. On resume, the poll thread performs an immediate re-synchronizing scan and invokes the availability callback for every backend whose availability differs from the state last reported, without debouncing, as described in the section on interaction with pausing. The sampling interval is reset to its base value.

This function is safe to call from any thread. It is a no-op if the context was not configured with an availability callback or if polling is not paused.

prism_availability_auto_power_supported

Reports whether this build can pause and resume polling automatically in response to operating-system power transitions.

Syntax

bool prism_availability_auto_power_supported(void);

Parameters

This function has no parameters.

Return Value

Returns true if the availability_auto_power_manage configuration option is honored on this build, and false otherwise.

Remarks

When this function returns true, setting availability_auto_power_manage in PrismConfig causes Prism to pause the poll thread when the system suspends and resume it when the system wakes, with no further action required from the application.

When it returns false, the option has no effect. This is the case on platforms where a library-level component cannot observe power transitions, and on builds compiled without power-management support. On such platforms, an application that wishes to avoid polling while the machine is unattended MUST drive prism_availability_poll_pause and prism_availability_poll_resume itself, from whatever lifecycle notifications it receives from the operating system.

This function reflects a compile-time and platform property and MAY be called at any time, including before prism_init.

Custom Backends

Custom Backends

This chapter specifies the interface through which an application supplies its own backend implementations to Prism, and the obligations each party assumes toward the other.

Definitions

For the purposes of this chapter, the following terms have the meanings given below.

  1. A custom backend is a backend implemented by the application and described to Prism by a table of function pointers of type PrismBackendVTable.
  2. A registration is the record Prism retains of one successful call to prism_registry_builder_add_backend, comprising the copied vtable, the userdata pointer, the userdata_free function, the declared feature set, and the backend’s name, identifier, and priority.
  3. A builder is a mutable collection of registrations, represented by a PrismRegistryBuilder. A builder is at all times either live or spent. A builder is live when created and becomes spent when successfully frozen. No operation returns a spent builder to the live state.
  4. A registration is referenced by the live builder that holds it, by any registry frozen from that builder, and by every backend instance created from it. A registration becomes unreferenced when the last of these referents ceases to exist.
  5. A registry is finalized when its reference count reaches zero. Finalization releases the registry’s backend cache and the registry’s references to its registrations. Finalization does not affect backend instances, which remain valid until freed.

General

A custom backend participates in a registry on the same terms as a compiled-in backend. It has an identifier derived from its name by the hash function described in the chapter on backend identifiers, a priority, and a declared feature set, and it participates in the priority-ordered selection performed by prism_registry_create_best and prism_registry_acquire_best. No restriction is placed on the priority of a custom backend relative to the compiled-in backends.

Custom backends are never present in the global registry. An application wishing to use one constructs a registry as follows:

  1. It creates a builder with prism_registry_builder_new.
  2. Registrations can then be added with prism_registry_builder_add_backend.
  3. Once the builder has been populated with all registrations the application wishes to perform, it freezes the builder with prism_registry_freeze.
  4. The registry is bound to one or more contexts through the registry field of PrismConfig.

A newly created builder already contains an entry for every compiled-in backend, so a frozen registry is always a superset of the global registry.

A registry cannot be modified after it is frozen. An application requiring a different set of custom backends constructs a new registry.

Guarantees made to the implementation

Prism validates the following before consulting the implementation. A call that fails validation returns the indicated error without any vtable function being invoked.

  • Every string argument is non-null, null-terminated, and valid UTF-8. A call supplying invalid UTF-8 fails with PRISM_ERROR_INVALID_UTF8.
  • Every argument to set_volume, set_rate, and set_pitch is finite and lies within [0.0, 1.0]. A call supplying any other value fails with PRISM_ERROR_RANGE_OUT_OF_BOUNDS.
  • No member other than initialize, create, destroy, and is_supported is invoked before an invocation of initialize has succeeded on the instance in question (or, if the initialize member is null, before initialization has trivially succeeded). A call arriving earlier fails with PRISM_ERROR_NOT_INITIALIZED.
  • initialize is not invoked on an instance whose initialization has already succeeded. Such a call fails with PRISM_ERROR_ALREADY_INITIALIZED.
  • pause is not invoked on an instance Prism considers paused, and resume is not invoked on an instance Prism does not consider paused. Such calls fail with PRISM_ERROR_ALREADY_PAUSED and PRISM_ERROR_NOT_PAUSED respectively. An instance is considered paused after a successful pause, and ceases to be considered paused after a successful resume or a successful stop.

Prism further guarantees two invariants that involve no per-call validation. First, vtable functions are never invoked concurrently for the same instance, provided the application observes the constraints given in the chapter on thread safety. Second, every invocation of destroy arising from a given registration precedes the invocation of userdata_free for that registration.

An implementation MAY rely on the guarantees in this section and need not defend against their violation. State that Prism cannot determine from the sequence of calls it makes, in particular whether speech is in progress, is not tracked by Prism and remains the responsibility of the implementation.

Requirements on implementations

An implementation of a custom backend MUST adhere to the following.

  1. A vtable function MUST return a defined PrismError value. Prism substitutes PRISM_ERROR_UNKNOWN for any return value outside the defined range rather than propagate it to the application.
  2. A vtable function MUST NOT return abnormally across the Prism boundary. Unwinding through Prism, whether by a C++ exception, by longjmp, or by any other means, results in undefined behavior.
  3. speak_to_memory MUST deliver audio synchronously: every invocation of the callback it receives MUST occur before it returns. Retaining the callback pointer or its userdata beyond that point, or invoking either afterwards, likewise results in undefined behavior. This requirement is stricter than the corresponding contract for compiled-in backends, some of which deliver audio asynchronously.
  4. Values reported through get_volume, get_rate, and get_pitch SHOULD lie within [0.0, 1.0], and audio samples delivered through the speak_to_memory callback SHOULD lie within [-1.0, 1.0]. Prism tolerates limited departures from both. For parameter values, a finite out-of-range value is clamped into range, and a non-finite value causes the call to fail with PRISM_ERROR_BACKEND_ENTERED_UNDEFINED_STATE. For audio samples, a finite out-of-range sample is clamped, and a non-finite sample is replaced with silence. This sanitization exists so that a defective implementation cannot corrupt the application and MUST NOT be relied upon.
  5. The function pointers in a registered vtable MUST remain valid until the registration is unreferenced. Prism has no means of detecting that the code behind a vtable has been unloaded; unloading it early leaves dangling function pointers that Prism may later invoke.

PrismBackendVTable

A table of function pointers implementing a custom backend.

Syntax

typedef struct PrismBackendVTable {
  size_t size;
  void *(*create)(void *userdata);
  void (*destroy)(void *instance);
  bool (*is_supported)(void *instance);
  PrismError (*initialize)(void *instance);
  PrismError (*speak)(void *instance, const char *text, bool interrupt);
  PrismError (*speak_to_memory)(void *instance, const char *text,
                                PrismAudioCallback callback,
                                void *callback_userdata);
  PrismError (*braille)(void *instance, const char *text);
  PrismError (*output)(void *instance, const char *text, bool interrupt);
  PrismError (*stop)(void *instance);
  PrismError (*pause)(void *instance);
  PrismError (*resume)(void *instance);
  PrismError (*is_speaking)(void *instance, bool *out_speaking);
  PrismError (*set_volume)(void *instance, float volume);
  PrismError (*get_volume)(void *instance, float *out_volume);
  PrismError (*set_rate)(void *instance, float rate);
  PrismError (*get_rate)(void *instance, float *out_rate);
  PrismError (*set_pitch)(void *instance, float pitch);
  PrismError (*get_pitch)(void *instance, float *out_pitch);
  PrismError (*refresh_voices)(void *instance);
  PrismError (*count_voices)(void *instance, size_t *out_count);
  PrismError (*get_voice_name)(void *instance, size_t voice_id,
                               const char **out_name);
  PrismError (*get_voice_language)(void *instance, size_t voice_id,
                                   const char **out_language);
  PrismError (*set_voice)(void *instance, size_t voice_id);
  PrismError (*get_voice)(void *instance, size_t *out_voice_id);
  PrismError (*get_channels)(void *instance, size_t *out_channels);
  PrismError (*get_sample_rate)(void *instance, size_t *out_sample_rate);
  PrismError (*get_bit_depth)(void *instance, size_t *out_bit_depth);
} PrismBackendVTable;

Members

size

The size of this structure as known to the application. This member MUST be set to sizeof(PrismBackendVTable). Prism reads at most size bytes from the structure. If size is smaller than the size of the structure as this version of Prism defines it, the members beyond size are treated as null; if it is larger, the additional bytes are ignored. This scheme permits the structure to grow in later library versions without invalidating applications compiled against earlier ones.

create

An optional function producing a per-instance state pointer. If non-null, Prism invokes it exactly once for each backend instance constructed from the registration, passing the registration’s userdata, and thereafter passes the returned pointer as the instance argument to every other member invoked for that instance. Should create return NULL, construction of the instance fails. If create is null, the registration’s userdata pointer is passed as the instance argument directly, and all instances of the backend consequently share it.

destroy

An optional function releasing a state pointer previously returned by create. If both create and destroy are non-null, Prism invokes destroy exactly once for each backend instance, at the time the instance is freed. destroy is never invoked if create is null.

is_supported

An optional runtime availability probe. If non-null, Prism invokes it to determine the PRISM_BACKEND_IS_SUPPORTED_AT_RUNTIME bit reported by prism_backend_get_features; if null, the bit declared at registration is reported unchanged. Because prism_backend_get_features MAY be called before initialization, is_supported MAY be invoked before initialize has succeeded, and an implementation of it MUST NOT assume the instance has been initialized. This member designates no operation and is therefore exempt from the feature consistency requirement of prism_registry_builder_add_backend.

initialize through get_bit_depth

Optional functions implementing the corresponding backend operations. Each carries the contract of the prism_backend_ function of the same name, except that its first argument is the instance pointer. A null member denotes an unimplemented operation, for which Prism returns PRISM_ERROR_NOT_IMPLEMENTED without invoking the implementation, exactly as for a compiled-in backend. As the sole exception, a null initialize causes initialization to succeed trivially, for the benefit of implementations requiring no setup.

prism_registry_builder_new

Creates a new registry builder seeded with the compiled-in backends.

Syntax

PrismRegistryBuilder *prism_registry_builder_new(void);

Parameters

This function has no parameters.

Return Value

Returns a pointer to a newly allocated, live PrismRegistryBuilder on success. Returns NULL if memory allocation fails.

Remarks

The returned builder contains an entry for every backend compiled into the library, at the priority each backend declares. The builder is owned by the caller and MUST eventually be released with prism_registry_builder_free, whether or not it is frozen first.

A builder is not a registry. It cannot be named by the registry field of PrismConfig, and no function outside this chapter accepts one. Its sole purpose is to accumulate registrations and be frozen.

prism_registry_builder_add_backend

Adds a custom backend to a registry builder.

Syntax

PrismError prism_registry_builder_add_backend(PrismRegistryBuilder *builder,
                                              const char *name, int priority,
                                              uint64_t features,
                                              const PrismBackendVTable *vtable,
                                              void *userdata,
                                              void (*userdata_free)(void *),
                                              PrismBackendId *out_id);

Parameters

builder

The builder to add the backend to. This parameter MUST NOT be NULL.

name

The backend’s name, as a null-terminated UTF-8 string. This parameter MUST NOT be NULL and MUST NOT be empty. The backend’s identifier is derived from this name in the same manner as for compiled-in backends. The string is copied and therefore may be freed after this function returns.

priority

The backend’s priority. Higher values indicate higher priority, exactly as for compiled-in backends. This value MUST be non-negative.

features

The feature set the backend declares, formed by ORing PRISM_BACKEND_* feature constants together. The declared set MUST satisfy the consistency requirement given under Remarks.

vtable

The vtable implementing the backend. This parameter MUST NOT be NULL, and its size member MUST be set as specified above. The structure is copied during this call.

userdata

An opaque pointer passed to the backend’s create function, or used directly as the instance pointer if no create function is supplied. This parameter MAY be NULL.

userdata_free

An optional function releasing userdata, subject to the invocation guarantee given under Remarks. This parameter MAY be NULL.

out_id

An optional pointer receiving the identifier assigned to the backend. This parameter MAY be NULL. It is written only when the function returns PRISM_OK.

Return Value

ValueMeaning
PRISM_OKThe backend was added to the builder.
PRISM_ERROR_INVALID_PARAMThe vtable’s size member was zero, the declared feature set contained unknown bits, declared both PRISM_BACKEND_IS_SCREEN_READER and PRISM_BACKEND_IS_SPEECH_ENGINE, or was inconsistent with the vtable, name was empty or hashes to the reserved identifier PRISM_BACKEND_INVALID, or the priority value was negative.
PRISM_ERROR_INVALID_UTF8name contains invalid UTF-8 sequences.
PRISM_ERROR_INVALID_OPERATIONThe builder is spent, or a backend with the same name or the same identifier is already present in the builder.
PRISM_ERROR_MEMORY_FAILUREMemory allocation failed.

Remarks

For every feature constant that designates an operation, the corresponding vtable member MUST be non-null if and only if the feature is declared. A registration violating this requirement is rejected during this call, with the consequence that no context can ever observe a backend whose declared features and implemented operations disagree. The is_supported member is exempt, as it designates no operation.

Every builder begins with the compiled-in backends present, so their names and identifiers are always reserved and MUST NOT be used by any custom backend. A custom backend whose name collides with one of them is rejected as a duplicate.

If userdata_free is non-null, Prism invokes it with userdata exactly once per call to this function, whatever the outcome. On failure, the invocation occurs before the call returns. On success, it is deferred until the registration becomes unreferenced, and occurs synchronously on the thread whose action caused the registration to become unreferenced; it is unspecified which library function performs the invocation when more than one could. This invocation is the only notification the application receives that userdata, and the code behind the vtable’s function pointers, are no longer needed. Applications that load speech engines dynamically SHOULD tie unloading to it.

Adding a backend constructs no instance of it. The create member, if any, is first invoked when an instance is created from a registry frozen from this builder.

prism_registry_freeze

Freezes a builder, producing an immutable registry.

Syntax

PrismRegistry *prism_registry_freeze(PrismRegistryBuilder *builder);

Parameters

builder

The builder to freeze. This parameter MUST NOT be NULL.

Return Value

Returns a pointer to a newly created PrismRegistry on success. Returns NULL if the builder is spent or if memory allocation fails.

Remarks

On success, the builder’s registrations are transferred to the returned registry and the builder becomes spent. A spent builder holds no registrations, and no operations may be performed on it other than freeing it.

The returned registry is reference-counted, with an initial count of one owned by the caller. Each context bound to the registry holds an additional reference for the lifetime of the context. The caller MUST eventually release its reference with prism_registry_release. The registry is finalized when the last reference, whichever party holds it, is released.

Example

The following program defines a minimal custom backend that writes speech requests to a log file, registers it, and speaks through it. Because the vtable supplies no create function, the single LogEngine allocated by the application serves as the instance state directly, and log_engine_free receives it when the registration becomes unreferenced.

#include <stdio.h>
#include <stdlib.h>
#include <prism.h>

typedef struct {
    FILE *log;
} LogEngine;

static PrismError log_speak(void *instance, const char *text, bool interrupt) {
    (void)interrupt;
    LogEngine *engine = (LogEngine*)instance;
    fprintf(engine->log, "speak: %s\n", text);
    fflush(engine->log);
    return PRISM_OK;
}

static PrismError log_stop(void *instance) {
    (void)instance;
    return PRISM_OK;
}

static void log_engine_free(void *userdata) {
    LogEngine *engine = (LogEngine*)userdata;
    fclose(engine->log);
    free(engine);
}

static const PrismBackendVTable log_vtable = {
    .size = sizeof(PrismBackendVTable),
    .speak = log_speak,
    .stop = log_stop,
};

int main(void) {
    LogEngine *engine = malloc(sizeof(LogEngine));
    if (!engine) {
        return 1;
    }
    engine->log = fopen("speech.log", "w");
    if (!engine->log) {
        free(engine);
        return 1;
    }
    PrismRegistryBuilder *builder = prism_registry_builder_new();
    if (!builder) {
        log_engine_free(engine);
        return 1;
    }
    PrismBackendId id;
    PrismError err = prism_registry_builder_add_backend(
        builder, "Example Logger", 10,
        PRISM_BACKEND_SUPPORTS_SPEAK | PRISM_BACKEND_SUPPORTS_STOP,
        &log_vtable, engine, log_engine_free, &id);
    if (err != PRISM_OK) {
        /* Ownership of engine has already passed to Prism; userdata_free
           has been invoked. Only the builder remains to clean up. */
        fprintf(stderr, "Registration failed: %s\n", prism_error_string(err));
        prism_registry_builder_free(builder);
        return 1;
    }
    PrismRegistry *registry = prism_registry_freeze(builder);
    prism_registry_builder_free(builder);
    if (!registry) {
        return 1;
    }
    PrismConfig cfg = prism_config_init();
    cfg.registry = registry;
    PrismContext *ctx = prism_init(&cfg);
    if (!ctx) {
        prism_registry_release(registry);
        return 1;
    }
    PrismBackend *backend = prism_registry_create(ctx, id);
    if (backend) {
        err = prism_backend_initialize(backend);
        if (err == PRISM_OK) {
            err = prism_backend_speak(backend, "Hello from a custom backend", true);
        }
        if (err != PRISM_OK) {
            fprintf(stderr, "Speech failed: %s\n", prism_error_string(err));
        }
        prism_backend_free(backend);
    }
    prism_shutdown(ctx);
    prism_registry_release(registry);
    return 0;
}

prism_registry_builder_free

Releases a registry builder.

Syntax

void prism_registry_builder_free(PrismRegistryBuilder *builder);

Parameters

builder

The builder to release. This parameter MAY be NULL.

Return Value

This function does not return a value.

Remarks

This function releases a builder in any state. After it returns, the builder pointer is invalid, and any use of it results in undefined behavior.

Releasing a spent builder frees bookkeeping only. Releasing a live builder discards the registrations it holds; each discarded registration thereby becomes unreferenced, with the consequence for userdata_free specified under prism_registry_builder_add_backend.

If builder is NULL, this function has no effect.

prism_registry_retain

Increments the reference count of a registry.

Syntax

PrismRegistry *prism_registry_retain(PrismRegistry *registry);

Parameters

registry

The registry to retain. This parameter MAY be NULL.

Return Value

Returns registry.

Remarks

This function increments the registry’s reference count and returns the same pointer, permitting inline use wherever a retained copy is wanted. It is provided primarily for language bindings and other code that manages registry lifetime by reference counting. Every successful retain MUST be balanced by exactly one later call to prism_registry_release.

If registry is NULL, this function has no effect and returns NULL.

prism_registry_release

Decrements the reference count of a registry, finalizing it when the count reaches zero.

Syntax

void prism_registry_release(PrismRegistry *registry);

Parameters

registry

The registry to release. This parameter MAY be NULL.

Return Value

This function does not return a value.

Remarks

When the reference count reaches zero, the registry is finalized as defined at the beginning of this chapter. A registration held by the finalized registry becomes unreferenced at that moment only if no backend instance created from it remains alive; otherwise it becomes unreferenced when the last such instance is freed. Backend instances are unaffected by finalization and remain valid until freed.

After the count reaches zero, the registry pointer is invalid, and any use of it results in undefined behavior. An application MUST NOT release more references than it holds. Releasing the application’s own reference while contexts bound to the registry remain alive is permitted; the registry then survives until those contexts have been shut down.

If registry is NULL, this function has no effect.

Shared Library Backends

Shared Library Backends

This chapter specifies the mechanism through which an application loads custom backends from a shared library at run time. The requirements of custom backends also applies to shared library ones. This chapter only discusses the set of requirements that apply solely to shared library backends and not custom backends.

General

A plugin is a shared library that supplies custom backends to Prism at run time. Every plugin exports one function, the plugin entry point, named prism_plugin_query. Prism calls it to obtain backend descriptors.

A backend descriptor is a PrismPluginBackend structure. It names one backend and gives its priority, feature set, and vtable.

Loading is the sequence of operations Prism performs in one call to prism_registry_builder_add_library. Prism opens the library, resolves the entry point, obtains the descriptors, examines them, and adds a backend for each. This chapter describes that sequence; Prism’s internal implementation need only behave as if it executed the sequence in this order.

Prism obtains a plugin’s descriptors by calling the entry point with successive indices, beginning at zero. Enumeration stops at the first index for which the entry point returns NULL. A plugin MAY supply any number of backends, subject to the limits described under Implementation limits.

For each descriptor obtained, Prism adds a backend to the builder. The backend is added as though the application had called prism_registry_builder_add_backend with the descriptor’s name, priority, feature set, and vtable. It is thereafter indistinguishable from a directly registered custom backend.

An application loads a plugin as follows:

  1. It creates a builder with prism_registry_builder_new, or reuses one it is still populating.
  2. It calls prism_registry_builder_add_library, naming the shared library.
  3. Once the builder holds every backend the application wishes to register, it freezes the builder with prism_registry_freeze.
  4. The registry is bound to one or more contexts through the registry field of PrismConfig.

A plugin’s backends are added to a builder only. They are never present in the global registry.

Loading is atomic. If any part of it fails, no backend from the library is added, and the library is unloaded before prism_registry_builder_add_library returns. A partially loaded plugin is never observable.

ABI compatibility

The plugin ABI comprises the layout and semantics of PrismPluginHost, of PrismPluginBackend, of the plugin entry point, and of every type they refer to. Two members of each structure govern compatibility: abi_version and struct_size. Both occupy fixed offsets in every generation, so either party can always read them.

The abi_version member gives an ABI generation. Prism increments it only for a change that is not backward compatible, such as reordering a member or changing the entry point’s signature. The generation this version of Prism implements is defined by the PRISM_PLUGIN_ABI_VERSION macro.

The struct_size member gives the size of the structure as the party supplying it defines it. Within one generation, a structure MAY grow by the addition of members at its end. The reader consults at most struct_size bytes and treats members beyond that point as absent.

Compatibility is settled during loading, and either side MAY refuse. A plugin that requires a newer host than the one presented declines by returning NULL at index 0, and SHOULD first record its reason through the host descriptor’s log member. Prism rejects a descriptor whose generation exceeds PRISM_PLUGIN_ABI_VERSION, or falls below the oldest generation it accepts.

A plugin declaring a generation Prism accepts MUST have been compiled against a layout-compatible definition of every shared structure. The two definitions may differ only in members appended beyond struct_size.

Host services

Prism gives every backend it registers from a plugin a services object. A services object is a PrismPluginServices structure, particular to one backend, through which that backend reaches the facilities Prism offers it.

A backend receives its services object through the instance context passed to its create member, which is required. The convention by which a registration’s userdata serves as the instance pointer directly is not available to it.

Every function of a services object takes that object as its first argument. Prism thereby supplies, on the backend’s behalf, what it would otherwise require the backend to state. The source a diagnostic is recorded under is such a value.

A services object is valid from the moment its backend is registered until the library has been unloaded. Prism releases it only after unloading completes, so code the library runs while unloading MAY still use it.

Unlike consumers of Prism, a plugin MUST NOT call the functions Prism exports. Instead, it reaches Prism through the host descriptor and through its backends’ services objects. A plugin therefore uses only the type and macro definitions of the Prism header, and does not link the library.

Threads and apartments

Prism calls the plugin entry point only on the thread that calls prism_registry_builder_add_library, only during that call, and never concurrently. The entry point MUST NOT assume any particular thread, COM apartment, or platform main context. It MUST NOT retain the host descriptor beyond the call.

The is_supported member of a plugin’s vtable MAY be invoked from Prism’s internal availability poll thread, described in the chapter on background availability enumeration. It MAY also be invoked from application threads. Since the poll thread is not owned by the application, a plugin MUST NOT assume that it has entered any particular COM apartment, that any platform main context is current on it, or that it is the thread on which the instance was created.

Some plugins wrap a resource with thread affinity, such as a COM object with apartment affinity or a handle bound to an event loop. Such a plugin MUST confine its use of that resource within is_supported to the single call, for example by acquiring and releasing it there. An is_supported that relies on affinity established on another thread results in undefined behavior.

Every member of the vtable other than is_supported is subject to the single-instance constraint given in the chapter on thread safety.

Library lifetime

Prism opens the library once and shares it among every backend registered from it. The library remains loaded for as long as any registration derived from it remains referenced.

A descriptor’s vtable function pointers reside in the library’s image, as does the name string Prism copies during loading. They remain valid for as long as the library remains loaded. Unloading the image while a registration referring to it remains leaves dangling function pointers that Prism may later invoke, and results in undefined behavior.

Prism does not free the object a descriptor’s userdata member designates. A plugin requiring that object to be released at a determined time SHOULD tie the release to the library’s own unloading, through whatever mechanism the platform provides. Whether the platform provides such a mechanism, or whether the platform ever actually unloads the library, is unspecified.

Implementation limits

This chapter describes the model Prism presents to a plugin. It does not oblige Prism to accept every plugin the model allows.

Prism MAY impose limits on what a plugin supplies. For example, Prism may constrain the number of descriptors one plugin may offer or the length of a backend’s name. A plugin that exceeds a limit is rejected, and loading fails as it does for any other malformed plugin. Exceeding a limit does not result in undefined behavior. Which limits Prism imposes, and whether it imposes any, is unspecified. Similarly, Prism MAY refuse to load a library for reasons this chapter does not describe; for example, Prism may refuse to load a library due to Code signing requirements, sandbox restrictions, loader policy, or Prism’s own security posture. A rejection on these grounds is indistinguishable from any other kind of load failure.

Lastly, Prism MAY examine a descriptor more strictly than this chapter requires, and MAY reject one this chapter does not require it to reject. Prism does not accept a descriptor this chapter requires it to reject.

Security and deployment

Opening a shared library runs that library’s initialization code before Prism reads any descriptor. Initialization code of this kind includes DLLMain or static constructors and destructors. Prism does not and will not disallow this code from executing. An application MUST therefore treat prism_registry_builder_add_library as equivalent to running code of the library’s choosing.

Note

Loading code at run time is restricted on some platforms and prohibited on others. Application store policies and sandboxed environments are examples of these kinds of restrictions. An application SHOULD establish that this mechanism is permitted where it will be deployed before relying on it. An application that cannot rely on it MUST register its backends directly instead.

PrismPluginHost

A structure describing the loading Prism library, passed to a plugin’s entry point.

Syntax

typedef struct PrismPluginHost {
  uint64_t abi_version;
  uint32_t struct_size;
  uint32_t reserved;
  void(PRISM_CALL *log)(const PrismPluginHost *self, PrismLogLevel level,
                        const char *message);
} PrismPluginHost;

Members

abi_version

The plugin ABI generation this implementation provides, equal to the PRISM_PLUGIN_ABI_VERSION against which Prism was compiled. A plugin MAY compare this value against its own requirements and decline to supply backends if it requires a newer host.

struct_size

The size of this structure as Prism understands it. A plugin MUST consult at most this many bytes and MUST treat any member beyond it as absent.

reserved

Reserved for future use. Prism sets this member to zero, and a plugin MUST ignore it.

log

A function through which the plugin MAY record diagnostics during the entry point call. Its first argument MUST be the host descriptor Prism supplied; the plugin does not supply a source, and Prism records the diagnostic under a source that names the library being loaded. This member is never NULL. A plugin SHOULD use it to state the reason for declining a host, and MUST NOT retain the pointer beyond the entry point call.

PrismPluginBackend

A descriptor supplied by a plugin to describe a single backend.

Syntax

typedef struct PrismPluginBackend {
  uint64_t abi_version;
  uint32_t struct_size;
  uint32_t reserved;
  const char *name;
  int priority;
  uint64_t features;
  const PrismBackendVTable *vtable;
  void *userdata;
  uint64_t plugin_version;
} PrismPluginBackend;

Members

abi_version

The plugin ABI generation this descriptor was built against. A plugin MUST set this member to the PRISM_PLUGIN_ABI_VERSION against which it was compiled. Prism rejects a descriptor whose generation it does not accept, as described under ABI compatibility.

struct_size

The size of this structure as the plugin understands it. A plugin MUST set this member to sizeof(PrismPluginBackend). Prism consults at most this many bytes.

reserved

Reserved for future use. A plugin MUST set this member to zero.

name

The backend’s name, as a null-terminated UTF-8 string, subject to the same requirements and consequences as the name parameter of prism_registry_builder_add_backend. The backend’s identifier is derived from it by the hash function described in the chapter on backend identifiers. The string is copied during loading.

priority

The backend’s priority. Higher values indicate higher priority. This value MUST be non-negative unless the loading call supplies a priority override, in which case it is ignored.

features

The feature set the backend declares, formed by ORing PRISM_BACKEND_* feature constants together. It MUST be consistent with the vtable in the sense required of any custom backend.

vtable

The vtable implementing the backend, subject to every requirement placed on a vtable by the chapter on custom backends. This member MUST NOT be NULL, and its size member MUST be set as that chapter requires. The vtable MUST provide a create member, as described under Host services. The vtable is copied during loading; the code it names remains valid while the library remains loaded.

userdata

An opaque pointer that Prism delivers to the backend’s create member as the userdata member of a PrismPluginInstanceContext (see PrismPluginInstanceContext). It has the meaning the userdata parameter of prism_registry_builder_add_backend would have, and permits several descriptors that share one vtable to be distinguished at instance creation. This member MAY be NULL. Prism does not free it.

plugin_version

An informational version identifying the plugin’s own release, distinct from the ABI generation and used for no compatibility decision. The value is three 16-bit components, major, minor, and patch, packed most-significant first, with the least-significant 16 bits reserved and set to zero. A plugin MAY set it to zero if it has no version to report.

PrismPluginServices

A structure through which a backend supplied by a plugin reaches the facilities Prism makes available to it. Prism provides one to each backend it registers from a plugin, as described under Host services.

Syntax

typedef struct PrismPluginServices {
  uint32_t struct_size;
  uint32_t reserved;
  void(PRISM_CALL *log)(const PrismPluginServices *self, PrismLogLevel level,
                        const char *message);
} PrismPluginServices;

Members

struct_size

The size of this structure as Prism understands it. A backend MUST consult at most this many bytes and MUST treat any member beyond it as absent. A later generation MAY append members after those defined here.

reserved

Reserved for future use. Prism sets this member to zero, and a backend MUST ignore it.

log

A function through which the backend records diagnostics. Its first argument MUST be the services object Prism supplied. Prism records the diagnostic under the name of the backend to which the services object belongs; the backend does not supply a source. This member is never NULL.

PrismPluginInstanceContext

The structure Prism passes as the userdata argument to the create member of a backend supplied by a plugin. The context is valid only for the duration of the create call; the services object it names is valid for the longer period given under Host services.

Syntax

typedef struct PrismPluginInstanceContext {
  uint32_t struct_size;
  uint32_t reserved;
  const PrismPluginServices *services;
  void *userdata;
} PrismPluginInstanceContext;

Members

struct_size

The size of this structure as Prism understands it. A backend MUST consult at most this many bytes and MUST treat any member beyond it as absent.

reserved

Reserved for future use. Prism sets this member to zero, and a backend MUST ignore it.

services

The backend’s services object. A backend MAY retain it for the period given under Host services. This member is never NULL.

userdata

The value of the userdata member of the descriptor from which the backend was registered.

prism_plugin_query

The function a plugin exports, through which Prism enumerates the plugin’s backends. Every plugin MUST export this function.

Syntax

const PrismPluginBackend *prism_plugin_query(const PrismPluginHost *host,
                                             size_t index);

Parameters

host

A pointer to a host descriptor Prism supplies. This pointer is never NULL and is valid only for the duration of the call.

index

The zero-based index of the descriptor Prism requests. Prism begins at zero and increments by one on each successive call within a single load.

Return Value

Returns a pointer to the backend descriptor at index when one exists. Returns NULL when index is past the last descriptor the plugin supplies, which terminates enumeration.

Remarks

The returned descriptor MUST remain valid until the entry point is next called or loading returns, whichever comes first. A plugin typically returns a pointer to a static descriptor, which satisfies this trivially. The vtable a descriptor names, and any object its userdata designates, are subject to the longer lifetimes described under Library lifetime.

A plugin MUST return descriptors for a contiguous range of indices beginning at zero. If the entry point returns NULL for one index and a non-null descriptor for a greater index, the enumeration SHALL be incomplete.

A plugin MUST export this function with C language linkage, the PRISM_CALL calling convention, and the external visibility the platform requires of a dynamically resolved symbol. The entry point is subject to the requirements given under Threads and apartments.

prism_registry_builder_add_library

Loads a plugin from a shared library and adds each backend it supplies to a registry builder.

Syntax

PrismError prism_registry_builder_add_library(PrismRegistryBuilder *builder,
                                              const char *path,
                                              int priority_override,
                                              size_t *out_count);

Parameters

builder

The builder to add the plugin’s backends to. This parameter MUST NOT be NULL.

path

The filesystem path to the shared library, as a null-terminated UTF-8 string. This parameter MUST NOT be NULL. On platforms whose native path encoding is not UTF-8, Prism converts the path as the platform requires. The string is not retained after the call.

priority_override

A priority to assign to every backend the plugin supplies, overriding the priority each declares. A non-negative value is applied to all of the plugin’s backends. A negative value directs Prism to honor the priority each descriptor declares, which then MUST itself be non-negative.

out_count

An optional pointer receiving the number of backends added. This parameter MAY be NULL. It is written only when the function returns PRISM_OK.

Return Value

ValueMeaning
PRISM_OKThe plugin was loaded and all of its backends were added to the builder.
PRISM_ERROR_INVALID_PARAMpriority_override was negative and a descriptor declared a negative priority; or a descriptor declared a feature set inconsistent with its vtable, a null vtable, a vtable whose size member was zero, or a vtable without a create member.
PRISM_ERROR_INVALID_UTF8path, or a descriptor’s name, contains invalid UTF-8.
PRISM_ERROR_LIBRARY_LOAD_FAILEDThe shared library could not be opened. Among the causes are that no file exists at path, that it is not a loadable image, that it was built for a different architecture, and that its initialization code failed.
PRISM_ERROR_LIBRARY_INVALIDThe library was opened but does not export prism_plugin_query.
PRISM_ERROR_INCOMPATIBLE_ABIThe plugin declined the host by returning NULL at index 0, or a descriptor declared an ABI generation this implementation does not accept.
PRISM_ERROR_INVALID_OPERATIONThe builder is spent, or a backend the plugin supplies has the same name or identifier as a backend already present in the builder, whether compiled-in, previously registered, or supplied by an earlier index of the same plugin.
PRISM_ERROR_INTERNALAn internal error occurred while trying to perform a loader operation.
PRISM_ERROR_UNKNOWNAn unspecified error occurred, and Prism has no other classification for it.

Remarks

Loading is atomic. On any error, no backend from the plugin is added, and the library is unloaded before the function returns. On success, one backend is added for each descriptor the plugin supplied, in the order the plugin supplied them, and out_count, if provided, receives their number.

The library is opened once and shared among every backend loaded from it. Prism unloads it when the last registration derived from the plugin becomes unreferenced.

The identifiers Prism assigns to the loaded backends are those their names hash to. An application that knows a plugin’s backend names can resolve their identifiers from the frozen registry with prism_registry_id; an application that does not can enumerate them with prism_registry_count and prism_registry_id_at.

Opening a shared library executes that library’s initialization code before Prism reads any descriptor or performs any validation, as described under Security and deployment.

Example

Part 1: shared library

The following source, compiled as a shared library, is a minimal plugin that supplies a single backend. The backend takes its services object from the instance context in create, records a diagnostic through it, and writes speech requests to standard output.

#include <prism.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>

#if defined(_WIN32)
#define DEMO_EXPORT __declspec(dllexport)
#elif defined(__GNUC__) || defined(__clang__)
#define DEMO_EXPORT __attribute__((visibility("default")))
#else
#define DEMO_EXPORT
#endif

typedef struct {
    const PrismPluginServices *services;
} demo_instance;

static void *PRISM_CALL demo_create(void *userdata) {
    const PrismPluginInstanceContext *ctx = userdata;
    demo_instance *self = calloc(1, sizeof *self);
    if (!self) {
        return NULL;
    }
    self->services = ctx->services;
    self->services->log(self->services, PRISM_LOG_LEVEL_INFO, "instance created");
    return self;
}

static void PRISM_CALL demo_destroy(void *instance) {
    free(instance);
}

static PrismError PRISM_CALL demo_speak(void *instance, const char *text,
                                        bool interrupt) {
    demo_instance *self = instance;
    (void)interrupt;
    self->services->log(self->services, PRISM_LOG_LEVEL_TRACE, text);
    printf("speak: %s\n", text);
    return PRISM_OK;
}

static PrismError PRISM_CALL demo_stop(void *instance) {
    (void)instance;
    return PRISM_OK;
}

static const PrismBackendVTable demo_vtable = {
    .size = sizeof(PrismBackendVTable),
    .create = demo_create,
    .destroy = demo_destroy,
    .speak = demo_speak,
    .stop = demo_stop,
};

static const PrismPluginBackend demo_backend = {
    .abi_version = PRISM_PLUGIN_ABI_VERSION,
    .struct_size = sizeof(PrismPluginBackend),
    .reserved = 0,
    .name = "Demo",
    .priority = 10,
    .features = PRISM_BACKEND_SUPPORTS_SPEAK | PRISM_BACKEND_SUPPORTS_STOP,
    .vtable = &demo_vtable,
    .userdata = NULL,
    .plugin_version = 0,
};

DEMO_EXPORT const PrismPluginBackend *prism_plugin_query(
    const PrismPluginHost *host, size_t index) {
    (void)host;
    return index == 0 ? &demo_backend : NULL;
}
Part 2: loading the shared library at run-time

The following loads that plugin and freezes a registry containing it alongside the compiled-in backends.

PrismRegistryBuilder *builder = prism_registry_builder_new();
if (!builder) {
    return 1;
}
size_t count = 0;
PrismError err =
    prism_registry_builder_add_library(builder, "./demo_plugin.so", -1, &count);
if (err != PRISM_OK) {
    fprintf(stderr, "Failed to load plugin: %s\n", prism_error_string(err));
    prism_registry_builder_free(builder);
    return 1;
}
PrismRegistry *registry = prism_registry_freeze(builder);
prism_registry_builder_free(builder);
if (!registry) {
    return 1;
}
/* Bind the registry to a context as shown in the chapter on custom backends. */

Voice Parameters

Voice Parameter Functions

Prism normalizes voice parameters (volume, rate, and pitch) to a floating-point range of 0.0 to 1.0 inclusive. This normalization allows applications to work with consistent ranges regardless of the underlying backend’s native parameter ranges.

The value 0.5 represents the default or neutral setting for rate and pitch. For volume, 0.5 represents the system default volume level, though the perceptual “middle” volume may vary by backend.

Parameter changes take effect on subsequent speech. Changing parameters while speech is in progress may or may not affect the current speech, depending on the backend.

prism_backend_set_volume

Sets the speech volume.

Syntax

PrismError prism_backend_set_volume(PrismBackend *backend, float volume);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

volume

The volume level, in the range [0.0, 1.0]. A value of 0.0 is silent; 1.0 is maximum volume.

Return Value

ValueMeaning
PRISM_OKVolume was set.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_RANGE_OUT_OF_BOUNDSvolume is outside [0.0, 1.0].
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support volume control.
PRISM_ERROR_INTERNALAn internal error occurred.

Remarks

This function sets the volume level for speech synthesis. The volume affects how loud the synthesized speech will be.

The volume is independent of system volume settings. Setting the Prism volume to 1.0 does not override or affect the system volume or the volume of other applications.

Setting volume to 0.0 produces silent output. The backend still synthesizes speech, but no sound is heard.

The volume persists until changed. Once set, it applies to all subsequent speech until a new volume is set or the backend is freed.

Screen reader backends do not support volume control.

The parameter is validated before being passed to the backend. If volume is negative, greater than 1.0, NaN, or infinity, the function returns PRISM_ERROR_RANGE_OUT_OF_BOUNDS.

prism_backend_get_volume

Retrieves the current speech volume.

Syntax

PrismError prism_backend_get_volume(PrismBackend *backend, float *out_volume);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

out_volume

Pointer to receive the volume. This parameter MUST NOT be NULL. On success, receives a value in the range [0.0, 1.0].

Return Value

ValueMeaning
PRISM_OKVolume was retrieved.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support volume control.

Remarks

This function retrieves the current volume setting. The returned value is in the same normalized range [0.0, 1.0] used by prism_backend_set_volume.

If prism_backend_set_volume has not been called, the returned value represents the backend’s default volume.

The value is retrieved from the backend’s internal state, not from any system setting. It reflects the most recent value set by prism_backend_set_volume, or the default if no value has been set.

prism_backend_set_rate

Sets the speech rate (speed).

Syntax

PrismError prism_backend_set_rate(PrismBackend *backend, float rate);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

rate

The speech rate, in the range [0.0, 1.0]. A value of 0.0 is the slowest rate; 1.0 is the fastest; 0.5 is the default.

Return Value

ValueMeaning
PRISM_OKRate was set.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_RANGE_OUT_OF_BOUNDSrate is outside [0.0, 1.0].
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support rate control.
PRISM_ERROR_INTERNALAn internal error occurred.

Remarks

This function sets the speech rate, which controls how fast the synthesized speech is spoken. Lower values produce slower speech; higher values produce faster speech.

The rate affects the tempo of speech but not the pitch. At extreme rates, speech may become difficult to understand.

The normalized range [0.0, 1.0] is mapped to each backend’s native rate range using a piecewise linear transformation that preserves the midpoint. This ensures that 0.5 always corresponds to the backend’s default rate, regardless of how the native range is defined.

The rate persists until changed and applies to all subsequent speech.

Screen reader backends do not support this function.

prism_backend_get_rate

Retrieves the current speech rate.

Syntax

PrismError prism_backend_get_rate(PrismBackend *backend, float *out_rate);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

out_rate

Pointer to receive the rate. This parameter MUST NOT be NULL. On success, receives a value in the range [0.0, 1.0].

Return Value

ValueMeaning
PRISM_OKRate was retrieved.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support rate control.

Remarks

This function retrieves the current rate setting. The returned value is in the same normalized range [0.0, 1.0] used by prism_backend_set_rate.

If prism_backend_set_rate has not been called, the returned value is the default rate for the selected backend.

Screen reader backends do not support this function.

prism_backend_set_pitch

Sets the speech pitch.

Syntax

PrismError prism_backend_set_pitch(PrismBackend *backend, float pitch);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

pitch

The pitch, in the range [0.0, 1.0].

Return Value

ValueMeaning
PRISM_OKPitch was set.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_RANGE_OUT_OF_BOUNDSpitch is outside [0.0, 1.0].
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support pitch control.
PRISM_ERROR_INTERNALAn internal error occurred.

Remarks

This function sets the speech pitch, which controls the fundamental frequency of the synthesized voice. Lower values produce a deeper voice; higher values produce a higher voice.

The pitch affects the perceived voice quality without changing the rate. Extreme pitch values may produce unnatural-sounding speech.

Not all backends support pitch control. Some TTS engines apply pitch at the voice level and do not allow runtime adjustment. SAPI supports pitch control; AVSpeech supports pitch control within a limited range; screen reader backends do not support pitch control.

The pitch persists until changed and applies to all subsequent speech.

prism_backend_get_pitch

Retrieves the current speech pitch.

Syntax

PrismError prism_backend_get_pitch(PrismBackend *backend, float *out_pitch);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

out_pitch

Pointer to receive the pitch. This parameter MUST NOT be NULL. On success, receives a value in the range [0.0, 1.0].

Return Value

ValueMeaning
PRISM_OKPitch was retrieved.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support pitch control.

Remarks

This function retrieves the current pitch setting. The returned value is in the same normalized range [0.0, 1.0] used by prism_backend_set_pitch.

If prism_backend_set_pitch has not been called, the returned value is the default pitch for the selected backend.

Screen reader backends do not support this function.

Voice Selection

Voice Selection Functions

Backends that support multiple voices provide functions to enumerate and select voices. Voices are identified by zero-based indices that are valid only for the current voice list; calling prism_backend_refresh_voices may invalidate previously obtained indices.

Screen reader backends do not support these functions.

prism_backend_refresh_voices

Refreshes the backend’s internal voice list.

Syntax

PrismError prism_backend_refresh_voices(PrismBackend *backend);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

Return Value

ValueMeaning
PRISM_OKVoice list was refreshed.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support voice enumeration.
PRISM_ERROR_INTERNALAn internal error occurred.

Remarks

This function queries the system for the current list of available voices and updates the backend’s internal voice list. Applications SHOULD call this function if voices may have been added to or removed from the system since the backend was initialized.

On most systems, voices are installed through system settings or third-party software. If a user installs a new voice while an application is running, that voice MAY NOT appear in the backend’s voice list until prism_backend_refresh_voices is called, although this behavior is backend-dependent.

After calling this function, previously obtained voice indices may no longer be valid. A voice that was at index 3 before the refresh may be at a different index (or may no longer exist) after the refresh. Applications that store voice preferences SHOULD store the voice name rather than the index, and look up the index after refreshing.

Some backends perform voice enumeration during initialization and do not support refreshing. These backends return PRISM_OK but may not actually update their voice list.

prism_backend_count_voices

Returns the number of voices available from the backend.

Syntax

PrismError prism_backend_count_voices(PrismBackend *backend, size_t *out_count);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

out_count

Pointer to receive the voice count. This parameter MUST NOT be NULL.

Return Value

ValueMeaning
PRISM_OKVoice count was retrieved.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support voice enumeration.

Remarks

This function returns the number of voices currently available from the backend. Voice indices range from 0 to count-1 inclusive.

A return value of 0 indicates that no voices are available. This may occur if no TTS voices are installed on the system, or if the backend does not enumerate voices.

The count reflects the state of the voice list at the time of the call. If voices are added or removed from the system, the count may change after a call to prism_backend_refresh_voices.

prism_backend_get_voice_name

Retrieves the human-readable name of a voice.

Syntax

PrismError prism_backend_get_voice_name(
    PrismBackend *backend,
    size_t voice_id,
    const char **out_name
);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

voice_id

The zero-based index of the voice.

out_name

Pointer to receive the voice name. This parameter MUST NOT be NULL. On success, receives a pointer to a null-terminated string.

Return Value

ValueMeaning
PRISM_OKVoice name was retrieved.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_RANGE_OUT_OF_BOUNDSvoice_id is out of range.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support voice enumeration.

Remarks

This function retrieves the human-readable name of a voice, such as “Microsoft David” or “Samantha”.

The returned string is owned by the backend and remains valid only until the next call to prism_backend_get_voice_name or prism_backend_get_voice_language on the same backend instance. Applications that need to retain the name MUST copy it.

Voice names are determined by the TTS engine and are typically localized. The same voice may have different names on different language versions of the operating system.

prism_backend_get_voice_language

Retrieves the language code or language string of a voice.

Syntax

PrismError prism_backend_get_voice_language(
    PrismBackend *backend,
    size_t voice_id,
    const char **out_language
);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

voice_id

The zero-based index of the voice.

out_language

Pointer to receive the language code or language string. This parameter MUST NOT be NULL. On success, receives a pointer to a null-terminated string.

Return Value

ValueMeaning
PRISM_OKLanguage was retrieved.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_RANGE_OUT_OF_BOUNDSvoice_id is out of range.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support voice enumeration.

Remarks

This function retrieves the language code or language string of a voice. The format is typically a BCP 47 language tag such as “en-US”, “de-DE”, or “ja-JP”, but the exact format depends on the backend and the underlying TTS engine.

The returned string follows the same lifetime rules as prism_backend_get_voice_name: it is valid only until the next call to either function on the same backend.

prism_backend_set_voice

Selects a voice to use for subsequent speech synthesis.

Syntax

PrismError prism_backend_set_voice(PrismBackend *backend, size_t voice_id);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

voice_id

The zero-based index of the voice to select.

Return Value

ValueMeaning
PRISM_OKVoice was selected.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_RANGE_OUT_OF_BOUNDSvoice_id is out of range.
PRISM_ERROR_VOICE_NOT_FOUNDThe voice does not exist.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support voice selection.
PRISM_ERROR_BACKEND_ENTERED_UNDEFINED_STATEAn error occured when the backend was executing a function which has caused the backend to enter an undefined state. The caller should re-initialize the backend from scratch.

Remarks

This function selects a voice to use for subsequent speech synthesis. The voice persists until changed and applies to all subsequent speech.

If speech is currently playing when the voice is changed, the change may or may not affect the current speech depending on the backend. Most backends apply the new voice to subsequent speech operations only.

If voice_id is out of range (greater than or equal to the count returned by prism_backend_count_voices), the function returns PRISM_ERROR_RANGE_OUT_OF_BOUNDS or PRISM_ERROR_VOICE_NOT_FOUND. The distinction between these errors is not significant; both indicate that the voice does not exist.

Changing the voice may affect the audio format returned by prism_backend_get_channels, prism_backend_get_sample_rate, and prism_backend_get_bit_depth. Applications using prism_backend_speak_to_memory SHOULD query the audio format after changing the voice.

prism_backend_get_voice

Retrieves the index of the currently selected voice.

Syntax

PrismError prism_backend_get_voice(PrismBackend *backend, size_t *out_voice_id);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

out_voice_id

Pointer to receive the voice index. This parameter MUST NOT be NULL.

Return Value

ValueMeaning
PRISM_OKVoice index was retrieved.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support voice selection.
PRISM_ERROR_INTERNALAn internal error occurred.

Remarks

This function retrieves the index of the currently selected voice. If prism_backend_set_voice has not been called, the returned value is the index of the backend’s default voice.

The returned index can be used with prism_backend_get_voice_name and prism_backend_get_voice_language to retrieve information about the current voice.

If the voice list has been refreshed since the voice was selected, the returned index reflects the voice’s position in the new list, which may differ from its position in the old list. If the selected voice no longer exists after a refresh, the backend’s behavior is undefined.

Audio Format

Audio Format Functions

For backends that support prism_backend_speak_to_memory, these functions provide information about the audio format of the generated samples.

prism_backend_get_channels

Retrieves the number of audio channels produced by the backend.

Syntax

PrismError prism_backend_get_channels(PrismBackend *backend, size_t *out_channels);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

out_channels

Pointer to receive the channel count. This parameter MUST NOT be NULL.

Return Value

ValueMeaning
PRISM_OKChannel count was retrieved.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support audio format queries.

Remarks

This function returns the number of audio channels in the audio produced by prism_backend_speak_to_memory. Common values are 1 (mono) and 2 (stereo).

The channel count depends on the backend and the selected voice. Most TTS engines produce mono output, but some voices may produce stereo.

prism_backend_get_sample_rate

Retrieves the sample rate of audio produced by the backend.

Syntax

PrismError prism_backend_get_sample_rate(PrismBackend *backend, size_t *out_sample_rate);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

out_sample_rate

Pointer to receive the sample rate in Hz. This parameter MUST NOT be NULL.

Return Value

ValueMeaning
PRISM_OKSample rate was retrieved.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support audio format queries.

Remarks

This function returns the sample rate of the audio produced by prism_backend_speak_to_memory, measured in samples per second (Hz). Common values include 16000, 22050, 44100, and 48000.

The sample rate depends on the backend and the selected voice. Different voices may produce audio at different sample rates.

Applications that process the audio data (for example, to mix it with other audio or to resample it) need to know the sample rate to perform correct processing.

prism_backend_get_bit_depth

Retrieves the native bit depth of audio produced by the backend.

Syntax

PrismError prism_backend_get_bit_depth(PrismBackend *backend, size_t *out_bit_depth);

Parameters

backend

The backend instance. This parameter MUST NOT be NULL.

out_bit_depth

Pointer to receive the bit depth. This parameter MUST NOT be NULL.

Return Value

ValueMeaning
PRISM_OKBit depth was retrieved.
PRISM_ERROR_NOT_INITIALIZEDThe backend has not been initialized.
PRISM_ERROR_NOT_IMPLEMENTEDThe backend does not support audio format queries.

Remarks

This function returns the native bit depth of the audio produced by the backend. Common values are 8 and 16.

This value represents the backend’s native format, not the format delivered to the callback. Samples delivered to PrismAudioCallback are always 32-bit floating-point values in the range [-1.0, 1.0], regardless of the native bit depth.

The bit depth is informational. Applications typically do not need this value unless they are converting the audio to a specific format for storage or transmission.

Utilities

Utility Functions

prism_error_string

Returns a human-readable description of an error code.

Syntax

const char *prism_error_string(PrismError error);

Parameters

error

The error code to describe.

Return Value

Returns a pointer to a null-terminated string describing the error. Never returns NULL.

Remarks

This function converts a PrismError code to a human-readable string suitable for display to users or inclusion in log messages.

The returned string is statically allocated and remains valid for the lifetime of the process. Applications MUST NOT modify or free the returned string.

If error is not a valid error code (for example, if it is greater than or equal to PRISM_ERROR_COUNT), the function returns the string “Unknown error”.

This function MAY be called regardless of the state of Prism.

Example

PrismError err = prism_backend_speak(backend, "Hello", true);
if (err != PRISM_OK) {
    fprintf(stderr, "Error: %s\n", prism_error_string(err));
}

prism_version

Returns the version of the loaded Prism library as an encoded integer.

Syntax

uint32_t prism_version(void);

Parameters

This function has no parameters.

Return Value

Returns the version of the loaded Prism library as an unsigned 32-bit integer. This function always returns a valid version value and does not use a sentinel value to indicate failure.

Remarks

This function reports the release version of the Prism library that is actually linked into or loaded by the calling process. The returned value describes the library binary, not the version of the Prism headers with which the application was compiled. This distinction is particularly important when Prism is used as a shared library, since the version of Prism installed on the user’s system MAY differ from the version against which the application was originally built.

Prism follows Semantic Versioning. A Prism release version therefore consists of major, minor, and patch components. Once Prism has reached a stable major version, an increment to the major component indicates an incompatible change to the public API, an increment to the minor component indicates the addition of functionality in a backward-compatible manner, and an increment to the patch component indicates a backward-compatible correction. While the major version is zero, Prism is considered to be under initial development in accordance with Semantic Versioning, and compatibility between minor releases MUST NOT be assumed merely because the major version remains zero.

The value returned by prism_version encodes the major, minor, and patch components of the Prism release version into the low 24 bits of the returned integer. The major version occupies bits 16 through 23, the minor version occupies bits 8 through 15, and the patch version occupies bits 0 through 7. Bits 24 through 31 are reserved and are currently zero. The encoded representation is equivalent to:

(major << 16) | (minor << 8) | patch

For example, Prism version 1.4.7 is represented by the value 0x00010407.

The encoded representation also permits the core major, minor, and patch version to be compared using an ordinary unsigned integer comparison. Because the major component occupies the most significant portion of the representation, followed by the minor and patch components, the numeric ordering of encoded values is the same as the Semantic Versioning ordering of their corresponding major, minor, and patch components. An application that requires at least Prism version 1.4.0, for example, MAY compare the result of prism_version against the value representing 1.4.0.

The version is determined when Prism is built and remains constant for the lifetime of the loaded library. Repeated calls therefore return the same value.

This function does not require Prism to be initialized.

prism_version_string

Returns the version of the loaded Prism library as a human-readable string.

Syntax

const char *prism_version_string(void);

Parameters

This function has no parameters.

Return Value

Returns a pointer to a null-terminated string containing the release version of the loaded Prism library. This function never returns NULL.

Remarks

This function provides the human-readable representation of the Prism release version reported numerically by prism_version. The returned string identifies the Prism library that is actually linked into or loaded by the current process. It therefore reflects the version of the library binary at runtime rather than necessarily reflecting the version of the Prism headers that were used when the calling application was compiled.

The returned string is owned by Prism and is statically allocated. It remains valid for the lifetime of the loaded library. Applications MUST NOT modify the contents of the returned string and MUST NOT attempt to free or otherwise deallocate the returned pointer. An application that requires a modifiable copy of the version string MUST copy it into storage owned by the application.

Audio Callback

Audio Callback

The PrismAudioCallback type defines the signature for audio data callbacks used with prism_backend_speak_to_memory.

Syntax

typedef void (*PrismAudioCallback)(
    void *userdata,
    const float *samples,
    size_t sample_count,
    size_t channels,
    size_t sample_rate
);

Parameters

userdata

The user-defined pointer that was passed to prism_backend_speak_to_memory.

samples

Pointer to an array of interleaved audio samples. Samples are normalized 32-bit floating-point values in the range [-1.0, 1.0]. For multi-channel audio, samples are interleaved: for stereo, samples alternate left-right-left-right (L0, R0, L1, R1, …).

sample_count

The total number of samples in the array. For multi-channel audio, this is the number of frames multiplied by the number of channels. To compute the number of frames, divide by channels.

channels

The number of audio channels. Typically 1 (mono) or 2 (stereo).

sample_rate

The sample rate in Hz. This is the number of samples per second per channel.

Remarks

The callback function receives synthesized audio data from prism_backend_speak_to_memory. The callback may be invoked once with all data, or multiple times with successive chunks of data.

The callback may be invoked from a different thread than the one that called prism_backend_speak_to_memory. Callback implementations MUST be thread-safe if they access any shared state.

The samples pointer is valid only for the duration of the callback invocation. If the callback needs to retain the audio data, it MUST copy the data to its own buffer before returning.

The callback MUST NOT call any Prism function on the backend that initiated the synthesis. Doing so may cause deadlocks or undefined behavior.

The callback MUST NOT throw exceptions (in C++) or call longjmp. Abnormal return from the callback results in undefined behavior.

Example

typedef struct {
    float *buffer;
    size_t capacity;
    size_t size;
} AudioBuffer;

void accumulate_audio(void *userdata, const float *samples,
                      size_t sample_count, size_t channels, size_t sample_rate) {
    AudioBuffer *buf = (AudioBuffer *)userdata;    
    if (buf->size + sample_count > buf->capacity) {
        buf->capacity = (buf->size + sample_count) * 2;
        buf->buffer = realloc(buf->buffer, buf->capacity * sizeof(float));
    }
    memcpy(buf->buffer + buf->size, samples, sample_count * sizeof(float));
    buf->size += sample_count;
}

Logging

Logging

Prism emits diagnostic messages through an internal logging subsystem. By default these messages are discarded; an application that wishes to observe them installs a log handler, and the library then delivers messages to that handler. The subsystem is designed so that logging never blocks the calling thread and never interferes with speech synthesis: messages are handed to a background thread and delivered to the handler from there.

Logging is a process-wide facility rather than a per-context one. There is a single logger shared by the entire library, and the functions in this chapter operate on it directly. They do not take a PrismContext and MAY be called before prism_init or after prism_shutdown.

PrismLogLevel

An enumeration describing the severity of a log message and, when used as a threshold, the minimum severity an application wishes to receive.

Syntax

typedef enum PrismLogLevel {
  PRISM_LOG_LEVEL_TRACE,
  PRISM_LOG_LEVEL_DEBUG,
  PRISM_LOG_LEVEL_INFO,
  PRISM_LOG_LEVEL_WARN,
  PRISM_LOG_LEVEL_ERROR,
  PRISM_LOG_LEVEL_NONE
} PrismLogLevel;

Members

PRISM_LOG_LEVEL_TRACE

The most verbose level, used for fine-grained tracing of internal operations.

PRISM_LOG_LEVEL_DEBUG

Diagnostic information useful during development.

PRISM_LOG_LEVEL_INFO

Informational messages describing normal operation.

PRISM_LOG_LEVEL_WARN

Conditions that are not errors but MAY indicate a problem.

PRISM_LOG_LEVEL_ERROR

Error conditions.

PRISM_LOG_LEVEL_NONE

Not a message severity. When supplied to prism_set_log_level, it suppresses all messages, since no message has a severity greater than or equal to it.

Remarks

The levels are ordered from least to most severe, with PRISM_LOG_LEVEL_NONE ordered above all message severities. A message is delivered only when its level is greater than or equal to the current threshold, so a threshold of PRISM_LOG_LEVEL_WARN delivers warnings and errors while discarding trace, debug, and informational messages. The numeric values are part of the ABI and will not change; new levels, if any are ever added, would be inserted in a way that preserves this ordering.

PrismLogCallback

The type of a function invoked to deliver a single log message.

Syntax

typedef void(PRISM_CALL *PrismLogCallback)(void *userdata, PrismLogLevel level,
                                           const char *source,
                                           const char *message);

Parameters

userdata

The opaque pointer supplied in the PrismLogHandler that installed this callback. Prism does not interpret this value.

level

The severity of the message.

source

A null-terminated string naming the component that produced the message. This string is valid only for the duration of the call.

message

A null-terminated string containing the message text. This string is valid only for the duration of the call.

Remarks

The callback is invoked from Prism’s internal logging thread, not from the thread that produced the message. Callback implementations MUST be prepared for this and MUST provide their own synchronization if they touch shared state. Because delivery is serialized on a single thread, a given handler is never invoked concurrently with itself.

The source and message pointers are owned by Prism and are valid only for the duration of the call. A handler that needs to retain either string MUST copy it.

A handler SHOULD do as little work as possible and MUST NOT block for an extended period, since the logging thread cannot deliver further messages until the handler returns and messages produced in the meantime MAY be dropped. A handler MUST NOT call prism_log, prism_log_flush, or prism_log_shutdown; doing so results in undefined behavior.

PrismLogHandler

A structure pairing a log callback with an opaque user pointer.

Syntax

typedef struct PrismLogHandler {
  PrismLogCallback fn;
  void *userdata;
} PrismLogHandler;

Members

fn

The callback invoked to deliver messages, or NULL to install no handler. When fn is NULL, messages are discarded.

userdata

An opaque pointer passed unmodified to fn on each invocation. Prism does not interpret or take ownership of this value. The lifetime of this value is the lifetime of the handler function, and therefore this value must be valid for as long as the handler is alive.

prism_set_log_handler

Installs the handler that receives log messages, replacing any previously installed handler.

Syntax

PrismLogHandler prism_set_log_handler(PrismLogHandler handler);

Parameters

handler

The handler to install. A handler whose fn is NULL disables delivery.

Return Value

Returns the handler that was previously installed. If no handler was installed, the returned structure’s fn member is NULL.

Remarks

Installing a handler makes Prism’s diagnostic output visible to the application. Until a handler is installed, messages are discarded, except when the PRISM_LOG environment variable is set.

This function is thread-safe and MAY be called at any time, including before prism_init and concurrently with logging activity on other threads. The replacement takes effect for messages delivered after the call; a message already in flight MAY still be delivered to the previous handler. For this reason, an application MUST NOT assume that its previous handler has stopped being invoked the instant this function returns, and MUST keep any state the previous handler depends on valid until the application has otherwise ensured that no delivery is in progress.

The returned handler MAY be retained and reinstalled later to restore prior behavior.

prism_set_log_level

Sets the minimum severity of messages that will be delivered.

Syntax

PrismLogLevel prism_set_log_level(PrismLogLevel level);

Parameters

level

The minimum severity to deliver. Messages whose severity is below this level are discarded before they are queued. PRISM_LOG_LEVEL_NONE suppresses all messages.

Return Value

Returns the threshold that was in effect before the call.

Remarks

The threshold is applied on the thread that produces a message, before the message is queued, so raising the threshold immediately reduces the work performed for suppressed messages. This function is thread-safe and MAY be called at any time.

The threshold and the installed handler are independent.

prism_log

Emits a log message.

Syntax

void prism_log(PrismLogLevel level, const char *source, const char *message);

Parameters

level

The severity of the message.

source

A null-terminated string naming the component producing the message. This parameter MUST NOT be NULL.

message

A null-terminated string containing the message text. This parameter MUST NOT be NULL.

Return Value

This function does not return a value.

Remarks

This function is provided primarily for use by backend implementations and other components layered on top of Prism, so that their diagnostics are routed through the same handler as the library’s own. Applications MAY also call it.

If the message’s severity is below the current threshold, the function returns without queuing anything. Otherwise the message is copied and placed on an internal queue for delivery by the logging thread; the function does not invoke the handler directly and does not block waiting for delivery.

The queue has a fixed capacity. If it is full when a message is submitted, the message is dropped and an internal counter is incremented. The next time the logging thread delivers messages, it reports the number of dropped messages to the handler as a single PRISM_LOG_LEVEL_WARN message whose source is "prism". Applications that observe such a report are producing messages faster than the handler consumes them and SHOULD either raise the threshold or make the handler faster.

This function is thread-safe and MAY be called from any thread concurrently.

prism_log_flush

Blocks until all messages queued before the call have been delivered.

Syntax

void prism_log_flush(void);

Parameters

This function has no parameters.

Return Value

This function does not return a value.

Remarks

prism_log_flush returns only after every message successfully queued before the call has been processed by the logging thread. Processing includes completion of the handler call as long as a handler is installed. Messages dropped because the queue was full cannot be recovered by flushing. It is useful before installing a new handler, before shutting the process down, or at any point where the application needs to be certain that pending diagnostics have been delivered.

A flush requires no queue capacity and is never dropped, even when the message queue is otherwise full. A call concurrent with prism_log_shutdown waits for preceding queued messages to be processed; after shutdown has completed, it returns immediately. When no handler is installed, this function returns immediately. This function MUST NOT be called from within a log handler.

prism_log_shutdown

Stops the logging thread and releases the resources associated with the logger.

Syntax

void prism_log_shutdown(void);

Parameters

This function has no parameters.

Return Value

This function does not return a value.

Remarks

prism_log_shutdown drains any remaining messages, stops the internal logging thread, and joins it. It is not ordinarily necessary to call this function, since the logger is torn down automatically when the process exits. It is provided for applications that must guarantee the logging thread has terminated at a specific point, for example before unloading the library.

After this function returns, the logger is no longer running. Calls to prism_log after shutdown are accepted but their messages are not delivered, and prism_log_flush returns immediately. The function is thread-safe with respect to the other logging functions: an operation concurrent with shutdown either completes before the logging thread terminates or observes the stopped logger. This function MUST NOT be called from within a log handler.

Warning

There is no way to re-launch the logging thread after this function is called. Applications SHOULD take care that this function is only called when they are absolutely certain they will not need the logging thread for the remaining lifetime of the process.

Default behavior and the PRISM_LOG environment variable

When the library is first initialized by prism_init, it inspects the PRISM_LOG environment variable. If the variable is set to one of the values trace, debug, info, warn, error, or none, Prism installs a built-in handler that writes messages to the standard error stream and sets the threshold to the corresponding level. The values are matched exactly and in lower case; any other value, or an unset variable, leaves the default configuration unchanged.

In the absence of both an application-installed handler and the PRISM_LOG variable, no handler is installed and all messages are discarded. The default log level in this instance is None. Logging therefore has negligible cost by default.

Because the PRISM_LOG handler is installed during prism_init, an application that installs its own handler with prism_set_log_handler after initialization will replace the standard-error handler. An application that wishes to observe messages produced before its own handler is installed SHOULD either set PRISM_LOG or install its handler before the first call to prism_init. When PRISM_LOG is set to a recognized value, the first call to prism_init replaces a handler installed before it.

On console platforms, such as Xbox, Prism does not consult PRISM_LOG.

Backend-specific notes

Backend-Specific Notes

This chapter documents preconditions that go beyond the requirement that a backend’s underlying speech or accessibility service be running. Backends not listed here have no preconditions beyond that one. Membership in a registry indicates only that a backend was compiled into the library or added at context initialization time, not that its preconditions are met. Applications SHOULD inspect PRISM_BACKEND_IS_SUPPORTED_AT_RUNTIME via prism_backend_get_features before invoking prism_backend_initialize. The flag reflects only the state at the moment of the call; prism_backend_initialize MAY still return PRISM_ERROR_BACKEND_NOT_AVAILABLE after a positive probe. Backend-specific error codes returned by prism_backend_initialize are documented in the relevant section below.

Some backends are designated legacy. Legacy backends are compiled into the official Prism release packages by default. They are NOT compiled into ordinary source builds; a source build compiles them when the PRISM_ENABLE_LEGACY_BACKENDS build option is enabled. Each MAY then be excluded through its own backend option, such as PRISM_ENABLE_SYSTEM_ACCESS_BACKEND, which cannot enable a legacy backend without PRISM_ENABLE_LEGACY_BACKENDS.

Note: if an application wishes to be notified asynchronously of a backends availability as it changes, it should utilize the backend availability enumeration system described previously in this manual.

AVSpeech

All PRISM_BACKEND_AV_SPEECH operations MUST execute in a context that can dispatch work to the host process’s main thread. When the calling thread is the main thread, the backend executes work inline. When the calling thread is a worker thread, the backend dispatches work to the main queue and blocks until that work completes. Applications using a worker thread MUST therefore ensure that the main thread is running an AppKit, UIKit, or Foundation run loop, and MUST NOT block the main thread while a Prism call from a worker thread is in flight. Programs entered through NSApplicationMain, UIApplicationMain, or the SwiftUI @main attribute satisfy the run-loop requirement. A single-threaded program that issues all Prism calls from its entry-point thread also satisfies it. A headless multithreaded program that has no application object MUST run the main run loop itself.

On macOS 14, iOS 17, and corresponding or later releases of other Apple platforms, initialization requests Personal Voice authorization. The request is asynchronous and waits up to 120 seconds for the user’s response, during which prism_backend_initialize blocks. The outcome does not affect whether initialization succeeds: on denial or timeout the backend operates with system-installed voices, and Personal Voice voices are absent from the voice list. Consumers that wish to control the timing of the prompt SHOULD invoke requestPersonalVoiceAuthorization before initializing the backend.

On systems supporting in-memory rendering (macOS 10.15 or later, iOS 13 or later, and corresponding releases of other Apple platforms), prism_backend_speak_to_memory is bounded by an internal 5-minute timeout. If synthesis does not complete within that interval, Prism stops the memory synthesizer and returns PRISM_ERROR_INTERNAL; partial audio is not delivered to the callback.

VoiceOver

macOS

The host process MUST own at least one NSWindow. The backend selects a window from the application’s window list using the following preference order:

  • The application’s key window, if it is visible.
  • The application’s main window, if it is visible.
  • The first window in the application’s ordered window list that is visible, not miniaturized, and at NSNormalWindowLevel.
  • The first window in the application’s window list that has a content view.
  • The first window in the application’s window list, irrespective of state.

In the case of the final two selections listed above, the chosen window need not be visible. An offscreen NSWindow of any size suffices. An NSApplication instance MUST exist and the main run loop MUST be active. NSApplicationMain and the SwiftUI @main attribute on a SwiftUI.App declaration satisfy these requirements. A console-style program that does not bring up AppKit does not, regardless of whether it is wrapped in an application bundle.

The backend has two delivery mechanisms for announcements: a legacy automation path that sends Apple events to VoiceOver, and a standard accessibility-announcement path that posts an announcement notification on a window owned by the host process. The legacy path requires that the host process be packaged as an application bundle whose Info.plist declares a non-empty value for NSAppleEventsUsageDescription, and that the user has approved automation access to VoiceOver. The string supplied for NSAppleEventsUsageDescription is shown verbatim in the Transparency, Consent, and Control prompt.

During initialization, if the host process is eligible for the legacy path, the backend requests automation access in the background. A host process is eligible if it is not sandboxed and is either not notarized or holds the com.apple.security.automation.apple-events entitlement. Initialization neither waits for the user’s response nor fails because of it. A speech request uses the legacy path only when all of the following hold:

  • VoiceOver is running;
  • The most recent permission check, which runs in the background after each speech request and never prompts, found that automation access has been granted; and
  • The host process is eligible.

Otherwise, the speech request uses the accessibility-announcement path. Because the permission check runs in the background, a change in automation access takes effect from the speech request after the one that observes it, without reinitializing the backend.

Apple events are sent from an internal background queue, in the order in which they were requested, and never block the main thread. If VoiceOver does not reply within 2 seconds, or if the event fails, the text is delivered through the accessibility-announcement path instead. A speech request whose event was merely slow MAY therefore be heard twice. After a reply times out, the backend does not use the legacy path for 30 seconds, and speech requests in that interval use the accessibility-announcement path.

The standard macOS accessibility-announcement API does not provide Prism with a completion notification or a way to cancel an announcement after it has been posted. Because the backend may fall back to that path on any speech request, the macOS backend does not advertise PRISM_BACKEND_SUPPORTS_IS_SPEAKING or PRISM_BACKEND_SUPPORTS_STOP; calls to the corresponding operations return PRISM_ERROR_NOT_IMPLEMENTED.

iOS, iPadOS, MacCatalyst, tvOS, and visionOS

The host process MUST be a fully initialized application of the platform’s standard application kind, with an active main run loop. Any process that uses UIApplicationMain or the SwiftUI @main entry point satisfies this requirement.

WatchOS

On watchOS, the VoiceOver backend requires watchOS 10.0 or later and is available only while VoiceOver is running. Initialization returns PRISM_ERROR_BACKEND_NOT_AVAILABLE if either requirement is not satisfied.

The watchOS VoiceOver backend supports the speak and output operations. No other operations are supported at this time.

UIA

The UIA backend delivers announcements through Microsoft UI Automation notification events. Current versions of NVDA, JAWS, and Windows Narrator all observe these notifications.

A notification event produces speech only when an assistive technology consumes it. The backend therefore reports PRISM_BACKEND_IS_SUPPORTED_AT_RUNTIME only when both of the following hold at the moment of the query:

  • The system screen reader flag, as retrieved through SystemParametersInfo with SPI_GETSCREENREADER, is set.
  • UiaClientsAreListening returns TRUE, indicating that at least one UI Automation client is listening for events.

If UiaClientsAreListening cannot be resolved from uiautomationcore.dll at runtime, the bit is never reported. Neither check activates a COM object, and neither requires COM to be initialized on the calling thread.

The bit remains advisory, as for every other backend. Neither condition establishes that the listening client consumes notification events, or that the software which set the system screen reader flag is still running; the bit MAY therefore be reported when no announcement will be heard. Conversely, an assistive technology that consumes notification events without setting the system screen reader flag is not detected.

In order for UIA to function, the host process MUST own at least one top-level window that, at the moment of initialization, satisfies all of the following:

  • IsWindow returns TRUE.
  • IsWindowVisible returns TRUE.
  • IsIconic returns FALSE.
  • The window is owned by the host process.
  • The window is not styled with the extended styles WS_EX_TOOLWINDOW or WS_EX_TOPMOST.
  • The window is not a console host window, including the window kind used by modern terminal hosts such as Windows Terminal.

Among the windows that satisfy the conditions above, the backend selects the first available of:

  • The foreground window, if it belongs to the host process;
  • The host process’s active window; and
  • The first qualifying window in the host process’s window order.

The root owner of the selected window MUST also belong to the host process; if it does not, prism_backend_initialize returns PRISM_ERROR_INVALID_PARAM.

The host process MUST also be running on an interactive desktop reachable through GetThreadDesktop. Services running in non-interactive sessions cannot use this backend even if they synthesize their own window. If no qualifying window can be found, prism_backend_initialize returns PRISM_ERROR_INVALID_PARAM. If a qualifying window is found but the backend’s internal worker fails to initialize within a 5-second deadline, prism_backend_initialize returns PRISM_ERROR_INTERNAL.

The backend captures the qualifying window at initialization and does not rebind to a different window thereafter. If the captured window is destroyed during the lifetime of the backend instance, subsequent operations return PRISM_ERROR_NOT_INITIALIZED, and applications SHOULD destroy and re-create the backend.

NVDA

The NVDA backend communicates with NVDA through a local RPC endpoint whose name is keyed by the user’s logon session identifier and the calling thread’s desktop name. NVDA MUST therefore be running in the same logon session and on the same desktop as the calling thread. Cross-session and cross-desktop announcements are not supported.

The endpoint binding established at initialization is fixed for the lifetime of the backend instance and is never retried. If NVDA terminates after a successful initialization, subsequent operations return PRISM_ERROR_INTERNAL. The backend does not automatically reconnect when NVDA is restarted; applications SHOULD destroy and re-create the backend.

JAWS

The JAWS backend communicates with JAWS through a COM interface registered by the JAWS installer. The host process MUST be running on Windows, and the COM interface MUST be registered on the system; if it is not, prism_backend_initialize returns PRISM_ERROR_BACKEND_NOT_AVAILABLE.

ZoomText

The ZoomText backend communicates with ZoomText through a COM interface registered by the ZoomText installer. The host process MUST be running on Windows, and the COM interface MUST be registered on the system; if it is not, prism_backend_initialize returns PRISM_ERROR_BACKEND_NOT_AVAILABLE.

SenseReader

The Sense Reader backend communicates with Sense Reader through a COM interface registered by the Sense Reader installer. The host process MUST be running on Windows, and the COM interface MUST be registered on the system; if it is not, prism_backend_initialize returns PRISM_ERROR_BACKEND_NOT_AVAILABLE.

PCTalker

The PC-Talker backend communicates with PC-Talker through a client library distributed with PC-Talker. The host process MUST be running on Windows, and the library MUST be resolvable through the host process’s library search order; if it is not, prism_backend_initialize returns PRISM_ERROR_BACKEND_NOT_AVAILABLE.

ZDSR

The ZDSR backend communicates with ZDSR through a client library distributed with ZDSR. The host process MUST be running on Windows, and the library MUST be resolvable through the host process’s library search order; if it is not, prism_backend_initialize returns PRISM_ERROR_BACKEND_NOT_AVAILABLE. The runtime-supported probe reflects only the presence of a running ZDSR process and does not load the library.

BoyPCReader

The Boy PC Reader backend communicates with Boy PC Reader through a client library distributed with Boy PC Reader. The host process MUST be running on Windows, and the library MUST be resolvable through the host process’s library search order; if it is not, prism_backend_initialize returns PRISM_ERROR_BACKEND_NOT_AVAILABLE. As with the ZDSR backend, the runtime-supported probe reflects only the presence of a running Boy PC Reader process and does not load the library.

SystemAccess

The System Access backend is a legacy backend. No other requirements are imposed.

WindowEyes

The Window Eyes backend is a legacy backend. The backend communicates with WindowEyes through a COM interface registered by the WindowEyes installer. The host process MUST be running on Windows, and the COM interface MUST be registered on the system; if it is not, prism_backend_initialize returns PRISM_ERROR_BACKEND_NOT_AVAILABLE.

SAPI

The SAPI backend uses SAPI 5 for synthesis only. Synthesized audio is played through XAudio2; SAPI’s own audio output is not used. The host process MUST be running on Windows, and the SAPI voice COM class MUST be registered; if it is not, prism_backend_initialize returns PRISM_ERROR_BACKEND_NOT_AVAILABLE. An audio output device that XAudio2 can open MUST be present when the backend is initialized; if none is present, prism_backend_initialize returns PRISM_ERROR_BACKEND_NOT_AVAILABLE.

The backend creates and exclusively uses its SAPI voice on an internal worker thread in the multithreaded apartment. Accordingly, the thread from which the backend is called MAY belong to any apartment. prism_backend_speak_to_memory, prism_backend_set_voice, and prism_backend_refresh_voices are executed on the worker thread and return when the worker has completed them. No other function waits for synthesis.

prism_backend_speak returns once the text has been queued. Each utterance is synthesized in its entirety before playback of it begins. From the return of prism_backend_speak until playback of the utterance ends, prism_backend_is_speaking reports true, unless speech is paused or stopped in the interim. Synthesis of an utterance that is stopped before its synthesis completes is abandoned. An utterance that cannot be synthesized is discarded, and the failure is reported through the logging facility.

The voice list comprises the voices registered under the SAPI voice token category and, where present, the voices of the Microsoft Speech Platform 11 runtime. If neither category contains a voice, prism_backend_initialize returns PRISM_ERROR_NO_VOICES. Each voice is synthesized in the audio format that SAPI reports for it. Where an implementation of SAPI reports no format, 16-bit mono PCM at 22050 Hz is used.

The rate, pitch, and voice in effect when prism_backend_speak is called apply to that utterance; subsequent changes affect only utterances queued thereafter. Pitch is applied through SAPI XML markup. Text passed to prism_backend_speak is escaped and SHALL NOT be interpreted as markup. Volume is applied during playback, and a change of volume therefore also affects the utterance being played.

When the default audio output device changes, playback moves to the new device at the next utterance that begins while no speech is playing. If the device in use is removed, speech in progress is discarded, and subsequent utterances are played on the current default device.

OneCore

The OneCore backend synthesizes speech through the Windows.Media.SpeechSynthesis API and plays it through the Windows.Media.Audio audio graph API. Both APIs MUST be present for the backend to report PRISM_BACKEND_IS_SUPPORTED_AT_RUNTIME; if either is absent, prism_backend_initialize returns PRISM_ERROR_BACKEND_NOT_AVAILABLE.

prism_backend_speak returns once the text has been queued. Each utterance is synthesized in its entirety on an internal worker thread before playback of it begins. From the return of prism_backend_speak until the audio graph has consumed the final audio frame of the speech, prism_backend_is_speaking reports true, unless speech is paused or stopped in the interim. prism_backend_stop silences the utterance being played immediately and discards all queued utterances.

The rate, pitch, and voice in effect when prism_backend_speak is called apply to that utterance; subsequent changes affect only utterances queued thereafter. Volume is applied during playback, and a change of volume therefore also affects the utterance being played.

The audio graph renders to the default audio output device. When the default device changes, the audio graph is recreated on the new device at the next utterance that begins while no speech is playing. If the audio graph fails, including when its device is removed, speech in progress is discarded, and the audio graph is recreated at the next utterance.

GDK Speech

The GDK speech backend synthesizes speech through the speech synthesis API of the Microsoft Game Development Kit (GDK) and plays it through XAudio2. The backend is compiled only when the GDK headers are available at build time. The build locates the headers through the PRISM_GDK_ROOT build option if it is set, and otherwise through the environment variables defined by the GDK installer.

Windows

On Windows, Prism does not link against the GDK. The backend loads xgameruntime.thunks.dll at run time. The library MUST be resolvable through the host process’s library search order or be present in the directory containing the Prism library; if it is not, the backend does not report PRISM_BACKEND_IS_SUPPORTED_AT_RUNTIME, and prism_backend_initialize returns PRISM_ERROR_BACKEND_NOT_AVAILABLE. Prism does not distribute the library, the redistribution of which is governed by the GDK license. An application that uses the backend MUST deploy the library itself. A build of Prism that locates the library within the GDK copies it to the build directory beside the Prism library; the library is not installed.

The library depends on the Gaming Runtime, which is provided by Microsoft Gaming Services. Gaming Services MUST be installed. Each backend instance holds a reference to the Gaming Runtime from its creation until it is freed. If the Gaming Runtime cannot be initialized, the backend does not report PRISM_BACKEND_IS_SUPPORTED_AT_RUNTIME, and prism_backend_initialize returns PRISM_ERROR_BACKEND_NOT_AVAILABLE.

Xbox

On Xbox, Prism links against the GDK xgameruntime library. When Prism is built as a static library, it shares the title’s Gaming Runtime, and the title MUST call XGameRuntimeInitialize before creating any backend instance. When Prism is built as a shared library, each backend instance holds its own reference to the Gaming Runtime, as on Windows.

All platforms

The runtime-supported probe creates and destroys a speech synthesizer and an XAudio2 engine. prism_backend_initialize enumerates the installed voices and returns PRISM_ERROR_NO_VOICES if the Gaming Runtime reports none.

prism_backend_speak returns once the text has been queued. Each utterance is synthesized in its entirety on an internal worker thread before playback of it begins; the delay before an utterance becomes audible therefore increases with its length. From the return of prism_backend_speak until playback of the utterance ends, prism_backend_is_speaking reports true, unless speech is paused or stopped in the interim. An utterance that cannot be synthesized is discarded, and the failure is reported through the logging facility. Changes of the default audio output device, and removal of the device in use, are handled as described for the SAPI backend.

Until prism_backend_set_voice is called, the backend uses the system default voice, and prism_backend_get_voice returns PRISM_ERROR_VOICE_NOT_FOUND. If the selected voice is no longer installed when prism_backend_refresh_voices is called, the backend reverts to the system default voice.

Rate and pitch are applied through SSML prosody markup. Text passed to prism_backend_speak is escaped and SHALL NOT be interpreted as SSML. The rate and pitch in effect when prism_backend_speak is called apply to that utterance; subsequent changes affect only utterances queued thereafter. Volume is applied during playback, and a change of volume therefore also affects the utterance being played.

Speech Dispatcher

The Speech Dispatcher backend is available natively on Linux and BSD and through a Wine bridge to Windows applications running under Wine or Proton. The native and bridged variants use the same backend identifier and expose the same public Prism API. For more information about using the wine bridges, see the Wine Bridges chapter for build, deployment, and selection requirements.

The native variant’s runtime-supported probe is a non-blocking connection attempt to the configured Speech Dispatcher socket address. The probe does not perform an SSIP handshake. The connection address is taken from the SPEECHD_ADDRESS environment variable if set, and from the platform default address otherwise. Most current Linux distributions configure their service manager to spawn Speech Dispatcher on first client connection. On systems without automatic spawning, the daemon MUST be started before the backend is initialized.

For the Wine variant, Speech Dispatcher MUST be reachable from the Unix session in which Wine is running. A Wine or Proton environment that cannot reach the user’s Speech Dispatcher service does not satisfy this requirement, even if the service is running elsewhere on the host.

Orca

The Orca backend is available natively on Linux and BSD and through a Wine bridge to Windows applications running under Wine or Proton. The native and bridged variants use the same backend identifier and expose the same public Prism API. For more information about using the wine bridges, see the Wine Bridges chapter for build, deployment, and selection requirements.

The native variant’s runtime-supported probe issues a NameHasOwner query against the session bus for the well-known names org.gnome.Orca1.Service and org.gnome.Orca.Service, in that order. For the first name that is owned, the probe issues a further method call on the matching speech-control interface to confirm that the remote-control interface is actually present. A process that owns one of the well-known names but does not answer on the speech-control interface is not reported as runtime-supported, and prism_backend_initialize returns PRISM_ERROR_BACKEND_NOT_AVAILABLE against it. The probe requires that the session bus be available to the host process. Headless environments and minimal SSH sessions that do not provide a session bus do not satisfy this requirement.

The backend supports two variants of the Orca remote-control interface: the current one, available in Orca 50 onwards, and a legacy one, which Orca used when the remote control feature was first introduced. The backend will automatically select whichever version the running Orca instance exposes. Backend initialization SHALL fail on an Orca release which does not implement the interface according to the semantics of D-bus. The Wine variant has the same requirement that the Orca service and its speech-control interface be reachable from the Unix session in which Wine is running.

Spiel

The Spiel backend is available natively on Linux and BSD and through a Wine bridge to Windows applications running under Wine or Proton. In either form, Spiel communicates with speech providers associated with the user’s session rather than synthesizing audio itself. The native and bridged versions of the backend support the same API. For more information about using the wine bridges, see the Wine Bridges chapter for build, deployment, and selection requirements.

The runtime-supported probe requires a session D-Bus connection and at least one currently owned or activatable D-Bus service whose name ends in .Speech.Provider. Initialization will require that any speech providers be genuinely reachable. Headless environments and minimal SSH sessions that do not provide a session bus do not satisfy this requirement. For the Wine variant, the same session services MUST be reachable from the Unix session in which Wine is running.

A single Spiel voice that declares support for multiple languages appears in the backend’s voice list as one entry per language, sharing the same name but with distinct language strings. The voice index selected through prism_backend_set_voice therefore identifies a (voice, language) pair, not a voice alone.

Note: the native Spiel backend is not currently enabled in the Python wheels because Spiel is not in the distribution package repositories used to build those artifacts at this time. The release packages and the Godot add-on for Linux include the backend, and load libspiel-1.0.so.1 at run time when it is present. If you wish to use the native backend from Python, you will need to build Prism and Spiel from source by hand or using a tool such as vcpkg. Wine bridge availability is determined separately by whether the bridge supplied with the build contains Spiel and whether a compatible provider is reachable at run time.

Android Text to Speech

The Prism Android library’s application context MUST be initialized in the host process before any operation is invoked. When Prism is consumed as a standard Android library archive (AAR) through Gradle or another mechanism that respects the merged manifest, this initialization is performed automatically by a ContentProvider registered in the library’s manifest, whose onCreate method runs before the application’s is executed. Consumers that repackage the library in a way that drops the merged manifest entries MUST initialize the application context manually before using any Android backend.

The calling thread MUST be attached to the Java VM. This is automatic for threads created by the platform runtime. Native threads created outside the runtime MUST attach themselves through JavaVM::AttachCurrentThread before invoking Prism. Initialization performs the platform’s text-to-speech engine handshake, which is asynchronous, and waits up to 10 seconds for the engine to report readiness. Because an engine that is already running accepts a new client almost immediately, Prism connects to the engine when its application context is initialized and keeps that connection open for the life of the process. An engine that fails to report readiness within the deadline causes prism_backend_initialize to return PRISM_ERROR_BACKEND_NOT_AVAILABLE.

Android Screen Reader

The application-context and Java VM attachment requirements of the Android Text to Speech backend also apply to the Android Screen Reader backend. Accessibility services MUST be enabled on the device, and at least one accessibility service capable of spoken feedback MUST be enabled in the user’s accessibility settings.

Web Speech

In browsing contexts that enforce an autoplay policy, the first call to an instance of the web speech backend to produce speech MUST originate from a user-initiated event handler such as a click or keydown listener. This is a property of the browsing context, not of Prism. The set of available voices is populated asynchronously by the browser. The voice list reported by the backend MAY be briefly empty after initialization on first page load. Applications SHOULD invoke prism_backend_refresh_voices if the voice list is unexpectedly empty, or arrange to do so in response to the browser’s voiceschanged event.

Python API Reference

Introduction

The Python binding is distributed as the prismatoid package and imported as prism. It requires Python 3.10 or later and depends on cffi. Installation is described in the Getting Started chapter.

The binding is documented in its docstrings, which are available through help(), as in help(prism.Context), and in editors that display docstrings. The chapters that follow are generated from the docstrings. Requirements of individual backends are given in the backend-specific notes.

Correspondence with the C API

Registry functions are methods of Context, and backend functions are methods of Backend, named without their prefix: prism_registry_create_best(ctx) is context.create_best(), and prism_backend_speak(backend, text, interrupt) is backend.speak(text, interrupt). A pair of functions that gets and sets a value is a property, as Backend.volume is for prism_backend_get_volume and prism_backend_set_volume. A function that only gets a value is a read-only property, as Backend.speaking is for prism_backend_is_speaking. Logging and version functions are functions of the prism module.

Enumeration members are named after the C constants without their prefix, and have the same values: PRISM_BACKEND_NVDA is prism.BackendId.NVDA, and PRISM_LOG_LEVEL_WARN is prism.LogLevel.WARN. Feature bits are fields of prism.BackendFeatures, named after the constants in lower case without their prefix: PRISM_BACKEND_SUPPORTS_SPEAK is supports_speak.

Errors are raised as exceptions. Each PrismError value has a subclass of prism.PrismError named after it, defined in prism.common, and each subclass also derives from the closest built-in exception. Methods that obtain a backend raise ValueError where the C function returns NULL.

Text is passed as str and encoded as UTF-8. It MUST NOT contain a NUL character, and MUST NOT be empty where text is required.

Differences from the C API

  • Backends are initialized when they are obtained. There is no equivalent of prism_backend_initialize.
  • Contexts and backends are released when their objects are destroyed. There are no equivalents of prism_shutdown and prism_backend_free. A reference to a Backend MUST be kept for as long as speech is expected to continue.
  • Availability callbacks run on a thread of the binding, not on Prism’s poll thread, and MAY be coroutine functions.
  • Log messages are forwarded to the logging module.
  • Custom backends are subclasses of CustomBackend. Their features are determined by the methods they override.

Context

Context

prism.Context

A Prism context.

Syntax

class Context(
    registry: Registry | None = None,
    *,
    on_availability: AvailabilityCallback | None = None,
    on_baseline: BaselineCallback | None = None,
    poll_interval_ms: int = 0,
    debounce_samples: int = 0,
    backoff_max_ms: int = 0,
    auto_power_manage: bool = True,
)

Parameters

registry

The registry to use, or None for the global registry. The context keeps its own reference, so registry MAY be closed afterwards.

on_availability

A function called with a BackendId, the backend’s name, and True if the backend became available or False if it became unavailable; or None to disable availability monitoring. It MAY be a coroutine function. The remaining arguments are ignored when it is None.

on_baseline

A function called with no arguments when the baseline is set, or None. It MAY be a coroutine function.

poll_interval_ms

The base interval between samples, in milliseconds. 0 selects 1000.

debounce_samples

The number of consecutive samples that MUST agree before a change is reported. 0 selects 2; 1 reports changes immediately.

backoff_max_ms

The longest interval between samples, in milliseconds. 0, or a value not greater than poll_interval_ms, keeps the interval constant.

auto_power_manage

Whether to stop sampling while the system is suspended. It has an effect only where auto_power_supported returns True.

Exceptions

RuntimeError

If the context cannot be created.

Remarks

This class wraps prism_init and prism_shutdown. A context is bound to a registry when it is created: the global registry, which contains the backends compiled into Prism, or a prism.Registry, which MAY also contain custom backends and backends from shared libraries. Backends are ordered by priority, highest first; screen readers have higher priority than speech engines.

create and create_best return independent instances. acquire, acquire_best, and get return a shared instance, as described under Backend.

The methods of a context MAY be called from any thread. The context is shut down when the object is destroyed. Backends obtained from it remain usable.

Availability monitoring

A context created with on_availability samples the availability of every backend on a background thread, and calls on_availability when a backend becomes available or unavailable. A context created without it starts no thread.

The first sample of each backend sets a baseline and is not reported. A change is reported once it has been observed on debounce_samples consecutive samples. If backoff_max_ms is greater than poll_interval_ms, the interval between samples doubles while nothing changes, up to backoff_max_ms, and returns to poll_interval_ms when a change is observed.

The baseline is set after the constructor returns. A change that occurs between the application’s own check of a backend and the baseline sample of that backend is part of the baseline and is not reported. on_baseline is called once the baseline is set, before any call to on_availability. An application that checks availability before then, and keeps the result, SHOULD check again when on_baseline is called. A later notification MAY report a change it has already seen.

Callbacks are not called on Prism’s sampling thread. Each notification is passed to a thread of the binding that runs an asyncio event loop, so a callback MAY block. A callback MAY be a coroutine function, in which case its coroutine runs on that loop. At most one coroutine runs at a time for each backend; a newer notification for the backend replaces one that is waiting to run. Exceptions from callbacks are logged to the prism.dispatch logger. Coroutines still running when the context is destroyed are cancelled. Callbacks MUST synchronize access to shared state, including backends used on other threads.

Context.backends_count

The number of backends in the registry.

Syntax

backends_count: int  # read-only

Remarks

Wraps prism_registry_count. The value includes backends that cannot be used on the current system, and does not change for the life of the registry. 0 means Prism was built without backends for the platform.

Context.id_of

Returns the identifier of a backend, given its position or its name.

Syntax

def id_of(index_or_name: int | str) -> BackendId

Parameters

index_or_name

The position of the backend in the registry, or its name.

Return Value

The identifier of the backend.

Exceptions

KeyError

If no backend is at the position or has the name.

TypeError

If index_or_name is neither an int nor a str. A bool is not accepted.

Remarks

This method wraps prism_registry_id_at and prism_registry_id. Positions run from 0, the highest-priority backend, to one less than backends_count. Names are compared exactly, including case.

Context.name_of

Returns the name of a backend.

Syntax

def name_of(backend_id: BackendId) -> str

Parameters

backend_id

The identifier of the backend.

Return Value

The name of the backend.

Exceptions

KeyError

If the registry does not contain the backend.

Remarks

This method wraps prism_registry_name.

Context.priority_of

Returns the priority of a backend.

Syntax

def priority_of(backend_id: BackendId) -> int

Parameters

backend_id

The identifier of the backend.

Return Value

The priority, or -1 if the registry does not contain the backend.

Remarks

This method wraps prism_registry_priority. Priorities are non-negative; higher priorities are tried first by create_best and acquire_best. The priorities of built-in backends MAY change between releases.

Context.exists

Reports whether the registry contains a backend.

Syntax

def exists(backend_id: BackendId) -> bool

Parameters

backend_id

The identifier of the backend.

Return Value

True if the registry contains the backend.

Remarks

This method wraps prism_registry_exists. It does not report whether the backend can be used; see BackendFeatures.is_supported_at_runtime.

Context.get

Returns the shared instance of a backend, if one exists.

Syntax

def get(backend_id: BackendId) -> Backend | None

Parameters

backend_id

The identifier of the backend.

Return Value

The shared instance, or None.

Exceptions

PrismError

If the instance exists but cannot be initialized.

Remarks

This method wraps prism_registry_get. It never creates an instance. It returns the instance obtained through acquire or acquire_best while any object referring to that instance remains.

Context.create

Creates and initializes an instance of a backend.

Syntax

def create(backend_id: BackendId) -> Backend

Parameters

backend_id

The identifier of the backend.

Return Value

The backend.

Exceptions

ValueError

If the registry does not contain the backend, or the backend cannot be created.

PrismBackendNotAvailableError

If a component the backend requires is missing or not running.

PrismError

If initialization fails for another reason.

Remarks

This method wraps prism_registry_create and prism_backend_initialize. Each call creates an independent instance. Initialization MAY take noticeable time.

Context.create_best

Creates and initializes the highest-priority backend that can be initialized.

Syntax

def create_best() -> Backend

Return Value

The backend.

Exceptions

ValueError

If no backend can be initialized, as on a system with no speech engine. Applications MUST handle this case.

Remarks

This method wraps prism_registry_create_best. Backends are tried in priority order, and the first that initializes is returned. A screen reader backend initializes only while its screen reader is running, so the result is the user’s screen reader if one is running, and otherwise a speech engine.

The choice is not revisited: a backend obtained while no screen reader was running remains a speech engine if one is started later. Availability monitoring, described under Context, reports such changes.

Context.acquire

Returns the shared instance of a backend, creating it if necessary.

Syntax

def acquire(backend_id: BackendId) -> Backend

Parameters

backend_id

The identifier of the backend.

Return Value

The backend.

Exceptions

ValueError

If the registry does not contain the backend, or the backend cannot be created.

PrismBackendNotAvailableError

If a component the backend requires is missing or not running.

PrismError

If initialization fails for another reason.

Remarks

This method wraps prism_registry_acquire and prism_backend_initialize. The instance is shared as described under Backend. create SHOULD be used unless the instance is meant to be shared.

Context.acquire_best

Returns the shared instance of the highest-priority backend that can be initialized.

Syntax

def acquire_best() -> Backend

Return Value

The backend.

Exceptions

ValueError

If no backend can be initialized.

Remarks

This method wraps prism_registry_acquire_best. Backends are tried in priority order, as for create_best, using existing shared instances where they exist. create_best SHOULD be used unless the instance is meant to be shared.

Context.pause_availability_polling

Pauses availability monitoring.

Syntax

def pause_availability_polling() -> None

Remarks

This method wraps prism_availability_poll_pause. While paused, the sampling thread uses no processor time. A sample in progress completes. The method does nothing if monitoring is disabled or already paused.

Context.resume_availability_polling

Resumes availability monitoring.

Syntax

def resume_availability_polling() -> None

Remarks

This method wraps prism_availability_poll_resume. Every backend is sampled immediately, and each whose availability differs from what was last reported is reported without debouncing. A change that occurred and reverted while paused is not reported. The method does nothing if monitoring is disabled or not paused.

Context.auto_power_supported

Reports whether auto_power_manage has an effect.

Syntax

@staticmethod
def auto_power_supported() -> bool

Return Value

True if automatic power management is supported.

Remarks

This method wraps prism_availability_auto_power_supported. Where it returns False, sampling continues while the system is suspended unless pause_availability_polling is called. The result depends only on the platform and build, and the method MAY be called at any time.

Backend

Backend

prism.Backend

A backend instance.

Syntax

class Backend(raw_ptr: ffi.CData)

Parameters

raw_ptr

A PrismBackend * returned by a registry function.

Exceptions

RuntimeError

If raw_ptr is NULL.

PrismBackendNotAvailableError

If a component the backend requires is missing or not running.

PrismError

If initialization fails for another reason.

Remarks

Backends are obtained from Context, and are initialized before they are returned. This class SHOULD NOT be instantiated directly.

The backend is released when the object is destroyed. A backend obtained through Context.create or Context.create_best is an independent instance. A backend obtained through Context.acquire, Context.acquire_best, or Context.get shares one instance with every object obtained in that way for the same backend: settings changed through one object are visible through the others, and the instance is released with the last of them. Releasing a backend MAY discard speech that is queued or playing, so a reference MUST be kept for as long as speech is expected to continue.

A backend is not thread-safe. It MUST NOT be used from more than one thread at a time, and this applies to all objects sharing an instance. Distinct instances MAY be used from different threads.

An operation the backend does not support raises prism.common.PrismNotImplementedError; features reports which operations are supported. Other failures raise the subclass of prism.PrismError for the error Prism reports. Each method lists the exceptions specific to it.

Text arguments MUST be non-empty, MUST NOT contain a NUL character, and MUST be encodable as UTF-8.

Used by Context. This constructor wraps prism_backend_initialize, and takes ownership of raw_ptr. A backend that is already initialized is not initialized again.

Backend.name

The name of the backend, such as "NVDA".

Syntax

name: str  # read-only

Remarks

Wraps prism_backend_name. The name is the one Context.name_of returns for the backend’s identifier.

Backend.speak

Speaks text.

Syntax

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

Parameters

text

The text to speak.

interrupt

If True, speech in progress is stopped and queued speech is discarded before the text is spoken. If False, the text is queued after speech in progress on most backends; some mix the two, and screen readers MAY apply their own setting.

Exceptions

PrismInvalidParamError

If text is empty or contains a NUL character.

UnicodeEncodeError

If text cannot be encoded as UTF-8.

PrismNotImplementedError

If the backend does not support speech.

PrismSpeakError

If synthesis fails.

Remarks

This method wraps prism_backend_speak. The text is spoken with the current voice, rate, pitch, and volume. Depending on the backend, the method returns when speech starts or when it ends; speaking reports whether speech is still in progress, where supported.

A screen reader backend passes the text to the screen reader, which speaks it according to the user’s settings. Some backends limit the length of text; long text SHOULD be divided into shorter segments.

Backend.speak_to_memory

Synthesizes text and passes the audio to a function.

Syntax

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

Parameters

text

The text to synthesize.

on_audio_data

The function that receives the audio.

Exceptions

PrismInvalidParamError

If text is empty or contains a NUL character.

UnicodeEncodeError

If text cannot be encoded as UTF-8.

PrismNotImplementedError

If the backend does not support synthesis to memory. No screen reader backend supports it.

PrismInvalidAudioFormatError

If the backend produces audio Prism cannot convert.

PrismSpeakError

If synthesis fails.

Remarks

This method wraps prism_backend_speak_to_memory. It returns when synthesis is complete and all audio has been passed to on_audio_data, on every backend.

on_audio_data is called one or more times, each time with the next portion of the audio, as described under AudioCallback. It MAY be called on a thread other than the calling thread, and MUST NOT use this backend. If it raises an exception, it is not called again for this synthesis, and the exception is re-raised by this method when synthesis ends.

The format of the audio is reported in advance by channels and sample_rate. Calling stop during synthesis has undefined behavior.

Backend.braille

Shows text on a braille display.

Syntax

def braille(text: str) -> None

Parameters

text

The text to show.

Exceptions

PrismInvalidParamError

If text is empty or contains a NUL character.

UnicodeEncodeError

If text cannot be encoded as UTF-8.

PrismNotImplementedError

If the backend does not support braille. No speech engine backend supports it.

Remarks

This method wraps prism_backend_braille. The text is shown according to the screen reader’s braille settings. Braille output is independent of speech. Some backends succeed when no display is connected and others raise an exception, so the outcome does not indicate whether a display is present.

Backend.output

Presents text through speech and braille.

Syntax

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

Parameters

text

The text to present.

interrupt

As for speak.

Exceptions

PrismInvalidParamError

If text is empty or contains a NUL character.

UnicodeEncodeError

If text cannot be encoded as UTF-8.

PrismNotImplementedError

If the backend does not support output.

PrismSpeakError

If output fails.

Remarks

This method wraps prism_backend_output. A screen reader backend speaks the text and shows it on the braille display; a speech engine backend speaks it, as speak does. Messages meant for the user SHOULD be presented with this method, since a screen reader user may rely on braille alone. The method MAY succeed when one of the two fails.

Backend.stop

Stops speech and discards queued speech.

Syntax

def stop() -> None

Exceptions

PrismNotImplementedError

If the backend does not support stopping.

PrismNotSpeakingError

If no speech is in progress, on some backends. Most backends succeed in that case.

Remarks

This method wraps prism_backend_stop. After it returns, speaking is False. A screen reader backend stops only speech it produced, not speech the screen reader produces on its own.

Calling this method during speak_to_memory has undefined behavior.

Backend.pause

Pauses speech.

Syntax

def pause() -> None

Exceptions

PrismNotImplementedError

If the backend does not support pausing.

PrismNotSpeakingError

If no speech is in progress.

PrismAlreadyPausedError

If speech is already paused.

Remarks

This method wraps prism_backend_pause. Queued speech is kept, and resume continues from the paused position. While paused, speaking is False. Speaking with interrupt=True discards the paused speech; speaking with interrupt=False queues after it.

Backend.resume

Resumes speech paused by pause.

Syntax

def resume() -> None

Exceptions

PrismNotImplementedError

If the backend does not support resuming.

PrismNotPausedError

If speech is not paused, including after stop.

Remarks

This method wraps prism_backend_resume.

Backend.speaking

Whether speech is in progress.

Syntax

speaking: bool  # read-only

Remarks

Wraps prism_backend_is_speaking. Paused speech is not in progress. The value MAY remain True briefly after the last audio is heard. Most screen reader backends do not support this property and raise PrismNotImplementedError.

Backend.volume

The speech volume, from 0.0 (silent) to 1.0 (loudest).

Syntax

volume: float

Remarks

Wraps prism_backend_get_volume and prism_backend_set_volume. The volume is relative to the backend and independent of the system volume. 0.5 is the backend’s default level. A new value applies to subsequent speech.

Screen reader backends do not support this property and raise PrismNotImplementedError. Setting a value outside [0.0, 1.0], NaN, or infinity raises PrismRangeError.

Backend.rate

The speech rate, from 0.0 (slowest) to 1.0 (fastest).

Syntax

rate: float

Remarks

Wraps prism_backend_get_rate and prism_backend_set_rate. 0.5 is always the backend’s default rate. The rate changes tempo but not pitch. A new value applies to subsequent speech.

Screen reader backends do not support this property and raise PrismNotImplementedError. Setting a value outside [0.0, 1.0] raises PrismRangeError.

Backend.pitch

The speech pitch, from 0.0 (lowest) to 1.0 (highest).

Syntax

pitch: float

Remarks

Wraps prism_backend_get_pitch and prism_backend_set_pitch. 0.5 is the backend’s default pitch. A new value applies to subsequent speech.

Screen reader backends, and speech engines whose voices have a fixed pitch, do not support this property and raise PrismNotImplementedError. Setting a value outside [0.0, 1.0] raises PrismRangeError.

Backend.refresh_voices

Reloads the list of voices from the system.

Syntax

def refresh_voices() -> None

Exceptions

PrismNotImplementedError

If the backend does not support voices. No screen reader backend supports them.

Remarks

This method wraps prism_backend_refresh_voices. Voices installed after the backend was initialized appear only after a refresh. A refresh MAY change voice indices, so a stored choice of voice SHOULD be identified by name. Backends that read their voices only during initialization succeed without changing the list.

Backend.voices_count

The number of voices.

Syntax

voices_count: int  # read-only

Remarks

Wraps prism_backend_count_voices. Voice indices run from 0 to one less than this value. The value changes only after refresh_voices. Backends without voice support raise PrismNotImplementedError.

Backend.get_voice_name

Returns the name of a voice.

Syntax

def get_voice_name(idx: int) -> str

Parameters

idx

The index of the voice.

Return Value

The name of the voice, such as "Microsoft David".

Exceptions

PrismRangeError

If idx is out of range.

PrismNotImplementedError

If the backend does not support voices.

Remarks

This method wraps prism_backend_get_voice_name. Names come from the speech engine and are often localized.

Backend.get_voice_language

Returns the language of a voice.

Syntax

def get_voice_language(idx: int) -> str

Parameters

idx

The index of the voice.

Return Value

The language of the voice, usually a BCP 47 tag such as "en-US".

Exceptions

PrismRangeError

If idx is out of range.

PrismNotImplementedError

If the backend does not support voices.

Remarks

This method wraps prism_backend_get_voice_language.

Backend.voice

The index of the voice used for speech.

Syntax

voice: int

Remarks

Wraps prism_backend_get_voice and prism_backend_set_voice. Until a voice is set, the value is the index of the backend’s default voice. A new voice applies to subsequent speech, and MAY change channels, sample_rate, and bit_depth. After refresh_voices, the value is the voice’s new index; if the voice no longer exists, the behavior is undefined.

Setting an index not less than voices_count raises PrismRangeError or PrismVoiceNotFoundError. If setting the voice leaves the backend unusable, the setter raises PrismBackendEnteredUndefinedStateError. Backends without voice support raise PrismNotImplementedError.

Backend.channels

The number of channels in the audio of speak_to_memory.

Syntax

channels: int  # read-only

Remarks

Wraps prism_backend_get_channels. Usually 1. The value depends on the voice. Backends without synthesis to memory raise PrismNotImplementedError.

Backend.sample_rate

The sample rate, in hertz, of the audio of speak_to_memory.

Syntax

sample_rate: int  # read-only

Remarks

Wraps prism_backend_get_sample_rate. Common values are 16000, 22050, 44100, and 48000. The value depends on the voice. Backends without synthesis to memory raise PrismNotImplementedError.

Backend.bit_depth

The bit depth of the speech engine’s native audio.

Syntax

bit_depth: int  # read-only

Remarks

Wraps prism_backend_get_bit_depth. Common values are 8 and 16. Audio from speak_to_memory is always delivered as float samples, so the value is informational. Backends without synthesis to memory raise PrismNotImplementedError.

Backend.features

Feature information for the backend.

Syntax

features: BackendFeatures  # read-only

Remarks

Wraps prism_backend_get_features. The backend is queried on each access, so BackendFeatures.is_supported_at_runtime is current. Determining it MAY require probing for a running process or service, so the result SHOULD be kept rather than read repeatedly when only the other fields are needed.

prism.BackendFeatures

Feature information for a backend.

Syntax

class BackendFeatures(
    is_supported_at_runtime: bool = _bit(0),
    supports_speak: bool = _bit(2),
    supports_speak_to_memory: bool = _bit(3),
    supports_braille: bool = _bit(4),
    supports_output: bool = _bit(5),
    supports_is_speaking: bool = _bit(6),
    supports_stop: bool = _bit(7),
    supports_pause: bool = _bit(8),
    supports_resume: bool = _bit(9),
    supports_set_volume: bool = _bit(10),
    supports_get_volume: bool = _bit(11),
    supports_set_rate: bool = _bit(12),
    supports_get_rate: bool = _bit(13),
    supports_set_pitch: bool = _bit(14),
    supports_get_pitch: bool = _bit(15),
    supports_refresh_voices: bool = _bit(16),
    supports_count_voices: bool = _bit(17),
    supports_get_voice_name: bool = _bit(18),
    supports_get_voice_language: bool = _bit(19),
    supports_get_voice: bool = _bit(20),
    supports_set_voice: bool = _bit(21),
    supports_get_channels: bool = _bit(22),
    supports_get_sample_rate: bool = _bit(23),
    supports_get_bit_depth: bool = _bit(24),
    performs_silence_trimming_on_speak: bool = _bit(25),
    performs_silence_trimming_on_speak_to_memory: bool = _bit(26),
    supports_speak_ssml: bool = _bit(27),
    supports_speak_to_memory_ssml: bool = _bit(28),
    is_screen_reader: bool = _bit(56),
    is_speech_engine: bool = _bit(57),
)

Remarks

Represents the mask returned by prism_backend_get_features, with one bool field for each defined bit. A field is named after its PrismBackendFeature constant without the PRISM_BACKEND_ prefix, in lower case: supports_speak is PRISM_BACKEND_SUPPORTS_SPEAK.

is_supported_at_runtime is True when the backend’s screen reader or speech engine is available. It is advisory; obtaining the backend MAY still fail.

A field beginning with supports_ is True when the backend implements the operation it names, using the operation’s C name: supports_count_voices concerns prism.Backend.voices_count, and supports_is_speaking concerns prism.Backend.speaking. An operation whose field is False raises PrismNotImplementedError. supports_speak_ssml and supports_speak_to_memory_ssml are reserved.

performs_silence_trimming_on_speak and performs_silence_trimming_on_speak_to_memory are True when the backend removes leading and trailing silence from spoken output, or from audio delivered by prism.Backend.speak_to_memory.

is_screen_reader is True for a backend that sends messages to a screen reader, and is_speech_engine for one that synthesizes speech itself. At most one is True. Every built-in backend sets exactly one; a custom backend MAY set neither.

Instances are immutable and compare equal when all fields are equal.

BackendFeatures.from_bits

Creates feature information from a feature mask.

Syntax

@classmethod
def from_bits(bits: int) -> BackendFeatures

Parameters

bits

A mask of PrismBackendFeature bits. Undefined bits are ignored.

Return Value

Feature information whose fields are True for the bits set in

bits.

BackendFeatures.to_bits

Returns the feature mask represented by this object.

Syntax

def to_bits() -> int

Return Value

A mask with the bit of each True field set.

Remarks

BackendFeatures.from_bits(features.to_bits()) is equal to features.

prism.BackendId

A backend identifier.

Syntax

class BackendId(IntEnum)

Remarks

The members correspond to the PRISM_BACKEND_* constants of the C API. Each member is named after its constant without the PRISM_BACKEND_ prefix and has the constant’s value: BackendId.SPEECH_DISPATCHER is PRISM_BACKEND_SPEECH_DISPATCHER. BackendId.INVALID is 0, which never identifies a backend.

The identifier of a backend is a hash of its name. It does not change between releases or platforms, and MAY be stored. Membership of a backend in a particular registry is reported by prism.Context.exists.

Custom backends and backends loaded from shared libraries have identifiers that are not members. BackendId(value) accepts any positive integer and, for such a value, returns a pseudo-member named CUSTOM_ followed by the value in hexadecimal; the same value always returns the same pseudo-member. A negative integer, a bool, or a non-integer raises ValueError.

prism.AudioCallback

The type of a function that receives audio from Backend.speak_to_memory.

Syntax

AudioCallback = Callable[[list[float], int, int], None]

Remarks

The function is called with a list of samples, the number of channels, and the sample rate in hertz. Samples are float values from -1.0 to 1.0, whatever the backend’s native format. Channels are interleaved. The list MAY be empty.

The emit function of prism.CustomBackend.speak_to_memory has the same type.

Custom Backends and Registries

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.

Logging

Logging

prism.LogLevel

The severity of a log message.

Syntax

class LogLevel(IntEnum)

Remarks

The members correspond to the PrismLogLevel values. Each is named after its value without the PRISM_LOG_LEVEL_ prefix and has the same value: LogLevel.WARN is PRISM_LOG_LEVEL_WARN.

Members are ordered from least to most severe. A message is delivered when its level is at least the threshold set by set_level. NONE is above every severity, so as a threshold it delivers nothing. It MUST NOT be the level of a message.

prism.install_logging

Forwards Prism’s log messages to the logging module.

Syntax

def install_logging(logger_name: str = 'prism') -> None

Parameters

logger_name

The prefix of the receiving loggers’ names.

Remarks

This function wraps prism_set_log_handler. A message from the source source goes to the logger named logger_name.source. TRACE maps to a level named TRACE with the value 5; the other levels map to the corresponding logging levels.

The handler replaces the one in effect, including the handler installed for PRISM_LOG; uninstall restores it. If forwarding is already installed, the function does nothing. Messages are delivered only once the threshold has been lowered with set_level.

Messages are delivered on Prism’s logging thread. Handlers of the receiving loggers run on that thread, MUST NOT call the functions of this module, and SHOULD return quickly; messages arriving while the thread is busy MAY be dropped.

The first call registers an atexit function that delivers pending messages and stops the logging thread when the interpreter exits.

Example

Deliver informational messages and above:

import logging
import prism

logging.basicConfig(level=logging.INFO)
prism.install_logging()
prism.set_log_level(prism.LogLevel.INFO)

prism.uninstall_logging

Stops forwarding log messages and restores the previous handler.

Syntax

def uninstall_logging() -> None

Remarks

A message already being delivered MAY still reach logging. The function does nothing if forwarding is not installed.

prism.set_log_level

Sets the least severe level of message delivered.

Syntax

def set_log_level(level: LogLevel) -> LogLevel

Parameters

level

The new threshold.

Return Value

The previous threshold.

Remarks

This function wraps prism_set_log_level. The initial threshold is LogLevel.NONE. Messages below the threshold are discarded where they are produced. The threshold is independent of the handler. The function MAY be called from any thread at any time.

prism.write_log

Writes a message to Prism’s log.

Syntax

def write_log(level: LogLevel, source: str, message: str) -> None

Parameters

level

The level of the message. It MUST NOT be LogLevel.NONE.

source

The component the message comes from.

message

The text of the message.

Exceptions

PrismInvalidParamError

If source or message is empty or contains a NUL character.

Remarks

This function wraps prism_log. The message is subject to the same threshold and handler as Prism’s own messages. A message below the threshold is discarded; otherwise it is queued, and the function returns without waiting for delivery. If the queue is full, the message is dropped, and the number of dropped messages is later reported as a warning from the source "prism".

The function MAY be called from any thread, but MUST NOT be called by a handler of a logger that receives Prism’s messages.

prism.flush_log

Waits until queued log messages have been delivered.

Syntax

def flush_log() -> None

Remarks

This function wraps prism_log_flush. It returns once every message queued before the call has been passed to the handler, or immediately if no handler is installed. Dropped messages are not recovered.

The function MUST NOT be called by a handler of a logger that receives Prism’s messages.

Exceptions

Exceptions

The subclasses of PrismError are defined in prism.common.

prism.common.PrismError

Base class of the exceptions that report Prism errors.

Syntax

class PrismError(code: int, message: str | None = None)

Parameters

code

The numeric PrismError value.

message

The description of the error. If None, the string form of the exception gives the numeric value.

Attributes

code

The numeric PrismError value.

message

The description of the error, or None. It is the result of prism_error_string for errors reported by Prism, and a description of the binding’s own for errors the binding detects.

Remarks

Each PrismError value of the C API other than PRISM_OK has a subclass named after it; PRISM_ERROR_NOT_IMPLEMENTED is reported as PrismNotImplementedError. Values unknown to this version of the binding are reported as PrismUnknownError. Subclasses also derive from the built-in exception closest in meaning, where there is one, so that PrismNotImplementedError is a NotImplementedError and PrismInvalidParamError is a ValueError.

A custom backend MAY raise these exceptions to report a specific error; see prism.CustomBackend.

prism.common.PrismNotInitializedError

The backend is not initialized.

Syntax

class PrismNotInitializedError(PrismError, RuntimeError)

Remarks

Reports PRISM_ERROR_NOT_INITIALIZED. Backends are initialized when they are obtained, so this error is not ordinarily seen.

prism.common.PrismAlreadyInitializedError

The backend is already initialized.

Syntax

class PrismAlreadyInitializedError(PrismError, RuntimeError)

Remarks

Reports PRISM_ERROR_ALREADY_INITIALIZED. The binding treats this error as success when it initializes a backend, so it is not ordinarily seen.

prism.common.PrismInvalidOperationError

The operation is invalid in the current state.

Syntax

class PrismInvalidOperationError(PrismError, RuntimeError)

Remarks

Reports PRISM_ERROR_INVALID_OPERATION. Raised, among other cases, when a registry builder is given a backend whose name or identifier it already contains.

prism.common.PrismInternalError

An internal error occurred.

Syntax

class PrismInternalError(PrismError, RuntimeError)

Remarks

Reports PRISM_ERROR_INTERNAL. Also reported for a custom backend method that raises an exception with no more specific error.

prism.common.PrismBackendNotAvailableError

The backend is not available on this system.

Syntax

class PrismBackendNotAvailableError(PrismError, RuntimeError)

Remarks

Reports PRISM_ERROR_BACKEND_NOT_AVAILABLE. A component the backend requires is missing or not running. The backend object remains valid but cannot produce output.

prism.common.PrismNotImplementedError

The backend does not support the operation.

Syntax

class PrismNotImplementedError(PrismError, NotImplementedError)

Remarks

Reports PRISM_ERROR_NOT_IMPLEMENTED. Support for each operation is reported by prism.Backend.features.

prism.common.PrismInvalidParamError

An argument is invalid.

Syntax

class PrismInvalidParamError(PrismError, ValueError)

Remarks

Reports PRISM_ERROR_INVALID_PARAM. Also raised by the binding, without calling Prism, for text that is empty or contains a NUL character.

prism.common.PrismRangeError

An argument is outside its valid range.

Syntax

class PrismRangeError(PrismError, IndexError)

Remarks

Reports PRISM_ERROR_RANGE_OUT_OF_BOUNDS. Raised for a voice parameter outside [0.0, 1.0] and for an out-of-range voice index.

prism.common.PrismInvalidUtf8Error

A string is not valid UTF-8.

Syntax

class PrismInvalidUtf8Error(PrismError, UnicodeError)

Remarks

Reports PRISM_ERROR_INVALID_UTF8. Text passed through the binding is encoded from str, so this error arises only from text produced elsewhere, such as backend names in a shared library.

prism.common.PrismNotSpeakingError

No speech is in progress.

Syntax

class PrismNotSpeakingError(PrismError)

Remarks

Reports PRISM_ERROR_NOT_SPEAKING.

prism.common.PrismNotPausedError

Speech is not paused.

Syntax

class PrismNotPausedError(PrismError)

Remarks

Reports PRISM_ERROR_NOT_PAUSED.

prism.common.PrismAlreadyPausedError

Speech is already paused.

Syntax

class PrismAlreadyPausedError(PrismError)

Remarks

Reports PRISM_ERROR_ALREADY_PAUSED.

prism.common.PrismSpeakError

Speech synthesis failed.

Syntax

class PrismSpeakError(PrismError, IOError)

Remarks

Reports PRISM_ERROR_SPEAK_FAILURE.

prism.common.PrismNoVoicesError

The backend has no voices.

Syntax

class PrismNoVoicesError(PrismError)

Remarks

Reports PRISM_ERROR_NO_VOICES.

prism.common.PrismVoiceNotFoundError

The voice does not exist.

Syntax

class PrismVoiceNotFoundError(PrismError, LookupError)

Remarks

Reports PRISM_ERROR_VOICE_NOT_FOUND. Backends report an out-of-range voice index either as this error or as PrismRangeError.

prism.common.PrismMemoryError

Memory allocation failed.

Syntax

class PrismMemoryError(PrismError, MemoryError)

Remarks

Reports PRISM_ERROR_MEMORY_FAILURE.

prism.common.PrismUnknownError

An unclassified error occurred.

Syntax

class PrismUnknownError(PrismError)

Remarks

Reports PRISM_ERROR_UNKNOWN. Also reported for error values unknown to this version of the binding.

prism.common.PrismInvalidAudioFormatError

The audio format is not one Prism can handle.

Syntax

class PrismInvalidAudioFormatError(PrismError, RuntimeError)

Remarks

Reports PRISM_ERROR_INVALID_AUDIO_FORMAT. The speech engine or voice uses an audio format Prism cannot convert, or reported format parameters that are not meaningful.

prism.common.PrismInternalBackendLimitExceededError

The backend’s instance limit would be exceeded.

Syntax

class PrismInternalBackendLimitExceededError(PrismError, RuntimeError)

Remarks

Reports PRISM_ERROR_INTERNAL_BACKEND_LIMIT_EXCEEDED. The backend allows only a fixed number of instances to exist at once.

prism.common.PrismBackendEnteredUndefinedStateError

The backend is in an undefined state.

Syntax

class PrismBackendEnteredUndefinedStateError(PrismError, RuntimeError)

Remarks

Reports PRISM_ERROR_BACKEND_ENTERED_UNDEFINED_STATE. An operation failed in a way that left the backend unusable. The backend MUST NOT be used further.

prism.common.PrismLibraryLoadFailedError

A shared library could not be opened.

Syntax

class PrismLibraryLoadFailedError(PrismError, OSError)

Remarks

Reports PRISM_ERROR_LIBRARY_LOAD_FAILED. The file does not exist, is not a loadable library, was built for another architecture, or failed to initialize.

prism.common.PrismLibraryInvalidError

A shared library is not a Prism plugin.

Syntax

class PrismLibraryInvalidError(PrismError, ImportError)

Remarks

Reports PRISM_ERROR_LIBRARY_INVALID. The library does not export prism_plugin_query.

prism.common.PrismIncompatibleAbiError

A plugin is incompatible with this version of Prism.

Syntax

class PrismIncompatibleAbiError(PrismError, RuntimeError)

Remarks

Reports PRISM_ERROR_INCOMPATIBLE_ABI. The plugin declined the host, or declared a plugin ABI generation this version does not accept.

Utilities

Utilities

prism.version

Returns the version of the Prism library in use.

Syntax

def version() -> int

Return Value

The encoded version.

Remarks

This function wraps prism_version. The version is that of the loaded native library, which MAY differ from the version of the prismatoid package. It is encoded as (major << 16) | (minor << 8) | patch, so version 1.4.7 is 0x010407; bits 24 through 31 are zero. Encoded versions compare in the same order as the versions they encode. Prism follows Semantic Versioning, and while the major version is 0, minor releases MUST NOT be assumed compatible.

prism.version_string

Returns the version of the Prism library in use, as a string.

Syntax

def version_string() -> str

Return Value

The version string.

Remarks

This function wraps prism_version_string. The result has the form "1.4.7", and is also available as prism.__version__.

prism.__version__

The version of the Prism library in use, as returned by version_string.

Syntax

__version__ = version_string()

Godot API Reference

Introduction

The Godot binding is distributed as an add-on, which requires Godot 4.5 or later. Installation is described in the Getting Started chapter.

The binding consists of two classes: GodotPrismContext, of which the extension registers one instance as the singleton Prism, and GodotPrismBackend. Both are documented in the editor’s help. The chapters that follow are generated from the same class reference. Requirements of individual backends are given in the backend-specific notes.

Correspondence with the C API

The Prism singleton owns a context, so the application neither creates nor destroys one. Registry functions are methods of the singleton, and backend functions are methods of GodotPrismBackend, named without their prefix: prism_registry_create_best(ctx) is Prism.create_best(), and prism_backend_speak(backend, text, interrupt) is backend.speak(text, interrupt). A pair of functions that gets and sets a value is a property, as backend.volume is for prism_backend_get_volume and prism_backend_set_volume.

Constants keep their C names and are constants of the class that uses them. PrismError values and PrismBackendFeature flags are constants of GodotPrismBackend, as in GodotPrismBackend.PRISM_OK. PrismLogLevel values are constants of GodotPrismContext, as in GodotPrismContext.PRISM_LOG_LEVEL_WARN. Backend identifiers are constants of GodotPrismContext without the PRISM_BACKEND_ prefix: PRISM_BACKEND_NVDA is GodotPrismContext.NVDA.

Backend identifiers are int values. Godot integers are signed, so an identifier whose highest bit is set is negative. Identifiers SHOULD be treated as opaque.

Differences from the C API

  • Backends are initialized when they are obtained. There is no equivalent of prism_backend_initialize; to retry after a failure, obtain the backend again.
  • Methods that can fail return a PrismError value. Property getters and setters print errors instead.
  • A backend is released with its last reference, and speech in progress is stopped first if the backend supports stopping. A reference MUST be kept for as long as speech is expected to continue.
  • Availability changes and log messages are delivered through signals emitted on the main thread.
  • Custom backends cannot be written in GDScript. Plugins can be loaded from shared libraries, including from the project’s PCK.
  • speak_to_stream synthesizes speech to an AudioStreamWAV.

GodotPrismContext

GodotPrismContext

Inherits: Object

Provides access to the user’s screen reader and to speech engines.

Description

The extension registers one instance of this class as the singleton Prism, created when the extension is loaded and freed when it is unloaded. Further instances should not be created.

The singleton holds a registry of backends. Each backend is a screen reader or a speech engine, and is obtained as a GodotPrismBackend. The registry contains the backends compiled into the extension for the current platform, and those loaded with load_backend_library(). Backends are ordered by priority, and screen readers have higher priority than speech engines, so create_best() returns the user’s screen reader if one is running, and a speech engine otherwise.

extends Node

var backend: GodotPrismBackend

func _ready() -> void:
    backend = Prism.create_best()
    if backend == null:
        push_error("No speech backend is available.")
        return
    var err := backend.output("Hello from Prism.")
    if err != GodotPrismBackend.PRISM_OK:
        push_error("Output failed: %s" % Prism.get_error_string(err))

Backend identifiers are [int] values: the PrismBackendId constants for built-in backends, and a hash of the name for others. An identifier whose highest bit is set is negative, so identifiers should be compared and stored, not used in arithmetic.

Availability changes and log messages are delivered through signals emitted on the main thread. See start_availability_monitoring() and set_log_signal_enabled().

Backends cannot be implemented in GDScript, because Prism calls backend implementations on other threads. Custom backends must be built as shared libraries and loaded with load_backend_library().

Tutorials

Properties

TypeName
intbackends_count

Methods

ReturnsMethod
GodotPrismBackendacquire(id: int)
GodotPrismBackendacquire_best()
GodotPrismBackendcreate(id: int)
GodotPrismBackendcreate_best()
voidflush_log() static
Dictionary[]get_available_backends() const
GodotPrismBackendget_backend(id: int)
Stringget_error_string(error: PrismError) static
intget_version() static
Stringget_version_string() static
boolhas(id: int) const
intid_at(index: int) const
intid_of(name: String) const
boolis_auto_power_management_supported() static
boolis_availability_monitoring() const
boolis_backend_available(id: int)
boolis_log_signal_enabled() const
PrismErrorload_backend_library(path: String, priority_override: int = -1)
Stringname_of(id: int) const
voidpause_availability_monitoring()
intpriority_of(id: int) const
voidresume_availability_monitoring()
PrismLogLevelset_log_level(level: PrismLogLevel) static
voidset_log_signal_enabled(enabled: bool)
PrismErrorstart_availability_monitoring(poll_interval_ms: int = 0, debounce_samples: int = 0, backoff_max_ms: int = 0, auto_power_manage: bool = true)
voidstop_availability_monitoring()
PrismErrorwrite_log(level: PrismLogLevel, source: String, message: String) static

Signals

availability_baseline_established()

Emitted once each time availability monitoring starts, after every backend has been sampled once and before any backend_availability_changed.

A change between a check made with is_backend_available() and the first sample of the same backend is part of the baseline and is not reported. If such a check was made before this signal and its result kept, the check should be repeated when this signal is emitted. A later backend_availability_changed may then report a change the repeated check already observed.

backend_availability_changed(id: int, name: String, available: bool)

Emitted when the backend id, named name, becomes available (available is true) or unavailable (available is false), while availability monitoring is active. Backends already obtained are not affected.

Emitted on the main thread during idle processing, which may be slightly after the change.

log_message_received(level: int, source: String, message: String)

Emitted for each log message while set_log_signal_enabled() is on. level is a PrismLogLevel value, and source is the component that wrote the message.

Emitted on the main thread during idle processing. Messages written faster than they are delivered are dropped, and a warning reports how many.

enum PrismBackendId

INVALID = 0

Never identifies a backend. Returned by methods that find no backend.

SAPI = 2120622734136241766

Microsoft Speech API (SAPI), on Windows.

AV_SPEECH = 2946271790952897572

AVSpeechSynthesizer, on Apple platforms.

VOICE_OVER = -3798619614742230069

The VoiceOver screen reader, on Apple platforms.

SPEECH_DISPATCHER = -2029161259604513794

Speech Dispatcher, on Linux and BSD.

NVDA = -8517404458065585578

The NVDA screen reader, on Windows.

JAWS = -6035561368583359170

The JAWS screen reader, on Windows.

ONE_CORE = 7464667106434960564

Windows OneCore speech synthesis, on Windows 10 and later.

ORCA = 1200807161653361004

The Orca screen reader, on Linux and BSD.

ANDROID_TTS = -4893345499418663476

The Android text-to-speech engine.

ANDROID_SCREEN_READER = -3343428537155827381

The active Android screen reader, such as TalkBack.

WEB_SPEECH = 3851232497565452943

The Web Speech API, on the web.

UIA = 7077670808258580366

UI Automation notifications, announced by any running Windows screen reader.

ZDSR = 4437107128196344366

The Zhengdu screen reader (ZDSR), on Windows.

ZOOM_TEXT = -5889690839739067271

ZoomText, on Windows.

BOY_PC_READER = 2907841139227963407

The Boy PC Reader screen reader, on Windows.

PC_TALKER = 3768269449491363893

The PC-Talker screen reader, on Windows.

SENSE_READER = -1349003421657218318

The Sense Reader screen reader, on Windows.

SYSTEM_ACCESS = -8970903673761939786

The System Access screen reader, on Windows. Available only in builds that enable legacy backends.

WINDOW_EYES = -7989147587171558381

The Window-Eyes screen reader, on Windows. Available only in builds that enable legacy backends.

SPIEL = 5155290001588869276

Spiel, on Linux.

GDK_SPEECH = -3198251574740554991

Speech synthesis of the Microsoft Game Development Kit, on Windows and Xbox.

enum PrismLogLevel

PRISM_LOG_LEVEL_TRACE = 0

Trace messages, the least severe level.

PRISM_LOG_LEVEL_DEBUG = 1

Debug messages.

PRISM_LOG_LEVEL_INFO = 2

Informational messages.

PRISM_LOG_LEVEL_WARN = 3

Warnings.

PRISM_LOG_LEVEL_ERROR = 4

Errors, the most severe level.

PRISM_LOG_LEVEL_NONE = 5

Above every severity. As the level passed to set_log_level(), delivers nothing.

Property Descriptions

int backends_count

Getter get_backends_count()

The number of backends in the registry, including backends that cannot currently be used. It changes only when load_backend_library() succeeds.

Method Descriptions

GodotPrismBackend acquire(id: int)

Returns the shared instance of the backend id, creating and initializing it if necessary, or null if the registry does not contain the backend or it cannot be created. All objects returned by acquire(), acquire_best(), and get_backend() for the same backend share one instance, including its settings. Use create() unless sharing is intended.

If initialization fails, the error is printed and the backend is still returned; its methods then return GodotPrismBackend.PRISM_ERROR_NOT_INITIALIZED.

GodotPrismBackend acquire_best()

Returns the shared instance of the highest-priority backend that can be initialized, or null if none can. Existing shared instances are used where they exist. Use create_best() unless sharing is intended.

GodotPrismBackend create(id: int)

Creates and initializes a new instance of the backend id. Returns null if the registry does not contain the backend or it cannot be created. Initialization may take noticeable time.

If initialization fails, for example because the backend’s screen reader is not running, the error is printed and the backend is still returned; its methods then return GodotPrismBackend.PRISM_ERROR_NOT_INITIALIZED. Create the backend again to retry. create_best() skips backends that cannot be initialized.

GodotPrismBackend create_best()

Creates and initializes the highest-priority backend that can be initialized, or returns null if none can. The result is the user’s screen reader if one is running, and a speech engine otherwise. null is a normal result on a system without a speech engine, and must be handled.

The choice is not revisited if a screen reader starts or stops later. See start_availability_monitoring().

void flush_log() static

Waits until every log message written so far has been passed to the log handler. Returns immediately if no handler is installed. log_message_received is emitted for these messages during the next idle frame, after this method returns.

Dictionary[] get_available_backends() const

Returns a Dictionary for each backend in the registry, in priority order, with the keys "id", "name", and "priority". The result includes backends that cannot currently be used; see is_backend_available().

GodotPrismBackend get_backend(id: int)

Returns the shared instance of the backend id, or null if there is none. Unlike acquire(), this method never creates an instance.

String get_error_string(error: PrismError) static

Returns an English description of error, or "Unknown error" if it is not a GodotPrismBackend.PrismError value.

int get_version() static

Returns the version of the Prism library, encoded as (major << 16) | (minor << 8) | patch. Version 1.4.7 is 0x010407. Encoded versions compare in version order.

String get_version_string() static

Returns the version of the Prism library as a string, such as "1.4.7".

bool has(id: int) const

Returns true if the registry contains the backend id. This does not mean the backend can be used; see is_backend_available().

int id_at(index: int) const

Returns the identifier of the backend at index in the registry, or INVALID if index is out of range. Index 0 is the highest-priority backend.

int id_of(name: String) const

Returns the identifier of the backend named name, or INVALID if name is empty or no backend has that name. The comparison is case-sensitive.

bool is_auto_power_management_supported() static

Returns true if the auto_power_manage argument of start_availability_monitoring() has an effect on this platform. Otherwise, monitoring continues while the system is suspended unless pause_availability_monitoring() is called.

bool is_availability_monitoring() const

Returns true if availability monitoring is active, including while it is paused.

bool is_backend_available(id: int)

Returns true if the backend id can currently be used. A temporary instance is created and queried, without being initialized. The result is advisory: initialization may still fail. Returns false if the registry does not contain the backend.

bool is_log_signal_enabled() const

Returns true if log messages are delivered through log_message_received.

PrismError load_backend_library(path: String, priority_override: int = -1)

Loads the backends of the shared library plugin at path. path may be absolute or begin with res:// or user://, and may refer to a file in the project’s PCK. If priority_override is not negative, it replaces the priority of every backend in the plugin.

Returns GodotPrismBackend.PRISM_OK on success. Returns GodotPrismBackend.PRISM_ERROR_INVALID_PARAM if path is empty or priority_override is too large, GodotPrismBackend.PRISM_ERROR_NOT_IMPLEMENTED if the platform does not support shared library backends, GodotPrismBackend.PRISM_ERROR_LIBRARY_LOAD_FAILED if the library cannot be opened, GodotPrismBackend.PRISM_ERROR_LIBRARY_INVALID if it is not a Prism plugin, GodotPrismBackend.PRISM_ERROR_INCOMPATIBLE_ABI if it is incompatible with this version of Prism, and GodotPrismBackend.PRISM_ERROR_INVALID_OPERATION if one of its backends has the name of a backend already in the registry.

A successful call replaces the singleton’s registry with one that also contains the new backends. Backends obtained earlier remain valid. Active availability monitoring is restarted, and availability_baseline_established is emitted again. If the call fails, nothing changes, except that if the library loads but monitoring cannot be restarted, the new registry is kept, monitoring is stopped, and GodotPrismBackend.PRISM_ERROR_INTERNAL is returned.

A library inside the PCK is first copied to user://prism/backends/, into a directory named after the SHA-256 hash of its contents, and loaded from there. The copy is reused by later calls. Libraries the plugin depends on are not copied, and must be found through the platform’s library search path. On macOS, the library must be signed before it is packed, by the project’s team if library validation is enforced. The directory may be deleted while no copy is loaded.

Loading a library runs its code with the project’s privileges. Only load trusted libraries.

String name_of(id: int) const

Returns the name of the backend id, such as "NVDA", or an empty String if the registry does not contain it.

void pause_availability_monitoring()

Pauses availability monitoring. While paused, monitoring uses no processor time. Does nothing if monitoring is not active.

int priority_of(id: int) const

Returns the priority of the backend id, or -1 if the registry does not contain it. Higher priorities are tried first by create_best(). The priorities of built-in backends may change between releases.

void resume_availability_monitoring()

Resumes paused availability monitoring. Every backend is sampled immediately, and backend_availability_changed is emitted for each whose availability differs from what was last reported. A change that occurred and reverted while paused is not reported. Does nothing if monitoring is not active.

PrismLogLevel set_log_level(level: PrismLogLevel) static

Sets the least severe level of log message delivered, and returns the previous level. The initial level is PRISM_LOG_LEVEL_NONE, which delivers nothing. The level applies to the whole process.

void set_log_signal_enabled(enabled: bool)

If enabled is true, log messages are delivered through log_message_received, replacing the log handler in effect, including the one installed for the PRISM_LOG environment variable. If false, the previous handler is restored.

No messages are delivered until set_log_level() lowers the level.

func _ready() -> void:
    Prism.log_message_received.connect(_on_log_message)
    Prism.set_log_signal_enabled(true)
    Prism.set_log_level(GodotPrismContext.PRISM_LOG_LEVEL_INFO)

func _on_log_message(level: int, source: String, message: String) -> void:
    print("%s: %s" % [source, message])

PrismError start_availability_monitoring(poll_interval_ms: int = 0, debounce_samples: int = 0, backoff_max_ms: int = 0, auto_power_manage: bool = true)

Starts sampling the availability of every backend on a background thread. backend_availability_changed is emitted when a backend becomes available or unavailable.

poll_interval_ms is the base interval between samples in milliseconds; 0 selects 1000. debounce_samples is the number of consecutive samples that must agree before a change is reported; 0 selects 2, and 1 reports changes immediately. backoff_max_ms is the longest interval, to which the interval doubles while nothing changes; 0, or a value not greater than poll_interval_ms, keeps the interval constant. If auto_power_manage is true, sampling stops while the system is suspended, where is_auto_power_management_supported() returns true.

The first sample of each backend sets a baseline and is not reported. availability_baseline_established is emitted once the baseline is set.

Returns GodotPrismBackend.PRISM_OK on success, GodotPrismBackend.PRISM_ERROR_RANGE_OUT_OF_BOUNDS if an integer argument is negative or greater than 4294967295, GodotPrismBackend.PRISM_ERROR_INVALID_OPERATION if monitoring is already active, and GodotPrismBackend.PRISM_ERROR_INTERNAL if monitoring cannot be started.

func _ready() -> void:
    Prism.backend_availability_changed.connect(_on_availability_changed)
    var err := Prism.start_availability_monitoring()
    if err != GodotPrismBackend.PRISM_OK:
        push_error("Monitoring failed: %s" % Prism.get_error_string(err))

func _on_availability_changed(id: int, backend_name: String, available: bool) -> void:
    print("%s: %s" % [backend_name, available])

void stop_availability_monitoring()

Stops availability monitoring. Does nothing if monitoring is not active.

PrismError write_log(level: PrismLogLevel, source: String, message: String) static

Writes message to Prism’s log, from the component source, at level. The message is queued and delivered with Prism’s own messages; it is dropped if the queue is full. level must not be PRISM_LOG_LEVEL_NONE.

Returns GodotPrismBackend.PRISM_OK, or GodotPrismBackend.PRISM_ERROR_INVALID_PARAM if source or message is empty.

GodotPrismBackend

GodotPrismBackend

Inherits: RefCounted

A screen reader or speech engine.

Description

A backend presents text through the user’s screen reader or through a speech engine. Backends are obtained from the Prism singleton, a GodotPrismContext, and are initialized before they are returned. An instance created with new() refers to no backend, and is_valid() returns false for it.

The backend is released with its last reference. Speech in progress is stopped first, if the backend supports stop(). Keep a reference for as long as speech should continue, for example in a member variable.

Backends support different operations; features reports which. An unsupported operation returns PRISM_ERROR_NOT_IMPLEMENTED. Methods that can fail return a PrismError value, described by GodotPrismContext.get_error_string(). Property setters and getters print errors instead, and getters then return the value documented for the property.

A backend must not be used from more than one thread at a time. Backends returned by GodotPrismContext.acquire() share an instance, and this applies to all of them.

Use output() for messages to the user; it reaches both speech and braille. The rate, pitch, and volume of screen reader backends cannot be changed.

Tutorials

Properties

Methods

ReturnsMethod
PrismErrorbraille(text: String)
intfind_voice(pattern: String) const
intfind_voice_by_language(prefix: String) const
Stringget_voice_language(index: int) const
Stringget_voice_name(index: int) const
Dictionary[]get_voices() const
boolhas_feature(flag: PrismBackendFeature) const
boolis_valid() const
PrismErroroutput(text: String, interrupt: bool = false)
PrismErrorpause()
PrismErrorrefresh_voices()
PrismErrorresume()
PrismErrorspeak(text: String, interrupt: bool = false)
PrismErrorspeak_to_memory(text: String, callback: Callable)
AudioStreamWAVspeak_to_stream(text: String)
PrismErrorstop()

enum PrismError

PRISM_OK = 0

The operation succeeded.

PRISM_ERROR_NOT_INITIALIZED = 1

The backend failed to initialize when it was obtained.

PRISM_ERROR_INVALID_PARAM = 2

An argument is invalid.

PRISM_ERROR_NOT_IMPLEMENTED = 3

The backend does not support the operation.

PRISM_ERROR_NO_VOICES = 4

The backend has no voices.

PRISM_ERROR_VOICE_NOT_FOUND = 5

The voice does not exist.

PRISM_ERROR_SPEAK_FAILURE = 6

Speech synthesis failed.

PRISM_ERROR_MEMORY_FAILURE = 7

Memory allocation failed.

PRISM_ERROR_RANGE_OUT_OF_BOUNDS = 8

An argument is outside its valid range.

PRISM_ERROR_INTERNAL = 9

An internal error occurred.

PRISM_ERROR_NOT_SPEAKING = 10

No speech is in progress.

PRISM_ERROR_NOT_PAUSED = 11

Speech is not paused.

PRISM_ERROR_ALREADY_PAUSED = 12

Speech is already paused.

PRISM_ERROR_INVALID_UTF8 = 13

A string is not valid UTF-8.

PRISM_ERROR_INVALID_OPERATION = 14

The operation is invalid in the current state.

PRISM_ERROR_ALREADY_INITIALIZED = 15

The backend is already initialized. Not returned by this extension’s methods.

PRISM_ERROR_BACKEND_NOT_AVAILABLE = 16

The backend’s screen reader or speech engine is not installed or not running.

PRISM_ERROR_UNKNOWN = 17

An unclassified error occurred.

PRISM_ERROR_INVALID_AUDIO_FORMAT = 18

The backend’s audio format is not one Prism can convert.

PRISM_ERROR_INTERNAL_BACKEND_LIMIT_EXCEEDED = 19

The backend allows only a fixed number of instances, and another would exceed it.

PRISM_ERROR_BACKEND_ENTERED_UNDEFINED_STATE = 20

The backend is in an undefined state and must not be used further.

PRISM_ERROR_LIBRARY_LOAD_FAILED = 21

A shared library could not be opened.

PRISM_ERROR_LIBRARY_INVALID = 22

A shared library is not a Prism plugin.

PRISM_ERROR_INCOMPATIBLE_ABI = 23

A plugin is incompatible with this version of Prism.

flags PrismBackendFeature

PRISM_BACKEND_IS_SUPPORTED_AT_RUNTIME = 1

The backend’s screen reader or speech engine is available. Advisory; initialization may still fail.

PRISM_BACKEND_SUPPORTS_SPEAK = 4

speak() is supported.

PRISM_BACKEND_SUPPORTS_SPEAK_TO_MEMORY = 8

speak_to_memory() and speak_to_stream() are supported.

PRISM_BACKEND_SUPPORTS_BRAILLE = 16

braille() is supported.

PRISM_BACKEND_SUPPORTS_OUTPUT = 32

output() is supported.

PRISM_BACKEND_SUPPORTS_IS_SPEAKING = 64

speaking reports the backend’s state.

PRISM_BACKEND_SUPPORTS_STOP = 128

stop() is supported.

PRISM_BACKEND_SUPPORTS_PAUSE = 256

pause() is supported.

PRISM_BACKEND_SUPPORTS_RESUME = 512

resume() is supported.

PRISM_BACKEND_SUPPORTS_SET_VOLUME = 1024

volume can be set.

PRISM_BACKEND_SUPPORTS_GET_VOLUME = 2048

volume can be read.

PRISM_BACKEND_SUPPORTS_SET_RATE = 4096

rate can be set.

PRISM_BACKEND_SUPPORTS_GET_RATE = 8192

rate can be read.

PRISM_BACKEND_SUPPORTS_SET_PITCH = 16384

pitch can be set.

PRISM_BACKEND_SUPPORTS_GET_PITCH = 32768

pitch can be read.

PRISM_BACKEND_SUPPORTS_REFRESH_VOICES = 65536

refresh_voices() is supported.

PRISM_BACKEND_SUPPORTS_COUNT_VOICES = 131072

voices_count reports the number of voices.

PRISM_BACKEND_SUPPORTS_GET_VOICE_NAME = 262144

get_voice_name() is supported.

PRISM_BACKEND_SUPPORTS_GET_VOICE_LANGUAGE = 524288

get_voice_language() is supported.

PRISM_BACKEND_SUPPORTS_GET_VOICE = 1048576

voice can be read.

PRISM_BACKEND_SUPPORTS_SET_VOICE = 2097152

voice can be set.

PRISM_BACKEND_SUPPORTS_GET_CHANNELS = 4194304

channels can be read.

PRISM_BACKEND_SUPPORTS_GET_SAMPLE_RATE = 8388608

sample_rate can be read.

PRISM_BACKEND_SUPPORTS_GET_BIT_DEPTH = 16777216

bit_depth can be read.

PRISM_BACKEND_PERFORMS_SILENCE_TRIMMING_ON_SPEAK = 33554432

The backend removes leading and trailing silence from speech.

PRISM_BACKEND_PERFORMS_SILENCE_TRIMMING_ON_SPEAK_TO_MEMORY = 67108864

The backend removes leading and trailing silence from synthesized audio.

PRISM_BACKEND_SUPPORTS_SPEAK_SSML = 134217728

Reserved.

PRISM_BACKEND_SUPPORTS_SPEAK_TO_MEMORY_SSML = 268435456

Reserved.

PRISM_BACKEND_IS_SCREEN_READER = 72057594037927936

The backend sends messages to a screen reader. Every built-in backend has exactly one of this flag and PRISM_BACKEND_IS_SPEECH_ENGINE.

PRISM_BACKEND_IS_SPEECH_ENGINE = 144115188075855872

The backend synthesizes speech without a screen reader.

Property Descriptions

int bit_depth

Getter get_bit_depth()

The bit depth of the speech engine’s native audio, commonly 8 or 16. speak_to_memory() always delivers floating-point samples. -1 if the backend does not support synthesis to memory or the query fails.

int channels

Getter get_channels()

The number of channels in synthesized audio, usually 1. It depends on the voice. -1 if the backend does not support synthesis to memory or the query fails.

PrismBackendFeature features

Getter get_features()

The backend’s PrismBackendFeature flags. The backend is queried each time the property is read.

String name

Getter get_name()

The name of the backend, such as "NVDA".

float pitch

Setter set_pitch(value), getter get_pitch()

The pitch of speech, from 0.0 (lowest) to 1.0 (highest). 0.5 is the backend’s default. A new value applies to subsequent speech. Not supported by screen readers, or by speech engines whose voices have a fixed pitch.

Reads 0.5 if unsupported and NAN if the query fails. Setting a value outside the range, or setting it on a backend that does not support it, prints an error.

float rate

Setter set_rate(value), getter get_rate()

The rate of speech, from 0.0 (slowest) to 1.0 (fastest). 0.5 is always the backend’s default. A new value applies to subsequent speech. Not supported by screen readers.

Reads 0.5 if unsupported and NAN if the query fails. Setting a value outside the range, or setting it on a backend that does not support it, prints an error.

int sample_rate

Getter get_sample_rate()

The sample rate of synthesized audio, in hertz. It depends on the voice. -1 if the backend does not support synthesis to memory or the query fails.

bool speaking

Getter get_speaking()

true while speech is in progress. Paused speech is not in progress. Reads false if the backend cannot report its state, as most screen readers cannot, or if the query fails; check PRISM_BACKEND_SUPPORTS_IS_SPEAKING before relying on false.

int voice

Setter set_voice(value), getter get_voice()

The index of the voice used for speech, from 0 to voices_count minus one. Initially the backend’s default voice. A new voice applies to subsequent speech, and may change channels, sample_rate, and bit_depth.

Reads -1 if the backend does not support voices or the query fails. Setting an index out of range, or setting it on a backend that does not support voices, prints an error.

int voices_count

Getter get_voices_count()

The number of voices, as of the last refresh_voices(). 0 if no voices are installed, the backend does not support voices, or the query fails.

float volume

Setter set_volume(value), getter get_volume()

The volume of speech, from 0.0 (silent) to 1.0 (loudest), independent of the system volume. 0.5 is the backend’s default. A new value applies to subsequent speech. Not supported by screen readers.

Reads 0.5 if unsupported and NAN if the query fails. Setting a value outside the range, or setting it on a backend that does not support it, prints an error.

Method Descriptions

PrismError braille(text: String)

Shows text on the user’s braille display, according to the screen reader’s braille settings. Braille output does not affect speech.

Returns PRISM_ERROR_INVALID_PARAM if text is empty, and PRISM_ERROR_NOT_IMPLEMENTED if the backend does not support braille, as no speech engine does. Some backends succeed when no display is connected and others fail, so the result does not indicate whether a display is present.

int find_voice(pattern: String) const

Returns the index of the first voice whose name contains pattern, ignoring case, or -1 if pattern is empty or no voice matches.

int find_voice_by_language(prefix: String) const

Returns the index of the first voice whose language begins with prefix, ignoring case, or -1 if prefix is empty or no voice matches. "en" matches "en-US" and "en-GB".

String get_voice_language(index: int) const

Returns the language of the voice at index, usually a BCP 47 tag such as "en-US". Returns an empty String if the backend does not support voices, index is out of range, or the query fails.

String get_voice_name(index: int) const

Returns the name of the voice at index, such as "Microsoft David". Returns an empty String if the backend does not support voices, index is out of range, or the query fails.

Dictionary[] get_voices() const

Returns a Dictionary for each voice, in index order, with the keys "index", "name", and "language". Returns an empty array if the backend does not support voices.

bool has_feature(flag: PrismBackendFeature) const

Returns true if the backend has every feature in flag. The backend is queried on each call.

if backend.has_feature(GodotPrismBackend.PRISM_BACKEND_SUPPORTS_IS_SPEAKING):
    print(backend.speaking)

bool is_valid() const

Returns true if the object refers to a backend. A backend that failed to initialize is valid; its methods return PRISM_ERROR_NOT_INITIALIZED.

PrismError output(text: String, interrupt: bool = false)

Presents text through speech and braille. A screen reader backend speaks the text and shows it on the braille display; a speech engine speaks it. interrupt has the same meaning as for speak().

Returns PRISM_ERROR_INVALID_PARAM if text is empty. May return PRISM_OK when one of speech and braille fails.

PrismError pause()

Pauses speech. Queued speech is kept, and resume() continues from the same position. While paused, speaking is false.

Returns PRISM_ERROR_NOT_SPEAKING if no speech is in progress, PRISM_ERROR_ALREADY_PAUSED if speech is already paused, and PRISM_ERROR_NOT_IMPLEMENTED if the backend does not support pausing.

PrismError refresh_voices()

Reloads the list of voices from the system. Voices installed while the project runs appear only after a refresh. Voice indices may change, so a stored choice of voice should be identified by name, for example with find_voice().

Returns PRISM_ERROR_NOT_IMPLEMENTED if the backend does not support voices, as no screen reader does.

PrismError resume()

Resumes speech paused by pause(). Returns PRISM_ERROR_NOT_PAUSED if speech is not paused, including after stop(), and PRISM_ERROR_NOT_IMPLEMENTED if the backend does not support resuming.

PrismError speak(text: String, interrupt: bool = false)

Speaks text with the current voice, rate, pitch, and volume. Depending on the backend, returns when speech starts or when it ends.

If interrupt is true, speech in progress is stopped and queued speech is discarded first. If false, most backends queue the text; some mix it with speech in progress, and screen readers may apply their own setting.

Returns PRISM_ERROR_INVALID_PARAM if text is empty, PRISM_ERROR_NOT_IMPLEMENTED if the backend does not support speech, and PRISM_ERROR_SPEAK_FAILURE if synthesis fails. Some backends limit the length of text, so long text should be divided.

var err := backend.speak("Hello from Prism.", true)
if err != GodotPrismBackend.PRISM_OK:
    push_error("Speech failed: %s" % Prism.get_error_string(err))

PrismError speak_to_memory(text: String, callback: Callable)

Synthesizes text and calls callback once with the audio: a PackedFloat32Array of interleaved samples from -1.0 to 1.0, the number of channels, and the sample rate in hertz. callback is called on the calling thread before the method returns, and is not called if synthesis fails. The method may take noticeable time for long text.

Returns PRISM_ERROR_INVALID_PARAM if text is empty or callback is invalid, and PRISM_ERROR_NOT_IMPLEMENTED if the backend does not support synthesis to memory, as no screen reader does. See also speak_to_stream().

AudioStreamWAV speak_to_stream(text: String)

Synthesizes text and returns the audio as a 16-bit AudioStreamWAV, which can be played through an AudioStreamPlayer or a positional player.

Returns null if text is empty, the backend does not support synthesis to memory, synthesis fails, or no audio is produced. Failures other than empty text and empty audio are printed. The stream is stereo if the backend produces more than one channel; more than two channels are not represented correctly.

var stream := backend.speak_to_stream("Behind you.")
if stream != null:
    $AudioStreamPlayer3D.stream = stream
    $AudioStreamPlayer3D.play()

PrismError stop()

Stops speech and discards queued speech. A screen reader backend stops only speech it produced.

Returns PRISM_ERROR_NOT_IMPLEMENTED if the backend does not support stopping. Some backends return PRISM_ERROR_NOT_SPEAKING if no speech is in progress; most return PRISM_OK.