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
| Message | Meaning |
|---|---|
libdbus-1.so.3: cannot open shared object file | A 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 found | The 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 found | The 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:
| Backend | Host requirement | Bridged capabilities |
|---|---|---|
| Orca | A usable desktop session bus and a running Orca release exposing its remote-control speech interface. | Speech, combined output, and stop. |
| Speech Dispatcher | A 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. |
| Spiel | A 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
Prismfollowed 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,
falseis returned when errors occur. - For functions returning
size_t,SIZE_MAXis returned on error. - For functions returning signed integers,
-1is returned on error. - For functions returning pointers or character strings,
NULLis returned on error. - For functions returning
PrismBackendId,PRISM_BACKEND_INVALIDis 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:
- The vtable passed to
prism_registry_builder_add_backendis 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. - If a
userdata_freefunction is supplied, ownership ofuserdatatransfers to Prism the momentprism_registry_builder_add_backendis called, and Prism invokesuserdata_freeexactly once, regardless of whether registration succeeds. Ifuserdata_freeisNULL, the caller retains ownership and MUST keepuserdatavalid 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_initandprism_shutdownare thread-safe. Multiple threads MAY callprism_initconcurrently; each call returns an independent context. However,prism_shutdownMUST only be called once per context, and the context MUST NOT be used from any thread afterprism_shutdownhas been called on that context. If one thread callsprism_shutdownwhile 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, forprism_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_initializeon 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, andprism_registry_getconsist 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_acquireandprism_registry_acquire_bestconsist 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 onprism_registry_acquireorprism_registry_acquire_bestbeing 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
PrismBackendinstance from multiple threads concurrently without external synchronization. This restriction applies even to logically independent operations; for example, callingprism_backend_get_ratefrom one thread while another thread callsprism_backend_set_volumeon 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, andprism_registry_acquire_bestfunctions 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_memoryMAY 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_acquireorprism_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
PrismRegistryBuilderis 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_retainandprism_registry_releaseare thread-safe. Note that the finalprism_registry_release(or the finalprism_shutdownon a context bound to the registry, whichever comes last) finalizes the registry on the calling thread; anyuserdata_freefunctions 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
userdatawhen nocreatefunction is supplied) MUST be synchronized by the implementation. - The functions
prism_availability_poll_pauseandprism_availability_poll_resumeare 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 throughPrismConfigare 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 callprism_shutdownon the owning context. The single-threaded backend constraint continues to apply to any backend instance the callback creates or acquires.prism_availability_auto_power_supportedis thread-safe and MAY be called at any time. - The logging functions
prism_set_log_handler,prism_set_log_level,prism_log, andprism_log_flushare thread-safe and MAY be called from any thread concurrently, including beforeprism_initand afterprism_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_shutdownis 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.
| Error | Value | Description |
|---|---|---|
PRISM_OK | 0 | Operation completed without error |
PRISM_ERROR_NOT_INITIALIZED | 1 | The backend was not initialized; prism_backend_initialize was not called or failed |
PRISM_ERROR_INVALID_PARAM | 2 | An invalid parameter was passed to a function |
PRISM_ERROR_NOT_IMPLEMENTED | 3 | The operation is not supported by the selected backend |
PRISM_ERROR_NO_VOICES | 4 | No voices are available for this backend |
PRISM_ERROR_VOICE_NOT_FOUND | 5 | The specified voice was not found |
PRISM_ERROR_SPEAK_FAILURE | 6 | Speech synthesis failed |
PRISM_ERROR_MEMORY_FAILURE | 7 | Memory allocation failed |
PRISM_ERROR_RANGE_OUT_OF_BOUNDS | 8 | A parameter value exceeded its valid range |
PRISM_ERROR_INTERNAL | 9 | An internal backend error occurred |
PRISM_ERROR_NOT_SPEAKING | 10 | Attempted to stop or pause when not speaking |
PRISM_ERROR_NOT_PAUSED | 11 | Attempted to resume when not paused |
PRISM_ERROR_ALREADY_PAUSED | 12 | Attempted to pause when already paused |
PRISM_ERROR_INVALID_UTF8 | 13 | A string parameter contained invalid UTF-8 |
PRISM_ERROR_INVALID_OPERATION | 14 | The operation is invalid in the current state |
PRISM_ERROR_ALREADY_INITIALIZED | 15 | Attempted to initialize an already-initialized backend |
PRISM_ERROR_BACKEND_NOT_AVAILABLE | 16 | The backend is not available on this system |
PRISM_ERROR_UNKNOWN | 17 | An unspecified error occurred |
PRISM_ERROR_INVALID_AUDIO_FORMAT | 18 | Either 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_EXCEEDED | 19 | The 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_STATE | 20 | An 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_FAILED | 21 | A 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_INVALID | 22 | A shared library was opened but does not export the plugin entry point |
PRISM_ERROR_INCOMPATIBLE_ABI | 23 | A 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.
| Constant | Backend |
|---|---|
PRISM_BACKEND_INVALID | Invalid/sentinel value (always 0) |
PRISM_BACKEND_SAPI | Microsoft SAPI (Windows) |
PRISM_BACKEND_AV_SPEECH | AVSpeechSynthesizer (macOS, iOS, tvOS, WatchOS, VisionOS) |
PRISM_BACKEND_VOICE_OVER | VoiceOver screen reader (macOS, MacCatalyst, iOS, WatchOS, tvOS, VisionOS) |
PRISM_BACKEND_SPEECH_DISPATCHER | Speech Dispatcher (Linux/BSD, win32 via Wine) |
PRISM_BACKEND_NVDA | NVDA screen reader (Windows) |
PRISM_BACKEND_JAWS | JAWS screen reader (Windows) |
PRISM_BACKEND_ONE_CORE | Windows OneCore speech API (Windows 10+) |
PRISM_BACKEND_ORCA | Orca screen reader (Linux/BSD, win32 via Wine) |
PRISM_BACKEND_ANDROID_TTS | Android TTS engine (Android) |
PRISM_BACKEND_ANDROID_SCREEN_READER | Android screen readers (Android) |
PRISM_BACKEND_WEB_SPEECH | Web SpeechSynthesis API (web) |
PRISM_BACKEND_UIA | UIAutomation backend (Windows only) |
PRISM_BACKEND_ZDSR | Zhengdu Screen Reader (Windows) |
PRISM_BACKEND_ZOOM_TEXT | ZoomText (Windows) |
PRISM_BACKEND_BOY_PC_READER | BoyPCReader (windows only) |
PRISM_BACKEND_PC_TALKER | PCTalker (windows only) |
PRISM_BACKEND_SENSE_READER | Sense Reader screen reader (Windows) |
PRISM_BACKEND_SYSTEM_ACCESS | SystemAccess screen reader (windows) (only available if explicitly enabled at build time) |
PRISM_BACKEND_WINDOW_EYES | WindowEyes screen reader (windows) (only available if explicitly enabled at build time) |
PRISM_BACKEND_GDK_SPEECH | Microsoft GDK speech synthesis (Windows, Xbox) |
PRISM_BACKEND_SPIEL | Spiel (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_createorprism_registry_create_best, callingprism_backend_freeimmediately destroys the backend and releases all associated resources. After the call returns, thebackendpointer is invalid and MUST NOT be used. - For backends obtained via
prism_registry_acquireorprism_registry_acquire_best, the backend uses reference counting. Callingprism_backend_freedecrements 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 toprism_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:
| Bit | Description |
|---|---|
PRISM_BACKEND_IS_SUPPORTED_AT_RUNTIME | The underlying engine or service is available. This determination is advisory; prism_backend_initialize MAY still fail. |
PRISM_BACKEND_SUPPORTS_SPEAK | prism_backend_speak is implemented. |
PRISM_BACKEND_SUPPORTS_SPEAK_TO_MEMORY | prism_backend_speak_to_memory is implemented. |
PRISM_BACKEND_SUPPORTS_BRAILLE | prism_backend_braille is implemented. |
PRISM_BACKEND_SUPPORTS_OUTPUT | prism_backend_output is implemented. |
PRISM_BACKEND_SUPPORTS_IS_SPEAKING | prism_backend_is_speaking is implemented. |
PRISM_BACKEND_SUPPORTS_STOP | prism_backend_stop is implemented. |
PRISM_BACKEND_SUPPORTS_PAUSE | prism_backend_pause is implemented. |
PRISM_BACKEND_SUPPORTS_RESUME | prism_backend_resume is implemented. |
PRISM_BACKEND_SUPPORTS_SET_VOLUME | prism_backend_set_volume is implemented. |
PRISM_BACKEND_SUPPORTS_GET_VOLUME | prism_backend_get_volume is implemented. |
PRISM_BACKEND_SUPPORTS_SET_RATE | prism_backend_set_rate is implemented. |
PRISM_BACKEND_SUPPORTS_GET_RATE | prism_backend_get_rate is implemented. |
PRISM_BACKEND_SUPPORTS_SET_PITCH | prism_backend_set_pitch is implemented. |
PRISM_BACKEND_SUPPORTS_GET_PITCH | prism_backend_get_pitch is implemented. |
PRISM_BACKEND_SUPPORTS_REFRESH_VOICES | prism_backend_refresh_voices is implemented. |
PRISM_BACKEND_SUPPORTS_COUNT_VOICES | prism_backend_count_voices is implemented. |
PRISM_BACKEND_SUPPORTS_GET_VOICE_NAME | prism_backend_get_voice_name is implemented. |
PRISM_BACKEND_SUPPORTS_GET_VOICE_LANGUAGE | prism_backend_get_voice_language is implemented. |
PRISM_BACKEND_SUPPORTS_GET_VOICE | prism_backend_get_voice is implemented. |
PRISM_BACKEND_SUPPORTS_SET_VOICE | prism_backend_set_voice is implemented. |
PRISM_BACKEND_SUPPORTS_GET_CHANNELS | prism_backend_get_channels is implemented. |
PRISM_BACKEND_SUPPORTS_GET_SAMPLE_RATE | prism_backend_get_sample_rate is implemented. |
PRISM_BACKEND_SUPPORTS_GET_BIT_DEPTH | prism_backend_get_bit_depth is implemented. |
PRISM_BACKEND_PERFORMS_SILENCE_TRIMMING_ON_SPEAK | The backend trims leading and trailing silence from each utterance before playing it. |
PRISM_BACKEND_PERFORMS_SILENCE_TRIMMING_ON_SPEAK_TO_MEMORY | The backend trims leading and trailing silence from the audio stream before delivering it to the audio callback. |
PRISM_BACKEND_SUPPORTS_SPEAK_SSML | Reserved. |
PRISM_BACKEND_SUPPORTS_SPEAK_TO_MEMORY_SSML | Reserved. |
PRISM_BACKEND_IS_SCREEN_READER | The backend sends messages to a screen reader. |
PRISM_BACKEND_IS_SPEECH_ENGINE | The 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
| Value | Meaning |
|---|---|
PRISM_OK | Initialization succeeded. |
PRISM_ERROR_ALREADY_INITIALIZED | The backend was already initialized. |
PRISM_ERROR_BACKEND_NOT_AVAILABLE | The backend’s underlying system component is unavailable. |
PRISM_ERROR_INTERNAL | An internal error occurred during initialization. |
PRISM_ERROR_MEMORY_FAILURE | Memory allocation failed during initialization. |
PRISM_ERROR_UNKNOWN | An unspecified error occurred. |
PRISM_ERROR_INTERNAL_BACKEND_LIMIT_EXCEEDED | The 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
| Value | Meaning |
|---|---|
PRISM_OK | Speech synthesis was successfully initiated. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_INVALID_UTF8 | text contains invalid UTF-8 sequences. |
PRISM_ERROR_SPEAK_FAILURE | Speech synthesis failed. |
PRISM_ERROR_BACKEND_NOT_AVAILABLE | The backend’s system component is no longer available. |
PRISM_ERROR_NOT_IMPLEMENTED | The 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
interruptistrue, any current speech is stopped immediately (equivalent to callingprism_backend_stop) before the new speech begins. The new speech will be the only speech playing. - If
interruptisfalse, 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
| Value | Meaning |
|---|---|
PRISM_OK | Synthesis completed and audio was delivered to the callback. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_INVALID_UTF8 | text contains invalid UTF-8 sequences. |
PRISM_ERROR_NOT_IMPLEMENTED | The backend does not support memory synthesis. |
PRISM_ERROR_INTERNAL | An internal error occurred during synthesis. |
PRISM_ERROR_INVALID_AUDIO_FORMAT | The 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
| Value | Meaning |
|---|---|
PRISM_OK | Text was successfully sent to the braille display. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_INVALID_UTF8 | text contains invalid UTF-8 sequences. |
PRISM_ERROR_NOT_IMPLEMENTED | The backend does not support braille output. |
PRISM_ERROR_BACKEND_NOT_AVAILABLE | The backend’s system component is no longer available. |
PRISM_ERROR_INTERNAL | An 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
| Value | Meaning |
|---|---|
PRISM_OK | Output was successfully initiated. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_INVALID_UTF8 | text contains invalid UTF-8 sequences. |
PRISM_ERROR_NOT_IMPLEMENTED | The backend does not support this operation. |
PRISM_ERROR_SPEAK_FAILURE | Output 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
| Value | Meaning |
|---|---|
PRISM_OK | Speech was stopped or no speech was playing. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_NOT_IMPLEMENTED | The backend does not support stopping. |
PRISM_ERROR_INTERNAL | An 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
| Value | Meaning |
|---|---|
PRISM_OK | Speech was paused. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_NOT_SPEAKING | No speech is currently playing. |
PRISM_ERROR_ALREADY_PAUSED | Speech is already paused. |
PRISM_ERROR_NOT_IMPLEMENTED | The backend does not support pausing. |
PRISM_ERROR_INTERNAL | An 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
| Value | Meaning |
|---|---|
PRISM_OK | Speech was resumed. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_NOT_PAUSED | Speech is not currently paused. |
PRISM_ERROR_NOT_IMPLEMENTED | The backend does not support resuming. |
PRISM_ERROR_INTERNAL | An 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
| Value | Meaning |
|---|---|
PRISM_OK | The speaking state was retrieved. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_NOT_IMPLEMENTED | The 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:
- 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.
- 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_inithas 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 onPrismAvailabilityBaselineCallbackdescribes how an application detects this condition. - 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.
- 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.
- 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:
- 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_RUNTIMEin the result ofprism_backend_get_features, and retains a decision derived from that observation, such as which backend to use. - 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.
- A custom backend is a backend implemented by the application and described to Prism by a table of function pointers of type
PrismBackendVTable. - A registration is the record Prism retains of one successful call to
prism_registry_builder_add_backend, comprising the copied vtable, theuserdatapointer, theuserdata_freefunction, the declared feature set, and the backend’s name, identifier, and priority. - 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. - 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.
- 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:
- It creates a builder with
prism_registry_builder_new. - Registrations can then be added with
prism_registry_builder_add_backend. - Once the builder has been populated with all registrations the application wishes to perform, it freezes the builder with
prism_registry_freeze. - The registry is bound to one or more contexts through the
registryfield ofPrismConfig.
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, andset_pitchis finite and lies within[0.0, 1.0]. A call supplying any other value fails withPRISM_ERROR_RANGE_OUT_OF_BOUNDS. - No member other than
initialize,create,destroy, andis_supportedis invoked before an invocation ofinitializehas succeeded on the instance in question (or, if theinitializemember is null, before initialization has trivially succeeded). A call arriving earlier fails withPRISM_ERROR_NOT_INITIALIZED. initializeis not invoked on an instance whose initialization has already succeeded. Such a call fails withPRISM_ERROR_ALREADY_INITIALIZED.pauseis not invoked on an instance Prism considers paused, andresumeis not invoked on an instance Prism does not consider paused. Such calls fail withPRISM_ERROR_ALREADY_PAUSEDandPRISM_ERROR_NOT_PAUSEDrespectively. An instance is considered paused after a successfulpause, and ceases to be considered paused after a successfulresumeor a successfulstop.
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.
- A vtable function MUST return a defined
PrismErrorvalue. Prism substitutesPRISM_ERROR_UNKNOWNfor any return value outside the defined range rather than propagate it to the application. - 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. speak_to_memoryMUST 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.- Values reported through
get_volume,get_rate, andget_pitchSHOULD lie within[0.0, 1.0], and audio samples delivered through thespeak_to_memorycallback 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 withPRISM_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. - 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
| Value | Meaning |
|---|---|
PRISM_OK | The backend was added to the builder. |
PRISM_ERROR_INVALID_PARAM | The 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_UTF8 | name contains invalid UTF-8 sequences. |
PRISM_ERROR_INVALID_OPERATION | The builder is spent, or a backend with the same name or the same identifier is already present in the builder. |
PRISM_ERROR_MEMORY_FAILURE | Memory 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:
- It creates a builder with
prism_registry_builder_new, or reuses one it is still populating. - It calls
prism_registry_builder_add_library, naming the shared library. - Once the builder holds every backend the application wishes to register, it freezes the builder with
prism_registry_freeze. - The registry is bound to one or more contexts through the
registryfield ofPrismConfig.
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
| Value | Meaning |
|---|---|
PRISM_OK | The plugin was loaded and all of its backends were added to the builder. |
PRISM_ERROR_INVALID_PARAM | priority_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_UTF8 | path, or a descriptor’s name, contains invalid UTF-8. |
PRISM_ERROR_LIBRARY_LOAD_FAILED | The 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_INVALID | The library was opened but does not export prism_plugin_query. |
PRISM_ERROR_INCOMPATIBLE_ABI | The 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_OPERATION | The 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_INTERNAL | An internal error occurred while trying to perform a loader operation. |
PRISM_ERROR_UNKNOWN | An 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
| Value | Meaning |
|---|---|
PRISM_OK | Volume was set. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_RANGE_OUT_OF_BOUNDS | volume is outside [0.0, 1.0]. |
PRISM_ERROR_NOT_IMPLEMENTED | The backend does not support volume control. |
PRISM_ERROR_INTERNAL | An 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
| Value | Meaning |
|---|---|
PRISM_OK | Volume was retrieved. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_NOT_IMPLEMENTED | The 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
| Value | Meaning |
|---|---|
PRISM_OK | Rate was set. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_RANGE_OUT_OF_BOUNDS | rate is outside [0.0, 1.0]. |
PRISM_ERROR_NOT_IMPLEMENTED | The backend does not support rate control. |
PRISM_ERROR_INTERNAL | An 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
| Value | Meaning |
|---|---|
PRISM_OK | Rate was retrieved. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_NOT_IMPLEMENTED | The 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
| Value | Meaning |
|---|---|
PRISM_OK | Pitch was set. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_RANGE_OUT_OF_BOUNDS | pitch is outside [0.0, 1.0]. |
PRISM_ERROR_NOT_IMPLEMENTED | The backend does not support pitch control. |
PRISM_ERROR_INTERNAL | An 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
| Value | Meaning |
|---|---|
PRISM_OK | Pitch was retrieved. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_NOT_IMPLEMENTED | The 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
| Value | Meaning |
|---|---|
PRISM_OK | Voice list was refreshed. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_NOT_IMPLEMENTED | The backend does not support voice enumeration. |
PRISM_ERROR_INTERNAL | An 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
| Value | Meaning |
|---|---|
PRISM_OK | Voice count was retrieved. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_NOT_IMPLEMENTED | The 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
| Value | Meaning |
|---|---|
PRISM_OK | Voice name was retrieved. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_RANGE_OUT_OF_BOUNDS | voice_id is out of range. |
PRISM_ERROR_NOT_IMPLEMENTED | The 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
| Value | Meaning |
|---|---|
PRISM_OK | Language was retrieved. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_RANGE_OUT_OF_BOUNDS | voice_id is out of range. |
PRISM_ERROR_NOT_IMPLEMENTED | The 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
| Value | Meaning |
|---|---|
PRISM_OK | Voice was selected. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_RANGE_OUT_OF_BOUNDS | voice_id is out of range. |
PRISM_ERROR_VOICE_NOT_FOUND | The voice does not exist. |
PRISM_ERROR_NOT_IMPLEMENTED | The backend does not support voice selection. |
PRISM_ERROR_BACKEND_ENTERED_UNDEFINED_STATE | An 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
| Value | Meaning |
|---|---|
PRISM_OK | Voice index was retrieved. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_NOT_IMPLEMENTED | The backend does not support voice selection. |
PRISM_ERROR_INTERNAL | An 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
| Value | Meaning |
|---|---|
PRISM_OK | Channel count was retrieved. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_NOT_IMPLEMENTED | The 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
| Value | Meaning |
|---|---|
PRISM_OK | Sample rate was retrieved. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_NOT_IMPLEMENTED | The 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
| Value | Meaning |
|---|---|
PRISM_OK | Bit depth was retrieved. |
PRISM_ERROR_NOT_INITIALIZED | The backend has not been initialized. |
PRISM_ERROR_NOT_IMPLEMENTED | The 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
SystemParametersInfowithSPI_GETSCREENREADER, is set. UiaClientsAreListeningreturnsTRUE, 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:
IsWindowreturnsTRUE.IsWindowVisiblereturnsTRUE.IsIconicreturnsFALSE.- The window is owned by the host process.
- The window is not styled with the extended styles
WS_EX_TOOLWINDOWorWS_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_shutdownandprism_backend_free. A reference to aBackendMUST 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
loggingmodule. - 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
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
If no backend is at the position or has the name.
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
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
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
If the registry does not contain the backend, or the backend cannot be created.
If a component the backend requires is missing or not running.
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
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
If the registry does not contain the backend, or the backend cannot be created.
If a component the backend requires is missing or not running.
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
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
If raw_ptr is NULL.
If a component the backend requires is missing or not running.
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
If text is empty or contains a NUL
character.
If text cannot be encoded as UTF-8.
If the backend does not support speech.
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
If text is empty or contains a NUL
character.
If text cannot be encoded as UTF-8.
If the backend does not support synthesis to memory. No screen reader backend supports it.
If the backend produces audio Prism cannot convert.
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
If text is empty or contains a NUL
character.
If text cannot be encoded as UTF-8.
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
If text is empty or contains a NUL
character.
If text cannot be encoded as UTF-8.
If the backend does not support output.
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
If the backend does not support stopping.
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
If the backend does not support pausing.
If no speech is in progress.
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
If the backend does not support resuming.
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
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
If idx is out of range.
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
If idx is out of range.
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
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
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
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
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
Unless overridden.
Remarks
Implements prism.Backend.stop.
CustomBackend.pause
Pauses speech.
Syntax
def pause() -> None
Exceptions
Unless overridden.
Remarks
Implements prism.Backend.pause.
CustomBackend.resume
Resumes paused speech.
Syntax
def resume() -> None
Exceptions
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
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
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
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
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
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
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
Unless overridden.
Remarks
Implements prism.Backend.pitch.
CustomBackend.refresh_voices
Reloads the list of voices.
Syntax
def refresh_voices() -> None
Exceptions
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
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
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
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
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
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
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
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
Unless overridden.
Remarks
Implements prism.Backend.bit_depth.
prism.RegistryBuilder
Builds a registry.
Syntax
class RegistryBuilder()
Exceptions
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
If backend is not a subclass of CustomBackend.
If the builder is spent or closed.
If EXTRA_FEATURES sets a bit that corresponds to a
method.
If name is empty, priority is
negative, or both classification bits are set.
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
If the builder is spent or closed.
If path is not a str or a path-like object yielding
one.
If path is empty or contains a
NUL character, or the library describes an inconsistent backend.
If priority_override is negative.
If the platform does not support shared library backends.
If the library cannot be opened.
If the library is not a Prism plugin.
If the plugin is incompatible with this version of Prism.
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
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
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
PrismErrorvalue. 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_streamsynthesizes speech to anAudioStreamWAV.
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
| Type | Name |
|---|---|
int | backends_count |
Methods
| Returns | Method |
|---|---|
GodotPrismBackend | acquire(id: int) |
GodotPrismBackend | acquire_best() |
GodotPrismBackend | create(id: int) |
GodotPrismBackend | create_best() |
void | flush_log() static |
Dictionary[] | get_available_backends() const |
GodotPrismBackend | get_backend(id: int) |
String | get_error_string(error: PrismError) static |
int | get_version() static |
String | get_version_string() static |
bool | has(id: int) const |
int | id_at(index: int) const |
int | id_of(name: String) const |
bool | is_auto_power_management_supported() static |
bool | is_availability_monitoring() const |
bool | is_backend_available(id: int) |
bool | is_log_signal_enabled() const |
PrismError | load_backend_library(path: String, priority_override: int = -1) |
String | name_of(id: int) const |
void | pause_availability_monitoring() |
int | priority_of(id: int) const |
void | resume_availability_monitoring() |
PrismLogLevel | set_log_level(level: PrismLogLevel) static |
void | set_log_signal_enabled(enabled: bool) |
PrismError | start_availability_monitoring(poll_interval_ms: int = 0, debounce_samples: int = 0, backoff_max_ms: int = 0, auto_power_manage: bool = true) |
void | stop_availability_monitoring() |
PrismError | write_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
| Type | Name |
|---|---|
int | bit_depth |
int | channels |
PrismBackendFeature | features |
String | name |
float | pitch |
float | rate |
int | sample_rate |
bool | speaking |
int | voice |
int | voices_count |
float | volume |
Methods
| Returns | Method |
|---|---|
PrismError | braille(text: String) |
int | find_voice(pattern: String) const |
int | find_voice_by_language(prefix: String) const |
String | get_voice_language(index: int) const |
String | get_voice_name(index: int) const |
Dictionary[] | get_voices() const |
bool | has_feature(flag: PrismBackendFeature) const |
bool | is_valid() const |
PrismError | output(text: String, interrupt: bool = false) |
PrismError | pause() |
PrismError | refresh_voices() |
PrismError | resume() |
PrismError | speak(text: String, interrupt: bool = false) |
PrismError | speak_to_memory(text: String, callback: Callable) |
AudioStreamWAV | speak_to_stream(text: String) |
PrismError | stop() |
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.