A cross-platform text-to-speech library with C interface, written in Rust.
It reverse-engineers the awesome Microsoft Edge Read aloud feature, allowing you to use it in your own projects in your preferred programming language.
Read aloud is a low-level library that is designed as a building block for higher-level text-to-speech libraries and applications.
This crate counterfeits the communication protocol used by Microsoft Edge to work with the Read Aloud service. Any change to the upstream Edge service can break the crate, even if the public C ABI stays the same. If the service updates its protocol, authentication, or required headers, the library may need internal changes to restore functionality.
This library connects to the Edge read-aloud service over secure WebSockets. TLS support is currently provided by tungstenite with its native-tls feature enabled.
Linux
On Linux, `native-tls` uses the system OpenSSL installation, so building this crate requires OpenSSL development files and `pkg-config` to be installed.For Debian or Ubuntu:
sudo apt-get update
sudo apt-get install libssl-dev pkg-configWindows/macOS
On Windows and macOS, `native-tls` uses the platform TLS stack instead of OpenSSL, so this Linux-specific package installation is usually not required.Add the crate to your Rust project:
cargo add read-aloudThe crate builds as a normal Rust library, so cargo build is enough when you only need the Rust API. For usage, see the API Reference and Examples sections below.
Build the shared library and generated header with Cargo:
cargo build --releaseThis crate is configured to produce a shared library (cdylib) and a Rust library (rlib). After a release build, the generated artifacts are placed in Cargo's target directory. By default this is target/release/ for the host platform, or target/<target-triple>/release/ for cross-compilation.
The C integration artifacts are:
- Shared library:
libread_aloud.soon Linux,libread_aloud.dylibon macOS, orread_aloud.dllon Windows - Generated header:
read_aloud.h
Link the shared library from your C or C++ project and include the generated header from the same target directory.
The generated header includes API comments sourced from the Rust rustdoc on the exported FFI items, so the Rust sources are the canonical reference for the C ABI.
The library exposes both a Rust API and a C ABI for text-to-speech generation.
See the full API documentation at: https://docs.rs/crate/read-aloud/latest
C
// Load the read_aloud shared library and resolve the exported symbols before running this example.
#include <stdio.h>
#include "read_aloud.h"
int main(void) {
ReadAloudStatus status = read_aloud_text_to_speech(
"Hello, World!",
VOICE_EN_GB_THOMAS_NEURAL,
NULL,
"output.mp3"
);
if (status != READ_ALOUD_STATUS_SUCCESS) {
fprintf(stderr, "TTS failed: %s\n", read_aloud_status_string(status));
fprintf(stderr, "details: %s\n", read_aloud_last_error_message());
return 1;
}
ReadAloudSpeechOptions options;
status = read_aloud_speech_options_init(&options);
if (status != READ_ALOUD_STATUS_SUCCESS) {
fprintf(stderr, "Failed to initialize options: %s\n", read_aloud_last_error_message());
return 1;
}
options.rate = 0.2f;
status = read_aloud_text_to_speech(
"Custom rate!",
VOICE_EN_GB_THOMAS_NEURAL,
&options,
"custom_output.mp3"
);
if (status != READ_ALOUD_STATUS_SUCCESS) {
fprintf(stderr, "TTS failed: %s\n", read_aloud_status_string(status));
fprintf(stderr, "details: %s\n", read_aloud_last_error_message());
return 1;
}
printf("Both TTS calls succeeded.\n");
return 0;
}Rust
use std::path::Path;
use read_aloud::{text_to_speech, SpeechOptions, Voice};
fn main() -> Result<(), Box<dyn std::error::Error>> {
text_to_speech(
"Hello, World!",
Voice::en_GB_ThomasNeural,
SpeechOptions::default(),
Path::new("output.mp3"),
)?;
let mut options = SpeechOptions::default();
options.rate = 0.2;
text_to_speech(
"Custom rate!",
Voice::en_GB_ThomasNeural,
options,
Path::new("custom_output.mp3"),
)?;
Ok(())
}C++
// Load the read_aloud shared library and resolve the exported symbols before running this example.
#include <iostream>
#include "read_aloud.h"
int main() {
auto status = read_aloud_text_to_speech("Hello, World!", VOICE_EN_GB_THOMAS_NEURAL, nullptr, "output.mp3");
if (status != READ_ALOUD_STATUS_SUCCESS) {
std::cerr << "TTS failed: " << read_aloud_status_string(status) << std::endl;
std::cerr << "details: " << read_aloud_last_error_message() << std::endl;
return 1;
}
ReadAloudSpeechOptions options;
status = read_aloud_speech_options_init(&options);
if (status != READ_ALOUD_STATUS_SUCCESS) {
std::cerr << "Failed to initialize options: " << read_aloud_last_error_message() << std::endl;
return 1;
}
options.rate = 0.2f;
status = read_aloud_text_to_speech("Custom rate!", VOICE_EN_GB_THOMAS_NEURAL, &options, "custom_output.mp3");
if (status != READ_ALOUD_STATUS_SUCCESS) {
std::cerr << "TTS failed: " << read_aloud_status_string(status) << std::endl;
std::cerr << "details: " << read_aloud_last_error_message() << std::endl;
return 1;
}
std::cout << "Both TTS calls succeeded." << std::endl;
return 0;
}Python
import ctypes
# Load the read_aloud shared library before running this example.
# Assume `lib` is an already-loaded ctypes.CDLL instance.
class ReadAloudSpeechOptions(ctypes.Structure):
_fields_ = [
("size", ctypes.c_uint32),
("pitch_hz", ctypes.c_int32),
("rate", ctypes.c_float),
("volume", ctypes.c_float),
]
lib.read_aloud_speech_options_init.argtypes = [ctypes.POINTER(ReadAloudSpeechOptions)]
lib.read_aloud_speech_options_init.restype = ctypes.c_int
lib.read_aloud_text_to_speech.argtypes = [
ctypes.c_char_p,
ctypes.c_int,
ctypes.POINTER(ReadAloudSpeechOptions),
ctypes.c_char_p,
]
lib.read_aloud_text_to_speech.restype = ctypes.c_int
lib.read_aloud_status_string.argtypes = [ctypes.c_int]
lib.read_aloud_status_string.restype = ctypes.c_char_p
lib.read_aloud_last_error_message.argtypes = []
lib.read_aloud_last_error_message.restype = ctypes.c_char_p
VOICE_EN_GB_THOMAS_NEURAL = 110
def print_error(status: int) -> None:
print("status:", lib.read_aloud_status_string(status).decode())
print("details:", lib.read_aloud_last_error_message().decode())
result = lib.read_aloud_text_to_speech(
b"Hello, World!",
VOICE_EN_GB_THOMAS_NEURAL,
None,
b"output.mp3",
)
if result != 0:
print_error(result)
raise SystemExit(1)
options = ReadAloudSpeechOptions()
result = lib.read_aloud_speech_options_init(ctypes.byref(options))
if result != 0:
print_error(result)
raise SystemExit(1)
options.rate = ctypes.c_float(0.2)
result = lib.read_aloud_text_to_speech(
b"Custom rate!",
VOICE_EN_GB_THOMAS_NEURAL,
ctypes.byref(options),
b"custom_output.mp3",
)
if result != 0:
print_error(result)
raise SystemExit(1)
print("Both TTS calls succeeded.")