Skip to main content

speechd_module_sys/
lib.rs

1//!# Low-level binding for `libspeechd_module`
2//!
3//! To use this module you must implement at least the following functions
4//! *as global symbols* in your own code and not doing so will result in
5//! linking errors (see each function for details):
6//!
7//!  * [`module_config`]
8//!  * [`module_init`]
9//!  * [`module_list_voices`]
10//!  * [`module_speak`] or [`module_speak_sync`]
11//!  * [`module_pause`]
12//!  * [`module_stop`]
13//!  * [`module_close`]
14//!
15//! In addition you must also implement the following functions, unless you
16//! compile and link against
17//! [`module_utils.c`](https://github.com/brailcom/speechd/blob/master/src/modules/module_utils.c)
18//! from the speech-dispatcher distribution (again see each function for
19//! details):
20//!
21//!  * [`module_loop`]
22//!  * [`module_set`]
23//!  * [`module_audio_set`]
24//!  * [`module_audio_init`]
25//!  * [`module_loglevel_set`]
26//!  * [`module_debug`]
27
28#[cfg(doc)]
29use std::io;
30use std::os::fd;
31use std::os::raw;
32
33pub mod audio;
34
35mod types;
36pub use types::*;
37
38
39// Detect missing feature flags, but only if this is *not* a documentation build
40#[cfg(
41	all(
42		any(
43			all(feature = "static", feature = "dynamic"),
44			all(not(feature = "static"), not(feature = "dynamic")),
45		),
46		not(doc),
47		not(speechd_module_doc),  // Workaround for `cargo doc` running check when documenating a dependant
48	),
49)]
50compile_error!("You must select either the `static` or `dynamic` linking feature for the `speechd-module-sys` crate");
51
52
53// Functions to be provided by the module
54unsafe extern "C" {
55	/// Read the module configuration from the given optional/nullable path,
56	/// called at startup
57	///
58	/// Returning a non-zero code from the function will abort module startup.
59	///
60	/// Your module implementation *must* export it’s own version of this
61	/// function as:
62	///
63	/// ```
64	/// #[unsafe(no_mangle)]
65	/// unsafe extern "C" fn module_config(configfile: *const raw::c_char) -> raw::c_int {
66	///     0  // Stub implementation
67	/// }
68	/// ```
69	#[must_use]
70	pub unsafe fn module_config(configfile: *const raw::c_char) -> raw::c_int;
71
72	/// Called after server sends the `INIT` command
73	///
74	/// `msg` is an optional out-pointer to a [`malloc`](libc::malloc)’d
75	/// C string with your response.
76	///
77	/// Returning a non-zero code from the function will abort module startup.
78	///
79	/// Your module implementation *must* export it’s own version of this
80	/// function as:
81	///
82	/// ```
83	/// #[unsafe(no_mangle)]
84	/// unsafe extern "C" fn module_init(msg: *mut *const raw::c_char) -> raw::c_int {
85	///     0  // Stub implementation
86	/// }
87	/// ```
88	#[must_use]
89	pub unsafe fn module_init(msg: *mut *const raw::c_char) -> raw::c_int;
90
91	/// List voices
92	///
93	/// Response is a `NULL`-terminated, [`malloc`](libc::malloc)’d array of
94	/// pointers to each configured module voice. See the [`Voice`] data
95	/// structure for the format of indivial voices.
96	///
97	/// Returning `NULL` from the function will abort module startup.
98	///
99	/// Your module implementation *must* export it’s own version of this
100	/// function as:
101	///
102	/// ```
103	/// # use std::ptr;
104	/// #[unsafe(no_mangle)]
105	/// extern "C" fn module_list_voices() -> *const *const Voice {
106	///     // Stub implementation
107	///     unsafe {
108	///         let res = libc::malloc(size_of<*const Voice>());
109	///         assert!(res != ptr::null_mut());
110	///         *res = ptr::null();
111	///         res
112	///     }
113	/// }
114	/// ```
115	#[must_use]
116	pub safe fn module_list_voices() -> *const *const Voice;
117
118	/// Asynchronous speak
119	///
120	/// Your module implementation *must* export it’s own version of *either*
121	/// [`module_speak_sync`] *or* this function as:
122	///
123	/// ```
124	/// #[unsafe(no_mangle)]
125	/// unsafe extern "C" fn module_speak(data: *const raw::c_char, len: usize, msgtype: MessageType) -> raw::c_int {
126	///     // Implementation
127	/// }
128	/// ```
129	#[must_use]
130	pub unsafe fn module_speak(data: *const raw::c_char, len: usize, msgtype: MessageType) -> raw::c_int;
131
132	/// Synchonous speak
133	///
134	/// Your module implementation *must* export it’s own version of *either*
135	/// [`module_speak`] *or* function as:
136	///
137	/// ```
138	/// #[unsafe(no_mangle)]
139	/// unsafe extern "C" fn module_speak_sync(data: *const raw::c_char, len: usize, msgtype: MessageType) -> raw::c_int {
140	///     // Implementation
141	/// }
142	/// ```
143	#[must_use]
144	pub unsafe fn module_speak_sync(data: *const raw::c_char, len: usize, msgtype: MessageType) -> raw::c_int;
145
146	/// Stop and drop the current utterance, preferably after reaching and
147	/// reporting the next `__spd_` index mark
148	///
149	/// Your module implementation *must* export it’s own version of this
150	/// function as:
151	///
152	/// ```
153	/// #[unsafe(no_mangle)]
154	/// extern "C" fn module_pause() -> usize {
155	///     // Implementation
156	/// }
157	/// ```
158	#[must_use]
159	pub safe fn module_pause() -> usize;
160
161	/// Immediately stop and drop the current utterance
162	///
163	/// Your module implementation *must* export it’s own version of this
164	/// function as:
165	///
166	/// ```
167	/// #[unsafe(no_mangle)]
168	/// extern "C" fn module_stop() -> raw::c_int {
169	///     // Implementation
170	/// }
171	/// ```
172	#[must_use]
173	pub safe fn module_stop() -> raw::c_int;
174
175	/// Called before exit
176	///
177	/// Your module implementation *must* export it’s own version of this
178	/// function as:
179	///
180	/// ```
181	/// #[unsafe(no_mangle)]
182	/// extern "C" fn module_close() -> raw::c_int {
183	///     // Implementation
184	/// }
185	/// ```
186	#[must_use]
187	pub safe fn module_close() -> raw::c_int;
188}
189
190
191// Functions that would be provided by `module_utils.c` but are not included
192// in the default distribution and hence unavailable unless you link with that
193// C file in your build
194unsafe extern "C" {
195	/// Called after init confirmed
196	///
197	/// This should invoke [`module_process`] to process SSIP commands from stdin.
198	///
199	/// Your module implementation *must* export it’s own version of this
200	/// function, unless your build system builds and links with its own
201	/// version of `module-utils.c` from the speech-dispatcher distribution.
202	///
203	/// ```
204	/// #[unsafe(no_mangle)]
205	/// unsafe extern "C" fn module_loop() -> raw::c_int {
206	///     // Minimal implementation
207	///     if modapi::module_process(io::stdin().as_fd(), 1) != 0 {
208	///         eprintln!("Standard input is end-of-stream");
209	///     }
210	///
211	///     0
212	/// }
213	/// ```
214	#[must_use]
215	pub safe fn module_loop() -> raw::c_int;
216
217	/// Set parameter, see the `SET` command of section 5.2.2 (“Communication
218	/// Protocol for Output Modules”) of the speech-dispatcher manual
219	/// for a list of standard properties
220	///
221	/// Your module implementation *must* export it’s own version of this
222	/// function, unless your build system builds and links with its own
223	/// version of `module-utils.c` from the speech-dispatcher distribution.
224	///
225	/// ```
226	/// #[unsafe(no_mangle)]
227	/// unsafe extern "C" fn module_set(var: *const raw::c_char, val: *const raw::c_char) -> raw::c_int {
228	///     // Implementation
229	/// }
230	/// ```
231	#[must_use]
232	pub unsafe fn module_set(var: *const raw::c_char, val: *const raw::c_char) -> raw::c_int;
233
234	/// Set audio parameter
235	///
236	/// Your module implementation *must* export it’s own version of this
237	/// function, unless your build system builds and links with its own
238	/// version of `module-utils.c` from the speech-dispatcher distribution.
239	///
240	/// Note that this function is only required when using client audio
241	/// playback. Implementations relying on server playback being available
242	/// may stub this.
243	///
244	/// ```
245	/// #[unsafe(no_mangle)]
246	/// unsafe extern "C" fn module_audio_set(var: *const raw::c_char, val: *const raw::c_char) -> raw::c_int {
247	///     -1  // Stub implementation
248	/// }
249	/// ```
250	#[must_use]
251	pub unsafe fn module_audio_set(var: *const raw::c_char, val: *const raw::c_char) -> raw::c_int;
252
253	/// Initialize audio
254	///
255	/// Your module implementation *must* export it’s own version of this
256	/// function, unless your build system builds and links with its own
257	/// version of `module-utils.c` from the speech-dispatcher distribution.
258	///
259	/// Note that this function is only required when using client audio
260	/// playback. Implementations relying on server playback being available
261	/// may stub this.
262	///
263	/// ```
264	/// # use std::ptr;
265	/// #[unsafe(no_mangle)]
266	/// unsafe extern "C" fn module_audio_init(status_info: *mut *mut raw::c_char) -> raw::c_int {
267	///     // Stub implementation
268	///     let resp = c"This speech-dispatcher module was compiled without client audio support";
269	///     unsafe {
270	///         let ptr = libc::malloc(size_of_val(resp));
271	///         assert!(ptr != ptr::null_mut());
272	///         libc::memcpy(ptr, resp.as_ptr(), size_of_val(resp));
273	///         *status_info = ptr;
274	///     }
275	///
276	///     -1
277	/// }
278	/// ```
279	#[must_use]
280	pub unsafe fn module_audio_init(status_info: *mut *mut raw::c_char) -> raw::c_int;
281
282	/// Set logging subsystem parameter
283	///
284	/// Generally `var` will only ever be the string “log_level” and `val` will
285	/// be an integer string in the range of 0 to 5, see
286	/// section 2.4.7 (“Log Levels”) of the speech-dispatcher manual for the
287	/// list of standard logging levels.
288	///
289	/// Your module implementation *must* export it’s own version of this
290	/// function, unless your build system builds and links with its own
291	/// version of `module-utils.c` from the speech-dispatcher distribution.
292	///
293	/// ```
294	/// #[unsafe(no_mangle)]
295	/// unsafe extern "C" fn module_loglevel_set(var: *const raw::c_char, val: *const raw::c_char) -> raw::c_int {
296	///     // Implementation
297	/// }
298	/// ```
299	#[must_use]
300	pub unsafe fn module_loglevel_set(var: *const raw::c_char, val: *const raw::c_char) -> raw::c_int;
301
302	/// Enable or disable debugging into the given file
303	///
304	/// The `enable` parameter is a boolean integer with values 0 (false) or
305	/// 1 (true) to disable or enable (respectively) verbose file debugging.
306	///
307	/// The `file` parameter is the NUL-terminated path where the debug file
308	/// should be written to.
309	///
310	/// Your module implementation *must* export it’s own version of this
311	/// function, unless your build system builds and links with its own
312	/// version of `module-utils.c` from the speech-dispatcher distribution.
313	///
314	/// ```
315	/// #[unsafe(no_mangle)]
316	/// unsafe extern "C" fn module_debug(enable: raw::c_int, file: *const raw::c_char) -> raw::c_int {
317	///     // Implementation
318	/// }
319	/// ```
320	#[must_use]
321	pub unsafe fn module_debug(enable: raw::c_int, file: *const raw::c_char) -> raw::c_int;
322}
323
324// There are provided by the module
325unsafe extern "C" {
326	/// Notify the library that the module will send its audio as wave events
327	/// to the server using [`module_tts_output_server`]
328	pub safe fn module_audio_set_server();
329
330	/// Forward an audio chunk to the server
331	///
332	/// Can be called as many times as desired, e.g. for each chunk produced by
333	/// the synthesizer.
334	pub unsafe fn module_tts_output_server(track: *const audio::Track, format: audio::Format);
335
336	/// Read and return one line of input read from the given file descriptor
337	///
338	/// The returned pointer must be `free`’d using [`libc::free`].
339	///
340	/// Since this function implements its own buffering, it must always be
341	/// called with the same input, usually simply stdin.
342	///
343	/// When `block` is set to 1, will wait until a line of input is received,
344	/// thus never returning [`ptr::null_mut()`](std::ptr::null_mut).
345	///
346	/// On I/O error or end-of-file, [`exit`](libc::exit) is called.
347	///
348	/// For Rust code it is recommended to only use this function with
349	/// [`io::stdin().as_fd()`](io::stdin) (since the required [`module_process`]
350	/// function uses this function to read input and it has internal
351	/// buffering) and rely on [`std::io`] for any other file I/O operations.
352	#[must_use]
353	pub safe fn module_readline(fd: fd::BorrowedFd<'_>, block: raw::c_int) -> *mut raw::c_char;
354
355	/// Protects multi-line answers against asynchronous event reporting
356	///
357	/// Rust consumers should generally not need this. It is only required if
358	/// you intend to send custom SSIP messages on [`io::stdout`]. Prefer using
359	/// [`module_tts_output_server`], [`module_speak_ok`], [`module_speak_error`],
360	/// [`module_report_index_mark`], [`module_report_icon`] and the
361	/// `module_report_event_*` class of functions to print SSIP message for
362	/// you.
363	///
364	/// If you *do* need to send SSID message yourself, lock this mutex using
365	/// [`libc::pthread_mutex_lock`], print you messages
366	/// **using [`libc::printf`]** (or other bufferred **C I/O**), call
367	/// [`libc::fflush`] to ensure your  response is writte, then unlock the
368	/// mutex using [`libc::pthread_mutex_unlock`].
369	pub static mut module_stdout_mutex: libc::pthread_mutex_t;
370
371	/// Callback for [`module_speak_sync`] to confirm the data is OK before
372	/// actually synthesizing
373	///
374	/// The server may send stop requests, so resetting the stop state must be
375	/// done before calling this.
376	pub safe fn module_speak_ok();
377
378	/// Callback for [`module_speak_sync`] to mark the data as not OK, before
379	/// returning from [`module_speak_sync`]
380	pub safe fn module_speak_error();
381
382	/// Callback to report reaching an index mark
383	pub unsafe fn module_report_index_mark(mark: *const raw::c_char);
384	
385	/// Callback to report the start of synthesis
386	pub safe fn module_report_event_begin();
387
388	/// Callback to report finishing synthesis
389	pub safe fn module_report_event_end();
390	
391	/// Callback to confirm a [`module_stop`] request
392	pub safe fn module_report_event_stop();
393
394	/// Callback to confirm a [`module_pause`] request
395	pub safe fn module_report_event_pause();
396	
397	/// Callback to report reaching a sound icon
398	pub unsafe fn module_report_icon(icon: *const raw::c_char);
399
400	/// Process the module input, interpreting the SSIP protocol and calling
401	/// appropriate module-provided functions
402	///
403	/// This can be either
404	///
405	///  * be called with `block` set to 1, to only return when the server sends
406	///    `QUIT` (acting as the main loop for the module)
407	///  * or called with `block` set to 0, to only process what was already
408	///    sent by the server and not wait for any further requests.
409	///
410	/// Typically this would be called periodically by the module, to let the
411	/// server inform the module when it should stop.
412	#[must_use]
413	pub safe fn module_process(fd: fd::BorrowedFd<'_>, block: raw::c_int) -> raw::c_int;
414}