This logic was implemented to limit the number of audio events sent to a device, but the logic is not too relevant in practice (these calls return 0 most of the time), and the logic is broken (e.g. when calling Synthesize with a very long block of text).master
return a_stop_is_required; | return a_stop_is_required; | ||||
} | } | ||||
// Asked for the time interval required for reaching the sample. | |||||
// If the stream is opened but the audio samples are not played, | |||||
// a timeout is started. | |||||
static int get_remaining_time(uint32_t sample, uint32_t *time_in_ms, int *stop_is_required) | |||||
{ | |||||
int err = 0; | |||||
*stop_is_required = 0; | |||||
int i = 0; | |||||
for (i = 0; i < MAX_ACTIVITY_CHECK && (*stop_is_required == 0); i++) { | |||||
err = wave_get_remaining_time(sample, time_in_ms); | |||||
if (err || // if err, stream not available: quit | |||||
wave_is_busy(NULL) || // if wave is busy, time_in_ms is known: quit | |||||
(*time_in_ms == 0)) { // if wave is not busy but remaining time == 0, event is reached: quit | |||||
break; | |||||
} | |||||
// stream opened but not active | |||||
// | |||||
// Several possible states: | |||||
// * the stream is opened but not yet started: | |||||
// | |||||
// wait for the start of stream | |||||
// | |||||
// * some samples have already been played, | |||||
// ** the end of stream is reached | |||||
// ** or there is an underrun | |||||
// | |||||
// wait for the close of stream | |||||
*stop_is_required = sleep_until_timeout_or_stop_request(ACTIVITY_TIMEOUT); | |||||
} | |||||
return err; | |||||
} | |||||
static void *polling_thread(void *p) | static void *polling_thread(void *p) | ||||
{ | { | ||||
(void)p; // unused | (void)p; // unused | ||||
espeak_EVENT *event = (espeak_EVENT *)(head->data); | espeak_EVENT *event = (espeak_EVENT *)(head->data); | ||||
assert(event); | assert(event); | ||||
uint32_t time_in_ms = 0; | |||||
int err = get_remaining_time((uint32_t)event->sample, | |||||
&time_in_ms, | |||||
&a_stop_is_required); | |||||
if (a_stop_is_required > 0) | |||||
break; | |||||
else if (err != 0) { | |||||
// No available time: the event is deleted. | |||||
a_status = pthread_mutex_lock(&my_mutex); | |||||
event_delete((espeak_EVENT *)pop()); | |||||
a_status = pthread_mutex_unlock(&my_mutex); | |||||
} else if (time_in_ms == 0) { // the event is already reached. | |||||
if (my_callback) { | |||||
event_notify(event); | |||||
// the user_data (and the type) are cleaned to be sure | |||||
// that MSG_TERMINATED is called twice (at delete time too). | |||||
event->type = espeakEVENT_LIST_TERMINATED; | |||||
event->user_data = NULL; | |||||
} | |||||
a_status = pthread_mutex_lock(&my_mutex); | |||||
event_delete((espeak_EVENT *)pop()); | |||||
a_status = pthread_mutex_unlock(&my_mutex); | |||||
if (my_callback) { | |||||
event_notify(event); | |||||
// the user_data (and the type) are cleaned to be sure | |||||
// that MSG_TERMINATED is called twice (at delete time too). | |||||
event->type = espeakEVENT_LIST_TERMINATED; | |||||
event->user_data = NULL; | |||||
} | |||||
a_status = pthread_mutex_lock(&my_mutex); | |||||
event_delete((espeak_EVENT *)pop()); | |||||
a_status = pthread_mutex_unlock(&my_mutex); | |||||
a_stop_is_required = 0; | |||||
a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required); | |||||
if ((a_status == 0) && (a_stop_is_required > 0)) { | |||||
while (0 == sem_trywait(&my_sem_stop_is_required)) | |||||
; | |||||
} else | |||||
a_stop_is_required = 0; | a_stop_is_required = 0; | ||||
a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required); | |||||
if ((a_status == 0) && (a_stop_is_required > 0)) { | |||||
while (0 == sem_trywait(&my_sem_stop_is_required)) | |||||
; | |||||
} else | |||||
a_stop_is_required = 0; | |||||
} else // The event will be notified soon: sleep until timeout or stop request | |||||
a_stop_is_required = sleep_until_timeout_or_stop_request(time_in_ms); | |||||
} | } | ||||
a_status = pthread_mutex_lock(&my_mutex); | a_status = pthread_mutex_lock(&my_mutex); |
void wave_port_flush(void *theHandler); | void wave_port_flush(void *theHandler); | ||||
void wave_port_set_callback_is_output_enabled(t_wave_callback *cb); | void wave_port_set_callback_is_output_enabled(t_wave_callback *cb); | ||||
void *wave_port_test_get_write_buffer(); | void *wave_port_test_get_write_buffer(); | ||||
int wave_port_get_remaining_time(uint32_t sample, uint32_t *time); | |||||
// wave_pulse.cpp | // wave_pulse.cpp | ||||
int is_pulse_running(); | int is_pulse_running(); | ||||
void wave_pulse_flush(void *theHandler); | void wave_pulse_flush(void *theHandler); | ||||
void wave_pulse_set_callback_is_output_enabled(t_wave_callback *cb); | void wave_pulse_set_callback_is_output_enabled(t_wave_callback *cb); | ||||
void *wave_pulse_test_get_write_buffer(); | void *wave_pulse_test_get_write_buffer(); | ||||
int wave_pulse_get_remaining_time(uint32_t sample, uint32_t *time); | |||||
// wrappers | // wrappers | ||||
void *wave_open(int srate, const char *device) | void *wave_open(int srate, const char *device) | ||||
wave_port_set_callback_is_output_enabled(cb); | wave_port_set_callback_is_output_enabled(cb); | ||||
} | } | ||||
int wave_get_remaining_time(uint32_t sample, uint32_t *time) | |||||
{ | |||||
if (pulse_running) | |||||
return wave_pulse_get_remaining_time(sample, time); | |||||
else | |||||
return wave_port_get_remaining_time(sample, time); | |||||
} | |||||
// rename functions to be wrapped | // rename functions to be wrapped | ||||
#define wave_open wave_port_open | #define wave_open wave_port_open | ||||
#define wave_write wave_port_write | #define wave_write wave_port_write | ||||
#define wave_get_write_position wave_port_get_write_position | #define wave_get_write_position wave_port_get_write_position | ||||
#define wave_flush wave_port_flush | #define wave_flush wave_port_flush | ||||
#define wave_set_callback_is_output_enabled wave_port_set_callback_is_output_enabled | #define wave_set_callback_is_output_enabled wave_port_set_callback_is_output_enabled | ||||
#define wave_get_remaining_time wave_port_get_remaining_time | |||||
#endif | #endif | ||||
return myWritePosition; | return myWritePosition; | ||||
} | } | ||||
int wave_get_remaining_time(uint32_t sample, uint32_t *time) | |||||
{ | |||||
double a_time = 0; | |||||
if (!time || !pa_stream) | |||||
return -1; | |||||
if (sample > myReadPosition) { | |||||
// TBD: take in account time suplied by portaudio V18 API | |||||
a_time = sample - myReadPosition; | |||||
a_time = 0.5 + (a_time * 1000.0) / wave_samplerate; | |||||
} else | |||||
a_time = 0; | |||||
*time = (uint32_t)a_time; | |||||
return 0; | |||||
} | |||||
#else | #else | ||||
void *wave_open(int srate, const char *device) | void *wave_open(int srate, const char *device) | ||||
(void)cb; // unused | (void)cb; // unused | ||||
} | } | ||||
int wave_get_remaining_time(uint32_t sample, uint32_t *time) | |||||
{ | |||||
(void)sample; // unused | |||||
if (!time) return -1; | |||||
*time = (uint32_t)0; | |||||
return 0; | |||||
} | |||||
#endif | #endif |
extern void wave_terminate(); | extern void wave_terminate(); | ||||
extern uint32_t wave_get_write_position(void *theHandler); | extern uint32_t wave_get_write_position(void *theHandler); | ||||
// Supply the remaining time in ms before the sample is played | |||||
// (or 0 if the event has been already played). | |||||
// sample: sample identifier | |||||
// time: supplied value in ms | |||||
// | |||||
// return 0 if ok or -1 otherwise (stream not opened). | |||||
extern int wave_get_remaining_time(uint32_t sample, uint32_t *time); | |||||
// set the callback which informs if the output is still enabled. | // set the callback which informs if the output is still enabled. | ||||
// Helpful if a new sample is waiting for free space whereas sound must be stopped. | // Helpful if a new sample is waiting for free space whereas sound must be stopped. | ||||
typedef int (t_wave_callback)(void); | typedef int (t_wave_callback)(void); |
#define wave_get_write_position wave_pulse_get_write_position | #define wave_get_write_position wave_pulse_get_write_position | ||||
#define wave_flush wave_pulse_flush | #define wave_flush wave_pulse_flush | ||||
#define wave_set_callback_is_output_enabled wave_pulse_set_callback_is_output_enabled | #define wave_set_callback_is_output_enabled wave_pulse_set_callback_is_output_enabled | ||||
#define wave_get_remaining_time wave_pulse_get_remaining_time | |||||
// check whether we can connect to PulseAudio | // check whether we can connect to PulseAudio | ||||
#include <pulse/simple.h> | #include <pulse/simple.h> | ||||
return a_timing_info.write_index; | return a_timing_info.write_index; | ||||
} | } | ||||
int wave_get_remaining_time(uint32_t sample, uint32_t *time) | |||||
{ | |||||
double a_time = 0; | |||||
if (!time || !stream) | |||||
return -1; | |||||
pa_timing_info a_timing_info = {0}; | |||||
pulse_playing(&a_timing_info); | |||||
if (sample > a_timing_info.read_index) { | |||||
// TBD: take in account time suplied by portaudio V18 API | |||||
a_time = sample - a_timing_info.read_index; | |||||
a_time = 0.5 + (a_time * 1000.0) / wave_samplerate; | |||||
} else | |||||
a_time = 0; | |||||
*time = (uint32_t)a_time; | |||||
return 0; | |||||
} | |||||
#endif | #endif |
static uint32_t wave_samplerate; | static uint32_t wave_samplerate; | ||||
static int wave_get_remaining_time(uint32_t sample, uint32_t *time); | |||||
// wave_open | // wave_open | ||||
// | // | ||||
// DESCRIPTION: | // DESCRIPTION: |