blob: 9699acaea6fb16240a9e1cb335259ae75af33586 [file] [log] [blame] [edit]
/*
* speak_queue.h - Speak queue helper for Speech Dispatcher modules
*
* Copyright (C) 2007 Brailcom, o.p.s.
* Copyright (C) 2019-2021 Samuel Thibault <samuel.thibault@ens-lyon.org>
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* Based on espeak.c
*
* @author Lukas Loehrer
* Based on ibmtts.c.
*/
/*
* This provides convenience support for pipeline speech synthesis in modules.
*
*
* In summary, a module will look like this:
*
* int module_init(char **status_info) {
* if (mysynth_init()) {
* *status_info = "synth initialization failed";
* return -1;
* }
* mysynth_set_play_callback(mycallback_play);
* mysynth_set_mark_callback(mycallback_mark);
* mysynth_set_end_callback(mycallback_end);
*
* if (module_speak_queue_init(queue_size, status_info))
* return -1;
*
* *status_info = "initialization succeeded";
* }
*
* int module_speak(gchar *data, size_t bytes, SPDMessageType msgtype) {
* if (!module_speak_queue_before_synth())
* return 0;
*
* switch (msgtype) {
* case SPD_MSGTYPE_TEXT:
* mysynth_synth(data, bytes);
* break;
* ...
* }
*
* int mycallback_play(short *wav, int samples)
* {
* if (module_speak_queue_stop_requested()) {
* return STOP;
* }
* AudioTrack track = {
* .samples = wav,
* .num_samples = samples,
* .bits = ...,
* ...
* };
*
* module_speak_queue_before_play();
* module_speak_queue_add_audio(&track, SPD_AUDIO_LE);
* }
*
* int mycallback_mark(const char *mark)
* {
* if (module_speak_queue_stop_requested()) {
* return STOP;
* }
* module_speak_queue_before_play();
* module_speak_queue_add_mark(mark);
* }
*
* int mycallback_end(const char *mark)
* {
* if (module_speak_queue_stop_requested()) {
* return STOP;
* }
* module_speak_queue_before_play();
* module_speak_queue_add_end();
* }
*
* void module_speak_queue_cancel(void)
* {
* mysynth_cancel();
* }
*
* int module_stop(void) {
* module_speak_queue_stop();
* }
*
* int module_pause(void) {
* module_speak_queue_pause();
* }
*
* int module_close(void) {
* module_speak_queue_terminate();
* mysynth_terminate();
* module_speak_queue_free();
* }
*
*
* the module_speak_queue_before_synth() call replaces module_report_event_begin(),
* the module_speak_queue_add_end() call replaces module_report_event_end(),
* the module_speak_queue_stop() call replaces module_report_event_stop() and spd_audio_stop(),
* the module_speak_queue_pause() call replaces module_report_event_pause(),
*
* The principle is that module_speak_queue_init() starts playback threads which
* will handle the audio part. The mysynth_synth() call from module_speak() will
* periodically callback into mycallback_*(), which just queues audio to the
* playback threads, which can thus start playing immediately, without having to
* wait for the whole synth to be completed.
*/
#ifndef __MODULE_UTILS_SPEAK_QUEUE_H
#define __MODULE_UTILS_SPEAK_QUEUE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <pthread.h>
#include <glib.h>
#include "spd_audio_plugin.h"
extern AudioID *module_audio_id;
typedef enum {
SPEAK_QUEUE_QET_AUDIO, /* Chunk of audio. */
SPEAK_QUEUE_QET_INDEX_MARK, /* Index mark event. */
SPEAK_QUEUE_QET_SOUND_ICON, /* A Sound Icon */
SPEAK_QUEUE_QET_BEGIN, /* Beginning of speech. */
SPEAK_QUEUE_QET_END, /* Speech completed. */
SPEAK_QUEUE_QET_PAUSE, /* Speech pause. */
SPEAK_QUEUE_QET_STOP, /* Speech stop. */
SPEAK_QUEUE_QET_BROKEN, /* Speech module is broken. */
} speak_queue_entry_type;
typedef struct {
AudioTrack track;
AudioFormat format;
} speak_queue_audio_chunk;
typedef struct {
speak_queue_entry_type type;
union {
char *markId;
speak_queue_audio_chunk audio;
char *sound_icon_filename;
} data;
} speak_queue_entry;
/* To be called in module_init after synth initialization, to start playback
* threads. */
int module_speak_queue_init(int maxsize, char **status_info);
/* To be called from module_speak before synthesizing the voice. */
int module_speak_queue_before_synth(void);
/* To be called from the synth callback before looking through its events. */
int module_speak_queue_before_play(void);
/* To be called from the synth callback to push different types of events. */
gboolean module_speak_queue_add_audio(const AudioTrack *track, AudioFormat format);
gboolean module_speak_queue_add_mark(const char *markId);
gboolean module_speak_queue_add_sound_icon(const char *filename);
/* To be called on the last synth callback call. */
gboolean module_speak_queue_add_end(void);
/* To be called in the synth callback to look for early stopping. */
int module_speak_queue_stop_requested(void);
/* To be called from module_stop. */
void module_speak_queue_stop(void);
/* To be called from module_pause. */
void module_speak_queue_pause(void);
/* To be called first from module_close to terminate audio early. */
void module_speak_queue_terminate(void);
/* To be called last from module_close to release resources. */
void module_speak_queue_free(void);
/* Can be called early to quickly discard audio */
void module_speak_queue_flush(void);
/* To be provided by the module, shall stop the synthesizer, i.e. make
* it stop calling the module callback, and thus make the module stop calling
* module_speak_queue_add_*. */
void module_speak_queue_cancel(void);
#ifdef __cplusplus
}
#endif
#endif /* #ifndef __MODULE_UTILS_SPEAK_QUEUE_H */