Browse Source

Remove the debugging code.

The debugging code is done inconsistently, mainly to trace the
event and audio output logic. This makes it harder to understand
the code flow for logic that is not enabled by default.

As such the debugging code makes the code harder to maintain.
master
Reece H. Dunn 9 years ago
parent
commit
e69c936329

+ 0
- 1
Makefile.am View File

src/libespeak-ng/compiledata.c \ src/libespeak-ng/compiledata.c \
src/libespeak-ng/compiledict.c \ src/libespeak-ng/compiledict.c \
src/libespeak-ng/compilembrola.c \ src/libespeak-ng/compilembrola.c \
src/libespeak-ng/debug.c \
src/libespeak-ng/dictionary.c \ src/libespeak-ng/dictionary.c \
src/libespeak-ng/ieee80.c \ src/libespeak-ng/ieee80.c \
src/libespeak-ng/intonation.c \ src/libespeak-ng/intonation.c \

+ 0
- 76
src/libespeak-ng/debug.c View File

/*
* Copyright (C) 2007 by Jonathan Duddington
* email: [email protected]
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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: <http://www.gnu.org/licenses/>.
*/

#include "config.h"

#include <stdio.h>
#include <stdarg.h>
#include "speech.h"
#include "debug.h"

#ifdef DEBUG_ENABLED
#include <sys/time.h>
#include <unistd.h>

static FILE *fd_log = NULL;
static const char *FILENAME = "/tmp/espeak.log";

void debug_init()
{
if ((fd_log = fopen(FILENAME, "a")) != NULL)
setvbuf(fd_log, NULL, _IONBF, 0);
}

void debug_enter(const char *text)
{
struct timeval tv;

gettimeofday(&tv, NULL);

if (!fd_log)
debug_init();

if (fd_log)
fprintf(fd_log, "%03d.%03dms > ENTER %s\n", (int)(tv.tv_sec%1000), (int)(tv.tv_usec/1000), text);
}


void debug_show(const char *format, ...)
{
va_list args;
va_start(args, format);
if (!fd_log)
debug_init();
if (fd_log)
vfprintf(fd_log, format, args);
va_end(args);
}

void debug_time(const char *text)
{
struct timeval tv;

gettimeofday(&tv, NULL);

if (!fd_log)
debug_init();
if (fd_log)
fprintf(fd_log, "%03d.%03dms > %s\n", (int)(tv.tv_sec%1000), (int)(tv.tv_usec/1000), text);
}

#endif

+ 0
- 51
src/libespeak-ng/debug.h View File

/*
* Copyright (C) 2007 to 2009 by Jonathan Duddington
* email: [email protected]
* Copyright (C) 2015 Reece H. Dunn
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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: <http://www.gnu.org/licenses/>.
*/

#ifndef DEBUG_H
#define DEBUG_H

#ifdef __cplusplus
extern "C"
{
#endif

#ifdef DEBUG_ENABLED
#define ENTER(text) debug_enter(text)
#define SHOW(format, ...) debug_show(format, __VA_ARGS__);
#define SHOW_TIME(text) debug_time(text);
extern void debug_enter(const char *text);
extern void debug_show(const char *format, ...);
extern void debug_time(const char *text);

#else

#ifdef NO_VARIADIC_MACROS
#define SHOW(format) // VC6 doesn't allow "..."
#else
#define SHOW(format, ...)
#endif
#define SHOW_TIME(text)
#define ENTER(text)
#endif

#ifdef __cplusplus
}
#endif

#endif

+ 1
- 122
src/libespeak-ng/espeak_command.c View File

#include <assert.h> #include <assert.h>
#include <wchar.h> #include <wchar.h>


#include "debug.h"

static unsigned int my_current_text_id = 0; static unsigned int my_current_text_id = 0;


t_espeak_command *create_espeak_text(const void *text, size_t size, unsigned int position, espeak_POSITION_TYPE position_type, unsigned int end_position, unsigned int flags, void *user_data) t_espeak_command *create_espeak_text(const void *text, size_t size, unsigned int position, espeak_POSITION_TYPE position_type, unsigned int end_position, unsigned int flags, void *user_data)
{ {
ENTER("create_espeak_text");
int a_error = 1; int a_error = 1;
void *a_text = NULL; void *a_text = NULL;
t_espeak_text *data = NULL; t_espeak_text *data = NULL;
data->user_data = user_data; data->user_data = user_data;
a_error = 0; a_error = 0;


SHOW("ET_TEXT malloc text=%x, command=%x (uid=%d)\n", a_text, a_command, data->unique_identifier);

text_error: text_error:
if (a_error) { if (a_error) {
if (a_text) if (a_text)
a_command = NULL; a_command = NULL;
} }


SHOW("command=0x%x\n", a_command);

return a_command; return a_command;
} }


t_espeak_command *create_espeak_terminated_msg(unsigned int unique_identifier, void *user_data) t_espeak_command *create_espeak_terminated_msg(unsigned int unique_identifier, void *user_data)
{ {
ENTER("create_espeak_terminated_msg");
int a_error = 1; int a_error = 1;
t_espeak_terminated_msg *data = NULL; t_espeak_terminated_msg *data = NULL;
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command));
data->user_data = user_data; data->user_data = user_data;
a_error = 0; a_error = 0;


SHOW("ET_TERMINATED_MSG command=%x (uid=%d, user_data=0x%x)\n", a_command, unique_identifier, (int)user_data);

msg_error: msg_error:
if (a_error) { if (a_error) {
if (a_command) if (a_command)
a_command = NULL; a_command = NULL;
} }


SHOW("command=0x%x\n", a_command);

return a_command; return a_command;


} }


t_espeak_command *create_espeak_mark(const void *text, size_t size, const char *index_mark, unsigned int end_position, unsigned int flags, void *user_data) t_espeak_command *create_espeak_mark(const void *text, size_t size, const char *index_mark, unsigned int end_position, unsigned int flags, void *user_data)
{ {
ENTER("create_espeak_mark");
int a_error = 1; int a_error = 1;
void *a_text = NULL; void *a_text = NULL;
char *a_index_mark = NULL; char *a_index_mark = NULL;
free(a_index_mark); free(a_index_mark);
} }


SHOW("ET_MARK malloc text=%x, command=%x (uid=%d)\n", a_text, a_command, data->unique_identifier);

return a_command; return a_command;
} }


t_espeak_command *create_espeak_key(const char *key_name, void *user_data) t_espeak_command *create_espeak_key(const char *key_name, void *user_data)
{ {
ENTER("create_espeak_key");
int a_error = 1; int a_error = 1;
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command));


a_command = NULL; a_command = NULL;
} }


SHOW("command=0x%x\n", a_command);

return a_command; return a_command;
} }


t_espeak_command *create_espeak_char(wchar_t character, void *user_data) t_espeak_command *create_espeak_char(wchar_t character, void *user_data)
{ {
ENTER("create_espeak_char");
int a_error = 1; int a_error = 1;
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command));
if (!a_command) if (!a_command)
a_command = NULL; a_command = NULL;
} }


SHOW("command=0x%x\n", a_command);

return a_command; return a_command;
} }


t_espeak_command *create_espeak_parameter(espeak_PARAMETER parameter, int value, int relative) t_espeak_command *create_espeak_parameter(espeak_PARAMETER parameter, int value, int relative)
{ {
ENTER("create_espeak_parameter");
int a_error = 1; int a_error = 1;
t_espeak_parameter *data = NULL; t_espeak_parameter *data = NULL;
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command));
a_command = NULL; a_command = NULL;
} }


SHOW("command=0x%x\n", a_command);

return a_command; return a_command;
} }


t_espeak_command *create_espeak_punctuation_list(const wchar_t *punctlist) t_espeak_command *create_espeak_punctuation_list(const wchar_t *punctlist)
{ {
ENTER("create_espeak_punctuation_list");
int a_error = 1; int a_error = 1;
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command));


a_command = NULL; a_command = NULL;
} }


SHOW("command=0x%x\n", a_command);

return a_command; return a_command;
} }


t_espeak_command *create_espeak_voice_name(const char *name) t_espeak_command *create_espeak_voice_name(const char *name)
{ {
ENTER("create_espeak_voice_name");

int a_error = 1; int a_error = 1;
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command));


a_command = NULL; a_command = NULL;
} }


SHOW("command=0x%x\n", a_command);

return a_command; return a_command;
} }


t_espeak_command *create_espeak_voice_spec(espeak_VOICE *voice) t_espeak_command *create_espeak_voice_spec(espeak_VOICE *voice)
{ {
ENTER("create_espeak_voice_spec");
int a_error = 1; int a_error = 1;
t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command)); t_espeak_command *a_command = (t_espeak_command *)malloc(sizeof(t_espeak_command));


a_command = NULL; a_command = NULL;
} }


SHOW("command=0x%x\n", a_command);

return a_command; return a_command;
} }


int delete_espeak_command(t_espeak_command *the_command) int delete_espeak_command(t_espeak_command *the_command)
{ {
ENTER("delete_espeak_command");
int a_status = 0; int a_status = 0;
if (the_command) { if (the_command) {
switch (the_command->type) switch (the_command->type)
{ {
case ET_TEXT: case ET_TEXT:
if (the_command->u.my_text.text) {
SHOW("delete_espeak_command > ET_TEXT free text=%x, command=%x, uid=%d\n", the_command->u.my_text.text, the_command, the_command->u.my_text.unique_identifier);
if (the_command->u.my_text.text)
free(the_command->u.my_text.text); free(the_command->u.my_text.text);
}
break; break;
case ET_MARK: case ET_MARK:
if (the_command->u.my_mark.text) if (the_command->u.my_mark.text)
t_espeak_terminated_msg *data = &(the_command->u.my_terminated_msg); t_espeak_terminated_msg *data = &(the_command->u.my_terminated_msg);
if (the_command->state == CS_PENDING) { if (the_command->state == CS_PENDING) {
the_command->state = CS_PROCESSED; the_command->state = CS_PROCESSED;
SHOW("delete_espeak_command > ET_TERMINATED_MSG callback (command=0x%x, uid=%d) \n", the_command, data->unique_identifier);
sync_espeak_terminated_msg(data->unique_identifier, data->user_data); sync_espeak_terminated_msg(data->unique_identifier, data->user_data);
} }
} }
default: default:
assert(0); assert(0);
} }
SHOW("delete_espeak_command > free command=0x%x\n", the_command);
free(the_command); free(the_command);
a_status = 1; a_status = 1;
} }


void process_espeak_command(t_espeak_command *the_command) void process_espeak_command(t_espeak_command *the_command)
{ {
ENTER("process_espeak_command");

SHOW("command=0x%x\n", the_command);

if (the_command == NULL) if (the_command == NULL)
return; return;


break; break;
} }
} }

void display_espeak_command(t_espeak_command *the_command)
{
ENTER("display_espeak_command");
#ifdef DEBUG_ENABLED
if (the_command == NULL) {
SHOW("display_espeak_command > command=%s\n", "NULL");
return;
}

SHOW("display_espeak_command > state=%d\n", the_command->state);

switch (the_command->type)
{
case ET_TEXT:
{
t_espeak_text *data = &(the_command->u.my_text);
SHOW("display_espeak_command > (0x%x) uid=%d, TEXT=%s, user_data=0x%x\n", the_command, data->unique_identifier, (char *)data->text, (int)(data->user_data));
}
break;
case ET_MARK:
{
t_espeak_mark *data = &(the_command->u.my_mark);
SHOW("display_espeak_command > (0x%x) uid=%d, MARK=%s, user_data=0x%x\n", the_command, data->unique_identifier, (char *)data->text, (int)(data->user_data));
}
break;
case ET_KEY:
{
const char *data = the_command->u.my_key.key_name;
SHOW("display_espeak_command > (0x%x) KEY=%c\n", the_command, data);
}
break;
case ET_TERMINATED_MSG:
{
t_espeak_terminated_msg *data = &(the_command->u.my_terminated_msg);

SHOW("display_espeak_command > (0x%x) TERMINATED_MSG uid=%d, user_data=0x%x, state=%d\n",
the_command, data->unique_identifier, data->user_data,
the_command->state);
}
break;
case ET_CHAR:
{
const wchar_t data = the_command->u.my_char.character;
SHOW("display_espeak_command > (0x%x) CHAR=%c\n", the_command, (char)data);
}
break;
case ET_PARAMETER:
{
t_espeak_parameter *data = &(the_command->u.my_param);
SHOW("display_espeak_command > (0x%x) PARAMETER=%d, value=%d, relative=%d\n",
the_command, data->parameter, data->value, data->relative);
}
break;
case ET_PUNCTUATION_LIST:
{
const wchar_t *data = the_command->u.my_punctuation_list;
sync_espeak_SetPunctuationList(data);
SHOW("display_espeak_command > (0x%x) PUNCTLIST=%s\n", the_command, (char *)data);
}
break;
case ET_VOICE_NAME:
{
const char *data = the_command->u.my_voice_name;
SHOW("display_espeak_command > (0x%x) VOICE_NAME=%s\n", the_command, data);
}
break;
case ET_VOICE_SPEC:
{
SHOW("display_espeak_command > (0x%x) VOICE_SPEC", the_command);
}
break;
default:
assert(0);
break;
}
#endif
}

+ 0
- 3
src/libespeak-ng/espeak_command.h View File



int delete_espeak_command(t_espeak_command *the_command); int delete_espeak_command(t_espeak_command *the_command);


void display_espeak_command(t_espeak_command *the_command);


espeak_ERROR sync_espeak_Synth(unsigned int unique_identifier, const void *text, size_t size, espeak_ERROR sync_espeak_Synth(unsigned int unique_identifier, const void *text, size_t size,
unsigned int position, espeak_POSITION_TYPE position_type, unsigned int position, espeak_POSITION_TYPE position_type,
unsigned int end_position, unsigned int flags, void *user_data); unsigned int end_position, unsigned int flags, void *user_data);

+ 3
- 122
src/libespeak-ng/event.c View File

#include "speak_lib.h" #include "speak_lib.h"
#include "event.h" #include "event.h"
#include "wave.h" #include "wave.h"
#include "debug.h"


// my_mutex: protects my_thread_is_talking, // my_mutex: protects my_thread_is_talking,
static pthread_mutex_t my_mutex; static pthread_mutex_t my_mutex;


void event_init(void) void event_init(void)
{ {
ENTER("event_init");

my_event_is_running = 0; my_event_is_running = 0;


// security // security
pthread_attr_destroy(&a_attrib); pthread_attr_destroy(&a_attrib);
} }


static void event_display(espeak_EVENT *event)
{
ENTER("event_display");

#ifdef DEBUG_ENABLED
if (event == NULL)
SHOW("event_display > event=%s\n", "NULL");
else {
static const char *label[] = {
"LIST_TERMINATED",
"WORD",
"SENTENCE",
"MARK",
"PLAY",
"END",
"MSG_TERMINATED",
"PHONEME",
"SAMPLERATE",
"??"
};

SHOW("event_display > event=0x%x\n", event);
SHOW("event_display > type=%s\n", label[event->type]);
SHOW("event_display > uid=%d\n", event->unique_identifier);
SHOW("event_display > text_position=%d\n", event->text_position);
SHOW("event_display > length=%d\n", event->length);
SHOW("event_display > audio_position=%d\n", event->audio_position);
SHOW("event_display > sample=%d\n", event->sample);
SHOW("event_display > user_data=0x%x\n", event->user_data);
}
#endif
}

static espeak_EVENT *event_copy(espeak_EVENT *event) static espeak_EVENT *event_copy(espeak_EVENT *event)
{ {
ENTER("event_copy");

if (event == NULL) if (event == NULL)
return NULL; return NULL;


} }
} }


event_display(a_event);

return a_event; return a_event;
} }




static void event_notify(espeak_EVENT *event) static void event_notify(espeak_EVENT *event)
{ {
ENTER("event_notify");
static unsigned int a_old_uid = 0; static unsigned int a_old_uid = 0;


espeak_EVENT events[2]; espeak_EVENT events[2];
events[1].type = espeakEVENT_LIST_TERMINATED; // ... terminated by an event type=0 events[1].type = espeakEVENT_LIST_TERMINATED; // ... terminated by an event type=0


if (event && my_callback) { if (event && my_callback) {
event_display(event);

switch (event->type) switch (event->type)
{ {
case espeakEVENT_SENTENCE: case espeakEVENT_SENTENCE:


static int event_delete(espeak_EVENT *event) static int event_delete(espeak_EVENT *event)
{ {
ENTER("event_delete");

event_display(event);

if (event == NULL) if (event == NULL)
return 0; return 0;




espeak_ERROR event_declare(espeak_EVENT *event) espeak_ERROR event_declare(espeak_EVENT *event)
{ {
ENTER("event_declare");

event_display(event);

if (!event) if (!event)
return EE_INTERNAL_ERROR; return EE_INTERNAL_ERROR;


espeak_ERROR a_error = EE_OK; espeak_ERROR a_error = EE_OK;


if (!a_status) { if (!a_status) {
SHOW_TIME("event_declare > locked\n");
espeak_EVENT *a_event = event_copy(event); espeak_EVENT *a_event = event_copy(event);
a_error = push(a_event); a_error = push(a_event);
if (a_error != EE_OK) if (a_error != EE_OK)
event_delete(a_event); event_delete(a_event);
SHOW_TIME("event_declare > unlocking\n");
a_status = pthread_mutex_unlock(&my_mutex); a_status = pthread_mutex_unlock(&my_mutex);
} }


SHOW_TIME("event_declare > post my_sem_start_is_required\n");
sem_post(&my_sem_start_is_required); sem_post(&my_sem_start_is_required);


if (a_status != 0) if (a_status != 0)


espeak_ERROR event_clear_all() espeak_ERROR event_clear_all()
{ {
ENTER("event_clear_all");

int a_status = pthread_mutex_lock(&my_mutex); int a_status = pthread_mutex_lock(&my_mutex);
int a_event_is_running = 0; int a_event_is_running = 0;


SHOW_TIME("event_stop > locked\n");
if (a_status != 0) if (a_status != 0)
return EE_INTERNAL_ERROR; return EE_INTERNAL_ERROR;


if (my_event_is_running) { if (my_event_is_running) {
SHOW_TIME("event_stop > post my_sem_stop_is_required\n");
sem_post(&my_sem_stop_is_required); sem_post(&my_sem_stop_is_required);
a_event_is_running = 1; a_event_is_running = 1;
} else } else
init(); // clear pending events init(); // clear pending events
SHOW_TIME("event_stop > unlocking\n");
a_status = pthread_mutex_unlock(&my_mutex); a_status = pthread_mutex_unlock(&my_mutex);
if (a_status != 0) if (a_status != 0)
return EE_INTERNAL_ERROR; return EE_INTERNAL_ERROR;


if (a_event_is_running) { if (a_event_is_running) {
SHOW_TIME("event_stop > wait for my_sem_stop_is_acknowledged\n");
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR)
continue; // Restart when interrupted by handler continue; // Restart when interrupted by handler
SHOW_TIME("event_stop > get my_sem_stop_is_acknowledged\n");
} }


SHOW_TIME("LEAVE event_stop\n");

return EE_OK; return EE_OK;
} }


static int sleep_until_timeout_or_stop_request(uint32_t time_in_ms) static int sleep_until_timeout_or_stop_request(uint32_t time_in_ms)
{ {
ENTER("sleep_until_timeout_or_stop_request");

int a_stop_is_required = 0; int a_stop_is_required = 0;
struct timespec ts; struct timespec ts;
struct timeval tv; struct timeval tv;


clock_gettime2(&ts); clock_gettime2(&ts);


#ifdef DEBUG_ENABLED
struct timespec to;
to.tv_sec = ts.tv_sec;
to.tv_nsec = ts.tv_nsec;
#endif

add_time_in_ms(&ts, time_in_ms); add_time_in_ms(&ts, time_in_ms);


SHOW("polling_thread > sleep_until_timeout_or_stop_request > start sem_timedwait from %d.%09lu to %d.%09lu \n",
to.tv_sec, to.tv_nsec,
ts.tv_sec, ts.tv_nsec);

while ((err = sem_timedwait(&my_sem_stop_is_required, &ts)) == -1 while ((err = sem_timedwait(&my_sem_stop_is_required, &ts)) == -1
&& errno == EINTR) && errno == EINTR)
continue; // Restart when interrupted by handler continue; // Restart when interrupted by handler


assert(gettimeofday(&tv, NULL) != -1); assert(gettimeofday(&tv, NULL) != -1);
SHOW("polling_thread > sleep_until_timeout_or_stop_request > stop sem_timedwait %d.%09lu \n",
tv.tv_sec, tv.tv_usec*1000);


if (err == 0) {
SHOW("polling_thread > sleep_until_timeout_or_stop_request > %s\n", "stop required!");
if (err == 0)
a_stop_is_required = 1; // stop required a_stop_is_required = 1; // stop required
}
return a_stop_is_required; return a_stop_is_required;
} }




static int get_remaining_time(uint32_t sample, uint32_t *time_in_ms, int *stop_is_required) static int get_remaining_time(uint32_t sample, uint32_t *time_in_ms, int *stop_is_required)
{ {
ENTER("get_remaining_time");

int err = 0; int err = 0;
*stop_is_required = 0; *stop_is_required = 0;
int i = 0; int i = 0;


static void *polling_thread(void *p) static void *polling_thread(void *p)
{ {
ENTER("polling_thread");

while (1) { while (1) {
int a_stop_is_required = 0; int a_stop_is_required = 0;


SHOW_TIME("polling_thread > locking\n");
int a_status = pthread_mutex_lock(&my_mutex); int a_status = pthread_mutex_lock(&my_mutex);
SHOW_TIME("polling_thread > locked (my_event_is_running = 0)\n");
my_event_is_running = 0; my_event_is_running = 0;
pthread_mutex_unlock(&my_mutex); pthread_mutex_unlock(&my_mutex);
SHOW_TIME("polling_thread > unlocked\n");

SHOW_TIME("polling_thread > wait for my_sem_start_is_required\n");


while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR) while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR)
continue; // Restart when interrupted by handler continue; // Restart when interrupted by handler


SHOW_TIME("polling_thread > get my_sem_start_is_required\n");

a_status = pthread_mutex_lock(&my_mutex); a_status = pthread_mutex_lock(&my_mutex);
SHOW_TIME("polling_thread > locked (my_event_is_running = 1)\n");
my_event_is_running = 1; my_event_is_running = 1;
pthread_mutex_unlock(&my_mutex); pthread_mutex_unlock(&my_mutex);
SHOW_TIME("polling_thread > unlocked\n");


a_stop_is_required = 0; a_stop_is_required = 0;
a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required); // NOTE: may set a_stop_is_required to -1 a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required); // NOTE: may set a_stop_is_required to -1
if ((a_status == 0) && (a_stop_is_required > 0)) { if ((a_status == 0) && (a_stop_is_required > 0)) {
SHOW("polling_thread > stop required (%d)\n", __LINE__);
while (0 == sem_trywait(&my_sem_stop_is_required)) while (0 == sem_trywait(&my_sem_stop_is_required))
; ;
} else } else


// In this loop, my_event_is_running = 1 // In this loop, my_event_is_running = 1
while (head && (a_stop_is_required <= 0)) { while (head && (a_stop_is_required <= 0)) {
SHOW_TIME("polling_thread > check head\n");
while (0 == sem_trywait(&my_sem_start_is_required)) while (0 == sem_trywait(&my_sem_start_is_required))
; ;


break; break;
else if (err != 0) { else if (err != 0) {
// No available time: the event is deleted. // No available time: the event is deleted.
SHOW("polling_thread > %s\n", "audio device down");
a_status = pthread_mutex_lock(&my_mutex); a_status = pthread_mutex_lock(&my_mutex);
SHOW_TIME("polling_thread > locked\n");
event_delete((espeak_EVENT *)pop()); event_delete((espeak_EVENT *)pop());
a_status = pthread_mutex_unlock(&my_mutex); a_status = pthread_mutex_unlock(&my_mutex);
SHOW_TIME("polling_thread > unlocked\n");
} else if (time_in_ms == 0) { // the event is already reached. } else if (time_in_ms == 0) { // the event is already reached.
if (my_callback) { if (my_callback) {
event_notify(event); event_notify(event);
} }


a_status = pthread_mutex_lock(&my_mutex); a_status = pthread_mutex_lock(&my_mutex);
SHOW_TIME("polling_thread > locked\n");
event_delete((espeak_EVENT *)pop()); event_delete((espeak_EVENT *)pop());
a_status = pthread_mutex_unlock(&my_mutex); a_status = pthread_mutex_unlock(&my_mutex);
SHOW_TIME("polling_thread > unlocked\n");


a_stop_is_required = 0; a_stop_is_required = 0;
a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required); a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required);


if ((a_status == 0) && (a_stop_is_required > 0)) { if ((a_status == 0) && (a_stop_is_required > 0)) {
SHOW("polling_thread > stop required (%d)\n", __LINE__);
while (0 == sem_trywait(&my_sem_stop_is_required)) while (0 == sem_trywait(&my_sem_stop_is_required))
; ;
} else } else
} }


a_status = pthread_mutex_lock(&my_mutex); a_status = pthread_mutex_lock(&my_mutex);
SHOW_TIME("polling_thread > locked\n");


SHOW_TIME("polling_thread > my_event_is_running = 0\n");
my_event_is_running = 0; my_event_is_running = 0;


if (a_stop_is_required <= 0) { if (a_stop_is_required <= 0) {
a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required); a_status = sem_getvalue(&my_sem_stop_is_required, &a_stop_is_required);
if ((a_status == 0) && (a_stop_is_required > 0)) { if ((a_status == 0) && (a_stop_is_required > 0)) {
SHOW("polling_thread > stop required (%d)\n", __LINE__);
while (0 == sem_trywait(&my_sem_stop_is_required)) while (0 == sem_trywait(&my_sem_stop_is_required))
; ;
} else } else
} }


a_status = pthread_mutex_unlock(&my_mutex); a_status = pthread_mutex_unlock(&my_mutex);
SHOW_TIME("polling_thread > unlocked\n");


if (a_stop_is_required > 0) { if (a_stop_is_required > 0) {
SHOW("polling_thread > %s\n", "stop required!");
// no mutex required since the stop command is synchronous // no mutex required since the stop command is synchronous
// and waiting for my_sem_stop_is_acknowledged // and waiting for my_sem_stop_is_acknowledged
init(); init();


// acknowledge the stop request // acknowledge the stop request
SHOW_TIME("polling_thread > post my_sem_stop_is_acknowledged\n");
a_status = sem_post(&my_sem_stop_is_acknowledged); a_status = sem_post(&my_sem_stop_is_acknowledged);
} }
} }


static espeak_ERROR push(void *the_data) static espeak_ERROR push(void *the_data)
{ {
ENTER("event > push");

assert((!head && !tail) || (head && tail)); assert((!head && !tail) || (head && tail));


if (the_data == NULL) {
SHOW("event > push > event=0x%x\n", NULL);
if (the_data == NULL)
return EE_INTERNAL_ERROR; return EE_INTERNAL_ERROR;
}


if (node_counter >= MAX_NODE_COUNTER) {
SHOW("event > push > %s\n", "EE_BUFFER_FULL");
if (node_counter >= MAX_NODE_COUNTER)
return EE_BUFFER_FULL; return EE_BUFFER_FULL;
}


node *n = (node *)malloc(sizeof(node)); node *n = (node *)malloc(sizeof(node));
if (n == NULL) if (n == NULL)
tail->data = the_data; tail->data = the_data;


node_counter++; node_counter++;
SHOW("event > push > counter=%d (uid=%d)\n", node_counter, ((espeak_EVENT *)the_data)->unique_identifier);


return EE_OK; return EE_OK;
} }


static void *pop() static void *pop()
{ {
ENTER("event > pop");
void *the_data = NULL; void *the_data = NULL;


assert((!head && !tail) || (head && tail)); assert((!head && !tail) || (head && tail));
head = n->next; head = n->next;
free(n); free(n);
node_counter--; node_counter--;
SHOW("event > pop > event=0x%x (counter=%d, uid=%d)\n", the_data, node_counter, ((espeak_EVENT *)the_data)->unique_identifier);
} }


if (head == NULL) if (head == NULL)


static void init() static void init()
{ {
ENTER("event > init");

while (event_delete((espeak_EVENT *)pop())) while (event_delete((espeak_EVENT *)pop()))
; ;




void event_terminate() void event_terminate()
{ {
ENTER("event_terminate");

if (thread_inited) { if (thread_inited) {
pthread_cancel(my_thread); pthread_cancel(my_thread);
pthread_join(my_thread, NULL); pthread_join(my_thread, NULL);

+ 5
- 89
src/libespeak-ng/fifo.c View File

#include "speech.h" #include "speech.h"
#include "fifo.h" #include "fifo.h"
#include "wave.h" #include "wave.h"
#include "debug.h"


// my_mutex: protects my_thread_is_talking, // my_mutex: protects my_thread_is_talking,
// my_stop_is_required, and the command fifo // my_stop_is_required, and the command fifo


void fifo_init() void fifo_init()
{ {
ENTER("fifo_init");

// security // security
pthread_mutex_init(&my_mutex, (const pthread_mutexattr_t *)NULL); pthread_mutex_init(&my_mutex, (const pthread_mutexattr_t *)NULL);
init(0); init(0);
pthread_attr_destroy(&a_attrib); pthread_attr_destroy(&a_attrib);


// leave once the thread is actually started // leave once the thread is actually started
SHOW_TIME("fifo > wait for my_sem_stop_is_acknowledged\n");
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR)
continue; // Restart when interrupted by handler continue; // Restart when interrupted by handler
SHOW_TIME("fifo > get my_sem_stop_is_acknowledged\n");
} }


espeak_ERROR fifo_add_command(t_espeak_command *the_command) espeak_ERROR fifo_add_command(t_espeak_command *the_command)
{ {
ENTER("fifo_add_command");

int a_status = pthread_mutex_lock(&my_mutex); int a_status = pthread_mutex_lock(&my_mutex);
espeak_ERROR a_error = EE_OK; espeak_ERROR a_error = EE_OK;


if (!a_status) { if (!a_status) {
SHOW_TIME("fifo_add_command > locked\n");
a_error = push(the_command); a_error = push(the_command);
SHOW_TIME("fifo_add_command > unlocking\n");
a_status = pthread_mutex_unlock(&my_mutex); a_status = pthread_mutex_unlock(&my_mutex);
} }


if (!a_status && !my_command_is_running && (a_error == EE_OK)) { if (!a_status && !my_command_is_running && (a_error == EE_OK)) {
// quit when command is actually started // quit when command is actually started
// (for possible forthcoming 'end of command' checks) // (for possible forthcoming 'end of command' checks)
SHOW_TIME("fifo_add_command > post my_sem_start_is_required\n");
sem_post(&my_sem_start_is_required); sem_post(&my_sem_start_is_required);
int val = 1; int val = 1;
while (val > 0) { while (val > 0) {
if (a_status != 0) if (a_status != 0)
a_error = EE_INTERNAL_ERROR; a_error = EE_INTERNAL_ERROR;


SHOW_TIME("LEAVE fifo_add_command");
return a_error; return a_error;
} }


espeak_ERROR fifo_add_commands(t_espeak_command *command1, t_espeak_command *command2) espeak_ERROR fifo_add_commands(t_espeak_command *command1, t_espeak_command *command2)
{ {
ENTER("fifo_add_command");

int a_status = pthread_mutex_lock(&my_mutex); int a_status = pthread_mutex_lock(&my_mutex);
espeak_ERROR a_error = EE_OK; espeak_ERROR a_error = EE_OK;


if (!a_status) { if (!a_status) {
SHOW_TIME("fifo_add_commands > locked\n");

if (node_counter+1 >= MAX_NODE_COUNTER) {
SHOW("push > %s\n", "EE_BUFFER_FULL");
if (node_counter+1 >= MAX_NODE_COUNTER)
a_error = EE_BUFFER_FULL; a_error = EE_BUFFER_FULL;
} else {
else {
push(command1); push(command1);
push(command2); push(command2);
} }
SHOW_TIME("fifo_add_command > unlocking\n");
a_status = pthread_mutex_unlock(&my_mutex); a_status = pthread_mutex_unlock(&my_mutex);
} }


if (!a_status && !my_command_is_running && (a_error == EE_OK)) { if (!a_status && !my_command_is_running && (a_error == EE_OK)) {
// quit when one command is actually started // quit when one command is actually started
// (for possible forthcoming 'end of command' checks) // (for possible forthcoming 'end of command' checks)
SHOW_TIME("fifo_add_command > post my_sem_start_is_required\n");
sem_post(&my_sem_start_is_required); sem_post(&my_sem_start_is_required);
int val = 1; int val = 1;
while (val > 0) { while (val > 0) {
if (a_status != 0) if (a_status != 0)
a_error = EE_INTERNAL_ERROR; a_error = EE_INTERNAL_ERROR;


SHOW_TIME("LEAVE fifo_add_commands");
return a_error; return a_error;
} }


espeak_ERROR fifo_stop() espeak_ERROR fifo_stop()
{ {
ENTER("fifo_stop");

int a_command_is_running = 0; int a_command_is_running = 0;
int a_status = pthread_mutex_lock(&my_mutex); int a_status = pthread_mutex_lock(&my_mutex);
SHOW_TIME("fifo_stop > locked\n");
if (a_status != 0) if (a_status != 0)
return EE_INTERNAL_ERROR; return EE_INTERNAL_ERROR;


if (my_command_is_running) { if (my_command_is_running) {
a_command_is_running = 1; a_command_is_running = 1;
my_stop_is_required = 1; my_stop_is_required = 1;
SHOW_TIME("fifo_stop > my_stop_is_required = 1\n");
} }
SHOW_TIME("fifo_stop > unlocking\n");
a_status = pthread_mutex_unlock(&my_mutex); a_status = pthread_mutex_unlock(&my_mutex);
if (a_status != 0) if (a_status != 0)
return EE_INTERNAL_ERROR; return EE_INTERNAL_ERROR;


if (a_command_is_running) { if (a_command_is_running) {
SHOW_TIME("fifo_stop > wait for my_sem_stop_is_acknowledged\n");
while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR) while ((sem_wait(&my_sem_stop_is_acknowledged) == -1) && errno == EINTR)
continue; // Restart when interrupted by handler continue; // Restart when interrupted by handler
SHOW_TIME("fifo_stop > get my_sem_stop_is_acknowledged\n");
} }


SHOW_TIME("fifo_stop > my_stop_is_required = 0\n");
my_stop_is_required = 0; my_stop_is_required = 0;
SHOW_TIME("LEAVE fifo_stop\n");


return EE_OK; return EE_OK;
} }


int fifo_is_busy() int fifo_is_busy()
{ {
SHOW("fifo_is_busy > aResult = %d\n", my_command_is_running);
return my_command_is_running; return my_command_is_running;
} }


static int sleep_until_start_request_or_inactivity() static int sleep_until_start_request_or_inactivity()
{ {
SHOW_TIME("fifo > sleep_until_start_request_or_inactivity > ENTER");
int a_start_is_required = 0; int a_start_is_required = 0;


// Wait for the start request (my_sem_start_is_required). // Wait for the start request (my_sem_start_is_required).


clock_gettime2(&ts); clock_gettime2(&ts);


#ifdef DEBUG_ENABLED
struct timespec to;
to.tv_sec = ts.tv_sec;
to.tv_nsec = ts.tv_nsec;
#endif

add_time_in_ms(&ts, INACTIVITY_TIMEOUT); add_time_in_ms(&ts, INACTIVITY_TIMEOUT);


SHOW("fifo > sleep_until_start_request_or_inactivity > start sem_timedwait (start_is_required) from %d.%09lu to %d.%09lu \n",
to.tv_sec, to.tv_nsec,
ts.tv_sec, ts.tv_nsec);

while ((err = sem_timedwait(&my_sem_start_is_required, &ts)) == -1 while ((err = sem_timedwait(&my_sem_start_is_required, &ts)) == -1
&& errno == EINTR) && errno == EINTR)
continue; continue;


assert(gettimeofday(&tv, NULL) != -1); assert(gettimeofday(&tv, NULL) != -1);
SHOW("fifo > sleep_until_start_request_or_inactivity > stop sem_timedwait (start_is_required, err=%d) %d.%09lu \n", err,
tv.tv_sec, tv.tv_usec*1000);


if (err == 0) if (err == 0)
a_start_is_required = 1; a_start_is_required = 1;
} }
SHOW_TIME("fifo > sleep_until_start_request_or_inactivity > LEAVE");
return a_start_is_required; return a_start_is_required;
} }


static void close_stream() static void close_stream()
{ {
SHOW_TIME("fifo > close_stream > ENTER\n");

// Warning: a wave_close can be already required by // Warning: a wave_close can be already required by
// an external command (espeak_Cancel + fifo_stop), if so: // an external command (espeak_Cancel + fifo_stop), if so:
// my_stop_is_required = 1; // my_stop_is_required = 1;


if (a_stop_is_required) { if (a_stop_is_required) {
// acknowledge the stop request // acknowledge the stop request
SHOW_TIME("fifo > close_stream > post my_sem_stop_is_acknowledged\n");
int a_status = sem_post(&my_sem_stop_is_acknowledged); int a_status = sem_post(&my_sem_stop_is_acknowledged);
assert(a_status != -1); assert(a_status != -1);
} }
} }

SHOW_TIME("fifo > close_stream > LEAVE\n");
} }


static void *say_thread(void *p) static void *say_thread(void *p)
{ {
ENTER("say_thread");

SHOW_TIME("say_thread > post my_sem_stop_is_acknowledged\n");

// announce that thread is started // announce that thread is started
sem_post(&my_sem_stop_is_acknowledged); sem_post(&my_sem_stop_is_acknowledged);


int look_for_inactivity = 0; int look_for_inactivity = 0;


while (1) { while (1) {
SHOW_TIME("say_thread > wait for my_sem_start_is_required\n");

int a_start_is_required = 0; int a_start_is_required = 0;
if (look_for_inactivity) { if (look_for_inactivity) {
a_start_is_required = sleep_until_start_request_or_inactivity(); a_start_is_required = sleep_until_start_request_or_inactivity();
while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR) while ((sem_wait(&my_sem_start_is_required) == -1) && errno == EINTR)
continue; // Restart when interrupted by handler continue; // Restart when interrupted by handler
} }
SHOW_TIME("say_thread > get my_sem_start_is_required\n");


SHOW_TIME("say_thread > my_command_is_running = 1\n");
my_command_is_running = 1; my_command_is_running = 1;


while (my_command_is_running) { while (my_command_is_running) {
SHOW_TIME("say_thread > locking\n");
int a_status = pthread_mutex_lock(&my_mutex); int a_status = pthread_mutex_lock(&my_mutex);
assert(!a_status); assert(!a_status);
t_espeak_command *a_command = (t_espeak_command *)pop(); t_espeak_command *a_command = (t_espeak_command *)pop();


if (a_command == NULL) { if (a_command == NULL) {
SHOW_TIME("say_thread > text empty (talking=0) \n");
a_status = pthread_mutex_unlock(&my_mutex); a_status = pthread_mutex_unlock(&my_mutex);
SHOW_TIME("say_thread > unlocked\n");
SHOW_TIME("say_thread > my_command_is_running = 0\n");
my_command_is_running = 0; my_command_is_running = 0;
} else { } else {
display_espeak_command(a_command);
// purge start semaphore // purge start semaphore
SHOW_TIME("say_thread > purge my_sem_start_is_required\n");
while (0 == sem_trywait(&my_sem_start_is_required)) while (0 == sem_trywait(&my_sem_start_is_required))
; ;


if (my_stop_is_required) {
SHOW_TIME("say_thread > my_command_is_running = 0\n");
if (my_stop_is_required)
my_command_is_running = 0; my_command_is_running = 0;
}
SHOW_TIME("say_thread > unlocking\n");
a_status = pthread_mutex_unlock(&my_mutex); a_status = pthread_mutex_unlock(&my_mutex);


if (my_command_is_running) if (my_command_is_running)
init(1); init(1);


// purge start semaphore // purge start semaphore
SHOW_TIME("say_thread > purge my_sem_start_is_required\n");
while (0 == sem_trywait(&my_sem_start_is_required)) while (0 == sem_trywait(&my_sem_start_is_required))
; ;


// acknowledge the stop request // acknowledge the stop request
SHOW_TIME("say_thread > post my_sem_stop_is_acknowledged\n");
int a_status = sem_post(&my_sem_stop_is_acknowledged); int a_status = sem_post(&my_sem_stop_is_acknowledged);
assert(a_status != -1); assert(a_status != -1);
} }
// and wait for the next start // and wait for the next start
SHOW_TIME("say_thread > wait for my_sem_start_is_required\n");
} }


return NULL; return NULL;


int fifo_is_command_enabled() int fifo_is_command_enabled()
{ {
SHOW("ENTER fifo_is_command_enabled=%d\n", (int)(0 == my_stop_is_required));
return 0 == my_stop_is_required; return 0 == my_stop_is_required;
} }




static espeak_ERROR push(t_espeak_command *the_command) static espeak_ERROR push(t_espeak_command *the_command)
{ {
ENTER("fifo > push");

assert((!head && !tail) || (head && tail)); assert((!head && !tail) || (head && tail));


if (the_command == NULL) {
SHOW("push > command=0x%x\n", NULL);
if (the_command == NULL)
return EE_INTERNAL_ERROR; return EE_INTERNAL_ERROR;
}


if (node_counter >= MAX_NODE_COUNTER) {
SHOW("push > %s\n", "EE_BUFFER_FULL");
if (node_counter >= MAX_NODE_COUNTER)
return EE_BUFFER_FULL; return EE_BUFFER_FULL;
}


node *n = (node *)malloc(sizeof(node)); node *n = (node *)malloc(sizeof(node));
if (n == NULL) if (n == NULL)
tail->data = the_command; tail->data = the_command;


node_counter++; node_counter++;
SHOW("push > counter=%d\n", node_counter);


the_command->state = CS_PENDING; the_command->state = CS_PENDING;
display_espeak_command(the_command);


return EE_OK; return EE_OK;
} }


static t_espeak_command *pop() static t_espeak_command *pop()
{ {
ENTER("fifo > pop");
t_espeak_command *the_command = NULL; t_espeak_command *the_command = NULL;


assert((!head && !tail) || (head && tail)); assert((!head && !tail) || (head && tail));
head = n->next; head = n->next;
free(n); free(n);
node_counter--; node_counter--;
SHOW("pop > command=0x%x (counter=%d)\n", the_command, node_counter);
} }


if (head == NULL) if (head == NULL)
tail = NULL; tail = NULL;


display_espeak_command(the_command);

return the_command; return the_command;
} }


static void init(int process_parameters) static void init(int process_parameters)
{ {
t_espeak_command *c = NULL; t_espeak_command *c = NULL;
ENTER("fifo > init");
c = pop(); c = pop();
while (c != NULL) { while (c != NULL) {
if (process_parameters && (c->type == ET_PARAMETER || c->type == ET_VOICE_NAME || c->type == ET_VOICE_SPEC)) if (process_parameters && (c->type == ET_PARAMETER || c->type == ET_VOICE_NAME || c->type == ET_VOICE_SPEC))


void fifo_terminate() void fifo_terminate()
{ {
ENTER("fifo_terminate");

pthread_cancel(my_thread); pthread_cancel(my_thread);
pthread_join(my_thread, NULL); pthread_join(my_thread, NULL);
pthread_mutex_destroy(&my_mutex); pthread_mutex_destroy(&my_mutex);

+ 0
- 94
src/libespeak-ng/speak_lib.c View File

#include "synthesize.h" #include "synthesize.h"
#include "voice.h" #include "voice.h"
#include "translate.h" #include "translate.h"
#include "debug.h"


#include "fifo.h" #include "fifo.h"
#include "event.h" #include "event.h"


static int dispatch_audio(short *outbuf, int length, espeak_EVENT *event) static int dispatch_audio(short *outbuf, int length, espeak_EVENT *event)
{ {
ENTER("dispatch_audio");

int a_wave_can_be_played = fifo_is_command_enabled(); int a_wave_can_be_played = fifo_is_command_enabled();


#ifdef DEBUG_ENABLED
SHOW("*** dispatch_audio > uid=%d, [write=%p (%d bytes)], sample=%d, a_wave_can_be_played = %d\n",
(event) ? event->unique_identifier : 0, wave_test_get_write_buffer(), 2*length,
(event) ? event->sample : 0,
a_wave_can_be_played);
#endif

switch (my_mode) switch (my_mode)
{ {
case AUDIO_OUTPUT_PLAYBACK: case AUDIO_OUTPUT_PLAYBACK:
espeak_ERROR a_error = event_declare(event); espeak_ERROR a_error = event_declare(event);
if (a_error != EE_BUFFER_FULL) if (a_error != EE_BUFFER_FULL)
break; break;
SHOW_TIME("dispatch_audio > EE_BUFFER_FULL\n");
usleep(10000); usleep(10000);
a_wave_can_be_played = fifo_is_command_enabled(); a_wave_can_be_played = fifo_is_command_enabled();
} }
break; break;
} }


if (!a_wave_can_be_played)
SHOW_TIME("dispatch_audio > synth must be stopped!\n");

SHOW_TIME("LEAVE dispatch_audio\n");

return a_wave_can_be_played == 0; // 1 = stop synthesis, -1 = error return a_wave_can_be_played == 0; // 1 = stop synthesis, -1 = error
} }


event = NULL; event = NULL;
else { else {
event = event_list + i; event = event_list + i;
#ifdef DEBUG_ENABLED
SHOW("Synthesize: event->sample(%d) + %d = %d\n", event->sample, the_write_pos, event->sample + the_write_pos);
#endif
event->sample += the_write_pos; event->sample += the_write_pos;
} }
#ifdef DEBUG_ENABLED
SHOW("*** Synthesize: i=%d (event_list_ix=%d), length=%d\n", i, event_list_ix, length);
#endif
finished = dispatch_audio((short *)outbuf, length, event); finished = dispatch_audio((short *)outbuf, length, event);
length = 0; // the wave data are played once. length = 0; // the wave data are played once.
i++; i++;


int sync_espeak_terminated_msg(uint32_t unique_identifier, void *user_data) int sync_espeak_terminated_msg(uint32_t unique_identifier, void *user_data)
{ {
ENTER("sync_espeak_terminated_msg");

int finished = 0; int finished = 0;


memset(event_list, 0, 2*sizeof(espeak_EVENT)); memset(event_list, 0, 2*sizeof(espeak_EVENT));
espeak_ERROR a_error = event_declare(event_list); espeak_ERROR a_error = event_declare(event_list);
if (a_error != EE_BUFFER_FULL) if (a_error != EE_BUFFER_FULL)
break; break;
SHOW_TIME("sync_espeak_terminated_msg > EE_BUFFER_FULL\n");
usleep(10000); usleep(10000);
} }
} else { } else {
uint32_t a_write_pos = 0; uint32_t a_write_pos = 0;
#endif #endif


#ifdef DEBUG_ENABLED
ENTER("Synthesize");
if (text)
SHOW("Synthesize > uid=%d, flags=%d, >>>text=%s<<<\n", unique_identifier, flags, text);
#endif

if ((outbuf == NULL) || (event_list == NULL)) if ((outbuf == NULL) || (event_list == NULL))
return EE_INTERNAL_ERROR; // espeak_Initialize() has not been called return EE_INTERNAL_ERROR; // espeak_Initialize() has not been called


} }


for (;;) { for (;;) {
#ifdef DEBUG_ENABLED
SHOW("Synthesize > %s\n", "for (next)");
#endif
out_ptr = outbuf; out_ptr = outbuf;
out_end = &outbuf[outbuf_size]; out_end = &outbuf[outbuf_size];
event_list_ix = 0; event_list_ix = 0;
return EE_OK; return EE_OK;
} }


#ifdef DEBUG_ENABLED
static const char *label[] = {
"END_OF_EVENT_LIST",
"WORD",
"SENTENCE",
"MARK",
"PLAY",
"END",
"MSG_TERMINATED",
"PHONEME",
"SAMPLERATE",
"??"
};
#endif

void MarkerEvent(int type, unsigned int char_position, int value, int value2, unsigned char *out_ptr) void MarkerEvent(int type, unsigned int char_position, int value, int value2, unsigned char *out_ptr)
{ {
// type: 1=word, 2=sentence, 3=named mark, 4=play audio, 5=end, 7=phoneme // type: 1=word, 2=sentence, 3=named mark, 4=play audio, 5=end, 7=phoneme
ENTER("MarkerEvent");
espeak_EVENT *ep; espeak_EVENT *ep;
double time; double time;


ep->audio_position = (int)time; ep->audio_position = (int)time;
ep->sample = (count_samples + mbrola_delay + (out_ptr - out_start)/2); ep->sample = (count_samples + mbrola_delay + (out_ptr - out_start)/2);


#ifdef DEBUG_ENABLED
SHOW("MarkerEvent > count_samples=%d, out_ptr=%x, out_start=0x%x\n", count_samples, out_ptr, out_start);
SHOW("*** MarkerEvent > type=%s, uid=%d, text_pos=%d, length=%d, audio_position=%d, sample=%d\n",
label[ep->type], ep->unique_identifier, ep->text_position, ep->length,
ep->audio_position, ep->sample);
#endif

if ((type == espeakEVENT_MARK) || (type == espeakEVENT_PLAY)) if ((type == espeakEVENT_MARK) || (type == espeakEVENT_PLAY))
ep->id.name = &namedata[value]; ep->id.name = &namedata[value];
else if (type == espeakEVENT_PHONEME) { else if (type == espeakEVENT_PHONEME) {
unsigned int position, espeak_POSITION_TYPE position_type, unsigned int position, espeak_POSITION_TYPE position_type,
unsigned int end_position, unsigned int flags, void *user_data) unsigned int end_position, unsigned int flags, void *user_data)
{ {
#ifdef DEBUG_ENABLED
ENTER("sync_espeak_Synth");
SHOW("sync_espeak_Synth > position=%d, position_type=%d, end_position=%d, flags=%d, user_data=0x%x, text=%s\n", position, position_type, end_position, flags, user_data, text);
#endif

espeak_ERROR aStatus; espeak_ERROR aStatus;


InitText(flags); InitText(flags);
wave_flush(my_audio); wave_flush(my_audio);
#endif #endif


SHOW_TIME("LEAVE sync_espeak_Synth");
return aStatus; return aStatus;
} }


end_character_position = end_position; end_character_position = end_position;


aStatus = Synthesize(unique_identifier, text, flags | espeakSSML); aStatus = Synthesize(unique_identifier, text, flags | espeakSSML);
SHOW_TIME("LEAVE sync_espeak_Synth_Mark");


return aStatus; return aStatus;
} }


ESPEAK_API void espeak_SetSynthCallback(t_espeak_callback *SynthCallback) ESPEAK_API void espeak_SetSynthCallback(t_espeak_callback *SynthCallback)
{ {
ENTER("espeak_SetSynthCallback");
synth_callback = SynthCallback; synth_callback = SynthCallback;
#ifdef USE_ASYNC #ifdef USE_ASYNC
event_set_callback(synth_callback); event_set_callback(synth_callback);


ESPEAK_API void espeak_SetUriCallback(int (*UriCallback)(int, const char *, const char *)) ESPEAK_API void espeak_SetUriCallback(int (*UriCallback)(int, const char *, const char *))
{ {
ENTER("espeak_SetUriCallback");
uri_callback = UriCallback; uri_callback = UriCallback;
} }




ESPEAK_API int espeak_Initialize(espeak_AUDIO_OUTPUT output_type, int buf_length, const char *path, int options) ESPEAK_API int espeak_Initialize(espeak_AUDIO_OUTPUT output_type, int buf_length, const char *path, int options)
{ {
ENTER("espeak_Initialize");
int param; int param;


// It seems that the wctype functions don't work until the locale has been set // It seems that the wctype functions don't work until the locale has been set
unsigned int end_position, unsigned int flags, unsigned int end_position, unsigned int flags,
unsigned int *unique_identifier, void *user_data) unsigned int *unique_identifier, void *user_data)
{ {
#ifdef DEBUG_ENABLED
ENTER("espeak_Synth");
SHOW("espeak_Synth > position=%d, position_type=%d, end_position=%d, flags=%d, user_data=0x%x, text=%s\n", position, position_type, end_position, flags, user_data, text);
#endif

if (f_logespeak) { if (f_logespeak) {
fprintf(f_logespeak, "\nSYNTH posn %d %d %d flags 0x%x\n%s\n", position, end_position, position_type, flags, (const char *)text); fprintf(f_logespeak, "\nSYNTH posn %d %d %d flags 0x%x\n%s\n", position, end_position, position_type, flags, (const char *)text);
fflush(f_logespeak); fflush(f_logespeak);
unsigned int *unique_identifier, unsigned int *unique_identifier,
void *user_data) void *user_data)
{ {
#ifdef DEBUG_ENABLED
ENTER("espeak_Synth_Mark");
SHOW("espeak_Synth_Mark > index_mark=%s, end_position=%d, flags=%d, text=%s\n", index_mark, end_position, flags, text);
#endif

espeak_ERROR a_error = EE_OK; espeak_ERROR a_error = EE_OK;
static unsigned int temp_identifier; static unsigned int temp_identifier;




ESPEAK_API espeak_ERROR espeak_Key(const char *key) ESPEAK_API espeak_ERROR espeak_Key(const char *key)
{ {
ENTER("espeak_Key");
// symbolic name, symbolicname_character - is there a system resource of symbolicnames per language // symbolic name, symbolicname_character - is there a system resource of symbolicnames per language


if (f_logespeak) if (f_logespeak)


ESPEAK_API espeak_ERROR espeak_Char(wchar_t character) ESPEAK_API espeak_ERROR espeak_Char(wchar_t character)
{ {
ENTER("espeak_Char");
// is there a system resource of character names per language? // is there a system resource of character names per language?


if (f_logespeak) if (f_logespeak)


ESPEAK_API espeak_ERROR espeak_SetVoiceByName(const char *name) ESPEAK_API espeak_ERROR espeak_SetVoiceByName(const char *name)
{ {
ENTER("espeak_SetVoiceByName");

return SetVoiceByName(name); return SetVoiceByName(name);
} }


ESPEAK_API espeak_ERROR espeak_SetVoiceByProperties(espeak_VOICE *voice_selector) ESPEAK_API espeak_ERROR espeak_SetVoiceByProperties(espeak_VOICE *voice_selector)
{ {
ENTER("espeak_SetVoiceByProperties");

return SetVoiceByProperties(voice_selector); return SetVoiceByProperties(voice_selector);
} }


ESPEAK_API int espeak_GetParameter(espeak_PARAMETER parameter, int current) ESPEAK_API int espeak_GetParameter(espeak_PARAMETER parameter, int current)
{ {
ENTER("espeak_GetParameter");
// current: 0=default value, 1=current value // current: 0=default value, 1=current value
if (current) if (current)
return param_stack[0].parameter[parameter]; return param_stack[0].parameter[parameter];


ESPEAK_API espeak_ERROR espeak_SetParameter(espeak_PARAMETER parameter, int value, int relative) ESPEAK_API espeak_ERROR espeak_SetParameter(espeak_PARAMETER parameter, int value, int relative)
{ {
ENTER("espeak_SetParameter");

if (f_logespeak) if (f_logespeak)
fprintf(f_logespeak, "SETPARAM %d %d %d\n", parameter, value, relative); fprintf(f_logespeak, "SETPARAM %d %d %d\n", parameter, value, relative);
#ifdef USE_ASYNC #ifdef USE_ASYNC


ESPEAK_API espeak_ERROR espeak_SetPunctuationList(const wchar_t *punctlist) ESPEAK_API espeak_ERROR espeak_SetPunctuationList(const wchar_t *punctlist)
{ {
ENTER("espeak_SetPunctuationList");
// Set the list of punctuation which are spoken for "some". // Set the list of punctuation which are spoken for "some".


#ifdef USE_ASYNC #ifdef USE_ASYNC


ESPEAK_API void espeak_SetPhonemeTrace(int phonememode, FILE *stream) ESPEAK_API void espeak_SetPhonemeTrace(int phonememode, FILE *stream)
{ {
ENTER("espeak_SetPhonemes");
/* phonememode: Controls the output of phoneme symbols for the text /* phonememode: Controls the output of phoneme symbols for the text
bits 0-2: bits 0-2:
value=0 No phoneme output (default) value=0 No phoneme output (default)


ESPEAK_API void espeak_CompileDictionary(const char *path, FILE *log, int flags) ESPEAK_API void espeak_CompileDictionary(const char *path, FILE *log, int flags)
{ {
ENTER("espeak_CompileDictionary");
CompileDictionary(path, dictionary_name, log, NULL, flags); CompileDictionary(path, dictionary_name, log, NULL, flags);
} }


ESPEAK_API espeak_ERROR espeak_Cancel(void) ESPEAK_API espeak_ERROR espeak_Cancel(void)
{ {
#ifdef USE_ASYNC #ifdef USE_ASYNC
ENTER("espeak_Cancel");
fifo_stop(); fifo_stop();
event_clear_all(); event_clear_all();


if (my_mode == AUDIO_OUTPUT_PLAYBACK) if (my_mode == AUDIO_OUTPUT_PLAYBACK)
wave_close(my_audio); wave_close(my_audio);
SHOW_TIME("espeak_Cancel > LEAVE");
#endif #endif
embedded_value[EMBED_T] = 0; // reset echo for pronunciation announcements embedded_value[EMBED_T] = 0; // reset echo for pronunciation announcements


{ {
espeak_ERROR berr = err; espeak_ERROR berr = err;
#ifdef USE_ASYNC #ifdef USE_ASYNC
SHOW_TIME("espeak_Synchronize > ENTER");
while (espeak_IsPlaying()) while (espeak_IsPlaying())
usleep(20000); usleep(20000);
#endif #endif
err = EE_OK; err = EE_OK;
SHOW_TIME("espeak_Synchronize > LEAVE");
return berr; return berr;
} }




ESPEAK_API espeak_ERROR espeak_Terminate(void) ESPEAK_API espeak_ERROR espeak_Terminate(void)
{ {
ENTER("espeak_Terminate");
#ifdef USE_ASYNC #ifdef USE_ASYNC
fifo_stop(); fifo_stop();
fifo_terminate(); fifo_terminate();

+ 16
- 113
src/libespeak-ng/wave.c View File

#include <unistd.h> #include <unistd.h>
#endif #endif
#include "wave.h" #include "wave.h"
#include "debug.h"


#ifdef NEED_STRUCT_TIMESPEC #ifdef NEED_STRUCT_TIMESPEC
#define HAVE_STRUCT_TIMESPEC 1 #define HAVE_STRUCT_TIMESPEC 1
myRead = myBuffer; myRead = myBuffer;
memset(myBuffer, 0, BUFFER_LENGTH); memset(myBuffer, 0, BUFFER_LENGTH);
myReadPosition = myWritePosition = 0; myReadPosition = myWritePosition = 0;
SHOW("init_buffer > myRead=0x%x, myWrite=0x%x, BUFFER_LENGTH=0x%x, myReadPosition = myWritePosition = 0\n", (int)myRead, (int)myWrite, BUFFER_LENGTH);
} }


static unsigned int get_used_mem() static unsigned int get_used_mem()
used = aWrite - aRead; used = aWrite - aRead;
else else
used = aWrite + BUFFER_LENGTH - aRead; used = aWrite + BUFFER_LENGTH - aRead;
SHOW("get_used_mem > %d\n", used);


return used; return used;
} }
static void start_stream() static void start_stream()
{ {
PaError err; PaError err;
SHOW_TIME("start_stream");


my_stream_could_start = 0; my_stream_could_start = 0;
mInCallbackFinishedState = false; mInCallbackFinishedState = false;


err = Pa_StartStream(pa_stream); err = Pa_StartStream(pa_stream);
SHOW("start_stream > Pa_StartStream=%d (%s)\n", err, Pa_GetErrorText(err));


#if USE_PORTAUDIO == 19 #if USE_PORTAUDIO == 19
if (err == paStreamIsNotStopped) { if (err == paStreamIsNotStopped) {
SHOW_TIME("start_stream > restart stream (begin)");
// not sure why we need this, but PA v19 seems to need it // not sure why we need this, but PA v19 seems to need it
err = Pa_StopStream(pa_stream); err = Pa_StopStream(pa_stream);
SHOW("start_stream > Pa_StopStream=%d (%s)\n", err, Pa_GetErrorText(err));
err = Pa_StartStream(pa_stream); err = Pa_StartStream(pa_stream);
SHOW("start_stream > Pa_StartStream=%d (%s)\n", err, Pa_GetErrorText(err));
SHOW_TIME("start_stream > restart stream (end)");
} }
#endif #endif
} }
size_t n = out_channels*sizeof(uint16_t)*framesPerBuffer; size_t n = out_channels*sizeof(uint16_t)*framesPerBuffer;


myReadPosition += framesPerBuffer; myReadPosition += framesPerBuffer;
SHOW("pa_callback > myReadPosition=%u, framesPerBuffer=%lu (n=0x%x) \n", (int)myReadPosition, framesPerBuffer, n);


if (aWrite >= myRead) { if (aWrite >= myRead) {
if ((size_t)(aWrite - myRead) >= n) { if ((size_t)(aWrite - myRead) >= n) {
memcpy(outputBuffer, myRead, n); memcpy(outputBuffer, myRead, n);
myRead += n; myRead += n;
} else { } else {
SHOW_TIME("pa_callback > underflow");
// underflow
aResult = 1; // paComplete; aResult = 1; // paComplete;
mInCallbackFinishedState = true; mInCallbackFinishedState = true;
size_t aUsedMem = 0; size_t aUsedMem = 0;
myRead += n; myRead += n;
} else if ((size_t)(aWrite + BUFFER_LENGTH - myRead) >= n) { } else if ((size_t)(aWrite + BUFFER_LENGTH - myRead) >= n) {
int aTopMem = myBuffer + BUFFER_LENGTH - myRead; int aTopMem = myBuffer + BUFFER_LENGTH - myRead;
if (aTopMem) {
SHOW("pa_callback > myRead=0x%x, aTopMem=0x%x\n", (int)myRead, (int)aTopMem);
if (aTopMem)
memcpy(outputBuffer, myRead, aTopMem); memcpy(outputBuffer, myRead, aTopMem);
}
int aRest = n - aTopMem; int aRest = n - aTopMem;
if (aRest) { if (aRest) {
SHOW("pa_callback > myRead=0x%x, aRest=0x%x\n", (int)myRead, (int)aRest);
char *p = (char *)outputBuffer + aTopMem; char *p = (char *)outputBuffer + aTopMem;
memcpy(p, myBuffer, aRest); memcpy(p, myBuffer, aRest);
} }
myRead = myBuffer + aRest; myRead = myBuffer + aRest;
} else { } else {
SHOW_TIME("pa_callback > underflow");
// underflow
aResult = 1; // paComplete; aResult = 1; // paComplete;


int aTopMem = myBuffer + BUFFER_LENGTH - myRead; int aTopMem = myBuffer + BUFFER_LENGTH - myRead;
if (aTopMem) {
SHOW("pa_callback > myRead=0x%x, aTopMem=0x%x\n", (int)myRead, (int)aTopMem);
if (aTopMem)
memcpy(outputBuffer, myRead, aTopMem); memcpy(outputBuffer, myRead, aTopMem);
}
int aRest = aWrite - myBuffer; int aRest = aWrite - myBuffer;
if (aRest) { if (aRest) {
SHOW("pa_callback > myRead=0x%x, aRest=0x%x\n", (int)myRead, (int)aRest);
char *p = (char *)outputBuffer + aTopMem; char *p = (char *)outputBuffer + aTopMem;
memcpy(p, myBuffer, aRest); memcpy(p, myBuffer, aRest);
} }
} }
} }


SHOW("pa_callback > myRead=%x\n", (int)myRead);

#ifdef ARCH_BIG #ifdef ARCH_BIG
// BIG-ENDIAN, swap the order of bytes in each sound sample in the portaudio buffer // BIG-ENDIAN, swap the order of bytes in each sound sample in the portaudio buffer
int c; int c;


void wave_flush(void *theHandler) void wave_flush(void *theHandler)
{ {
ENTER("wave_flush");

if (my_stream_could_start) if (my_stream_could_start)
start_stream(); start_stream();
} }


static int wave_open_sound() static int wave_open_sound()
{ {
ENTER("wave_open_sound");

PaError err = paNoError; PaError err = paNoError;
PaError active; PaError active;


#endif #endif


if (active == 1) if (active == 1)
SHOW_TIME("wave_open_sound > already active");
return 0; return 0;
if (active < 0) { if (active < 0) {
out_channels = 1; out_channels = 1;
paNoFlag, paNoFlag,
pa_callback, (void *)userdata); pa_callback, (void *)userdata);


SHOW("wave_open_sound > Pa_OpenDefaultStream(1): err=%d (%s)\n", err, Pa_GetErrorText(err));

if (err == paInvalidChannelCount) { if (err == paInvalidChannelCount) {
SHOW_TIME("wave_open_sound > try stereo");
// failed to open with mono, try stereo // failed to open with mono, try stereo
out_channels = 2; out_channels = 2;
PaError err = Pa_OpenStream(&pa_stream, PaError err = Pa_OpenStream(&pa_stream,
wave_samplerate, FRAMES_PER_BUFFER, 0, wave_samplerate, FRAMES_PER_BUFFER, 0,
paNoFlag, paNoFlag,
pa_callback, (void *)userdata); pa_callback, (void *)userdata);
SHOW("wave_open_sound > Pa_OpenDefaultStream(2): err=%d (%s)\n", err, Pa_GetErrorText(err));
err = 0; // avoid warning err = 0; // avoid warning
} }
mInCallbackFinishedState = false; // v18 only mInCallbackFinishedState = false; // v18 only
(void *)userdata); (void *)userdata);
} }
if (err == paInvalidChannelCount) { if (err == paInvalidChannelCount) {
SHOW_TIME("wave_open_sound > try stereo");
// failed to open with mono, try stereo // failed to open with mono, try stereo
out_channels = 2; out_channels = 2;
myOutputParameters.channelCount = out_channels; myOutputParameters.channelCount = out_channels;
#endif #endif
} }


SHOW("wave_open_sound > %s\n", "LEAVE");

return err != paNoError; return err != paNoError;
} }


// deviceInfo = Pa_GetDeviceInfo(selectedDevice); // deviceInfo = Pa_GetDeviceInfo(selectedDevice);
if (deviceInfo) { if (deviceInfo) {
double aLatency = deviceInfo->defaultLowOutputLatency; double aLatency = deviceInfo->defaultLowOutputLatency;
myOutputParameters.suggestedLatency = aLatency; // for faster response ?
SHOW("Device=%d, myOutputParameters.suggestedLatency=%f, aCoeff=%f\n",
selectedDevice,
myOutputParameters.suggestedLatency,
aCoeff);
} else {
myOutputParameters.suggestedLatency = aLatency; // for faster response ?
} else
myOutputParameters.suggestedLatency = (double)0.1; // 100ms myOutputParameters.suggestedLatency = (double)0.1; // 100ms
SHOW("Device=%d, myOutputParameters.suggestedLatency=%f (default)\n",
selectedDevice,
myOutputParameters.suggestedLatency);
}


myOutputParameters.hostApiSpecificStreamInfo = NULL; myOutputParameters.hostApiSpecificStreamInfo = NULL;
} }


static void select_device(const char *the_api) static void select_device(const char *the_api)
{ {
ENTER("select_device");

#if (USE_PORTAUDIO == 19) #if (USE_PORTAUDIO == 19)
int numDevices = Pa_GetDeviceCount(); int numDevices = Pa_GetDeviceCount();
if (numDevices < 0) {
SHOW("ERROR: Pa_CountDevices returned 0x%x\n", numDevices);
if (numDevices < 0)
assert(0); assert(0);
}


PaDeviceIndex i = 0, selectedIndex = 0, defaultAlsaIndex = numDevices; PaDeviceIndex i = 0, selectedIndex = 0, defaultAlsaIndex = numDevices;
const PaDeviceInfo *deviceInfo = NULL; const PaDeviceInfo *deviceInfo = NULL;
const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(defaultAlsaIndex); const PaDeviceInfo *deviceInfo = Pa_GetDeviceInfo(defaultAlsaIndex);
update_output_parameters(defaultAlsaIndex, deviceInfo); update_output_parameters(defaultAlsaIndex, deviceInfo);
if (Pa_IsFormatSupported(NULL, &myOutputParameters, wave_samplerate) == 0) { if (Pa_IsFormatSupported(NULL, &myOutputParameters, wave_samplerate) == 0) {
SHOW("select_device > ALSA (default), name=%s (#%d)\n", deviceInfo->name, defaultAlsaIndex);
selectedIndex = defaultAlsaIndex; selectedIndex = defaultAlsaIndex;
selectedDeviceInfo = deviceInfo; selectedDeviceInfo = deviceInfo;
break; break;


// if the default output device does not match, // if the default output device does not match,
// look for the device with the highest number of output channels // look for the device with the highest number of output channels
SHOW("select_device > ALSA, i=%d (numDevices=%d)\n", i, numDevices);


update_output_parameters(i, deviceInfo); update_output_parameters(i, deviceInfo);


if (Pa_IsFormatSupported(NULL, &myOutputParameters, wave_samplerate) == 0) { if (Pa_IsFormatSupported(NULL, &myOutputParameters, wave_samplerate) == 0) {
SHOW("select_device > ALSA, name=%s (#%d)\n", deviceInfo->name, i);

if (!selectedDeviceInfo if (!selectedDeviceInfo
|| (selectedDeviceInfo->maxOutputChannels < deviceInfo->maxOutputChannels)) { || (selectedDeviceInfo->maxOutputChannels < deviceInfo->maxOutputChannels)) {
selectedIndex = i; selectedIndex = i;


int wave_init(int srate) int wave_init(int srate)
{ {
ENTER("wave_init");
PaError err; PaError err;


pa_stream = NULL; pa_stream = NULL;
// PortAudio sound output library // PortAudio sound output library
err = Pa_Initialize(); err = Pa_Initialize();
pa_init_err = err; pa_init_err = err;
if (err != paNoError)
SHOW_TIME("wave_init > Failed to initialise the PortAudio sound");
return err == paNoError; return err == paNoError;
} }


void *wave_open(const char *the_api) void *wave_open(const char *the_api)
{ {
ENTER("wave_open");
static int once = 0; static int once = 0;


if (!once) { if (!once) {
if ((src != NULL) && dest != NULL) { if ((src != NULL) && dest != NULL) {
// copy for one channel (mono)? // copy for one channel (mono)?
if (out_channels == 1) { if (out_channels == 1) {
SHOW("copyBuffer > 1 channel > memcpy %x (%d bytes)\n", (int)myWrite, theSizeInBytes);
memcpy(dest, src, theSizeInBytes); memcpy(dest, src, theSizeInBytes);
bytes_written = theSizeInBytes; bytes_written = theSizeInBytes;
} else { // copy for 2 channels (stereo) } else { // copy for 2 channels (stereo)
SHOW("copyBuffer > 2 channels > memcpy %x (%d bytes)\n", (int)myWrite, theSizeInBytes);
i = 0; i = 0;
a_dest = (uint16_t *)dest; a_dest = (uint16_t *)dest;
a_src = (uint16_t *)src; a_src = (uint16_t *)src;


size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize) size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize)
{ {
ENTER("wave_write");
size_t bytes_written = 0; size_t bytes_written = 0;
// space in ringbuffer for the sample needed: 1x mono channel but 2x for 1 stereo channel // space in ringbuffer for the sample needed: 1x mono channel but 2x for 1 stereo channel
size_t bytes_to_write = (out_channels == 1) ? theSize : theSize*2; size_t bytes_to_write = (out_channels == 1) ? theSize : theSize*2;
my_stream_could_start = 0; my_stream_could_start = 0;


if (pa_stream == NULL) { if (pa_stream == NULL) {
SHOW_TIME("wave_write > wave_open_sound\n");
if (0 != wave_open_sound()) {
SHOW_TIME("wave_write > wave_open_sound fails!");
if (0 != wave_open_sound())
return 0; return 0;
}
my_stream_could_start = 1; my_stream_could_start = 1;
} else if (!wave_is_busy(NULL)) } else if (!wave_is_busy(NULL))
my_stream_could_start = 1; my_stream_could_start = 1;


size_t aTotalFreeMem = 0; size_t aTotalFreeMem = 0;
char *aRead = myRead; char *aRead = myRead;
SHOW("wave_write > aRead=%x, myWrite=%x\n", (int)aRead, (int)myWrite);


while (1) { while (1) {
if (my_callback_is_output_enabled && (0 == my_callback_is_output_enabled())) {
SHOW_TIME("wave_write > my_callback_is_output_enabled: no!");
if (my_callback_is_output_enabled && (0 == my_callback_is_output_enabled()))
return 0; return 0;
}


aRead = myRead; aRead = myRead;


if (aTotalFreeMem >= bytes_to_write) if (aTotalFreeMem >= bytes_to_write)
break; break;


SHOW("wave_write > wait: aTotalFreeMem=%d\n", aTotalFreeMem);
SHOW("wave_write > aRead=%x, myWrite=%x\n", (int)aRead, (int)myWrite);
usleep(10000); usleep(10000);
} }




// write pointer is ahead the read pointer? // write pointer is ahead the read pointer?
if (myWrite >= aRead) { if (myWrite >= aRead) {
SHOW_TIME("wave_write > myWrite >= aRead");
// determine remaining free memory to the end of the ringbuffer // determine remaining free memory to the end of the ringbuffer
size_t aFreeMem = myBuffer + BUFFER_LENGTH - myWrite; size_t aFreeMem = myBuffer + BUFFER_LENGTH - myWrite;
// is enough linear space available (regardless 1 or 2 channels)? // is enough linear space available (regardless 1 or 2 channels)?
myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer+aFreeMem, theSize - aFreeMem); myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer+aFreeMem, theSize - aFreeMem);
} }
} }
} else { // read pointer is ahead the write pointer
SHOW_TIME("wave_write > myWrite <= aRead");
} else // read pointer is ahead the write pointer
myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer, theSize); myWrite += copyBuffer(myWrite, theMono16BitsWaveBuffer, theSize);
}


bytes_written = bytes_to_write; bytes_written = bytes_to_write;
myWritePosition += theSize/sizeof(uint16_t); // add number of samples myWritePosition += theSize/sizeof(uint16_t); // add number of samples
if (my_stream_could_start && (get_used_mem() >= out_channels * sizeof(uint16_t) * FRAMES_PER_BUFFER)) if (my_stream_could_start && (get_used_mem() >= out_channels * sizeof(uint16_t) * FRAMES_PER_BUFFER))
start_stream(); start_stream();


SHOW_TIME("wave_write > LEAVE");

return bytes_written; return bytes_written;
} }


int wave_close(void *theHandler) int wave_close(void *theHandler)
{ {
SHOW_TIME("wave_close > ENTER");

static int aStopStreamCount = 0; static int aStopStreamCount = 0;


#if (USE_PORTAUDIO == 19) #if (USE_PORTAUDIO == 19)
if (pa_stream == NULL) {
SHOW_TIME("wave_close > LEAVE (NULL stream)");
if (pa_stream == NULL)
return 0; return 0;
}


if (Pa_IsStreamStopped(pa_stream)) {
SHOW_TIME("wave_close > LEAVE (stopped)");
if (Pa_IsStreamStopped(pa_stream))
return 0; return 0;
}
#else #else
if (pa_stream == NULL) {
SHOW_TIME("wave_close > LEAVE (NULL stream)");
if (pa_stream == NULL)
return 0; return 0;
}


if (Pa_StreamActive(pa_stream) == false && mInCallbackFinishedState == false) {
SHOW_TIME("wave_close > LEAVE (not active)");
if (Pa_StreamActive(pa_stream) == false && mInCallbackFinishedState == false)
return 0; return 0;
}
#endif #endif


// Avoid race condition by making sure this function only // Avoid race condition by making sure this function only
// gets called once at a time // gets called once at a time
aStopStreamCount++; aStopStreamCount++;
if (aStopStreamCount != 1) {
SHOW_TIME("wave_close > LEAVE (stopStreamCount)");
if (aStopStreamCount != 1)
return 0; return 0;
}


// Comment from Audacity-1.2.4b adapted to the eSpeak context. // Comment from Audacity-1.2.4b adapted to the eSpeak context.
// //
#if (USE_PORTAUDIO == 19) #if (USE_PORTAUDIO == 19)
if (pa_stream) { if (pa_stream) {
Pa_AbortStream(pa_stream); Pa_AbortStream(pa_stream);
SHOW_TIME("wave_close > Pa_AbortStream (end)");


Pa_CloseStream(pa_stream); Pa_CloseStream(pa_stream);
SHOW_TIME("wave_close > Pa_CloseStream (end)");
pa_stream = NULL; pa_stream = NULL;
mInCallbackFinishedState = false; mInCallbackFinishedState = false;
} }
if (pa_stream) { if (pa_stream) {
if (mInCallbackFinishedState) { if (mInCallbackFinishedState) {
Pa_StopStream(pa_stream); Pa_StopStream(pa_stream);
SHOW_TIME("wave_close > Pa_StopStream (end)");
} else { } else {
Pa_AbortStream(pa_stream); Pa_AbortStream(pa_stream);
SHOW_TIME("wave_close > Pa_AbortStream (end)");
} }
Pa_CloseStream(pa_stream); Pa_CloseStream(pa_stream);
SHOW_TIME("wave_close > Pa_CloseStream (end)");


pa_stream = NULL; pa_stream = NULL;
mInCallbackFinishedState = false; mInCallbackFinishedState = false;
init_buffer(); init_buffer();


aStopStreamCount = 0; // last action aStopStreamCount = 0; // last action
SHOW_TIME("wave_close > LEAVE");
return 0; return 0;
} }


{ {
PaError active = 0; PaError active = 0;


SHOW_TIME("wave_is_busy");

if (pa_stream) { if (pa_stream) {
#if USE_PORTAUDIO == 18 #if USE_PORTAUDIO == 18
active = Pa_StreamActive(pa_stream) active = Pa_StreamActive(pa_stream)
#endif #endif
} }


SHOW("wave_is_busy: %d\n", active);

return active == 1; return active == 1;
} }


void wave_terminate() void wave_terminate()
{ {
ENTER("wave_terminate");

Pa_Terminate(); Pa_Terminate();
} }


uint32_t wave_get_read_position(void *theHandler) uint32_t wave_get_read_position(void *theHandler)
{ {
SHOW("wave_get_read_position > myReadPosition=%u\n", myReadPosition);
return myReadPosition; return myReadPosition;
} }


uint32_t wave_get_write_position(void *theHandler) uint32_t wave_get_write_position(void *theHandler)
{ {
SHOW("wave_get_write_position > myWritePosition=%u\n", myWritePosition);
return myWritePosition; return myWritePosition;
} }


{ {
double a_time = 0; double a_time = 0;


if (!time || !pa_stream) {
SHOW("event get_remaining_time> %s\n", "audio device not available");
if (!time || !pa_stream)
return -1; return -1;
}


if (sample > myReadPosition) { if (sample > myReadPosition) {
// TBD: take in account time suplied by portaudio V18 API // TBD: take in account time suplied by portaudio V18 API
} else } else
a_time = 0; a_time = 0;


SHOW("wave_get_remaining_time > sample=%d, time=%d\n", sample, (uint32_t)a_time);

*time = (uint32_t)a_time; *time = (uint32_t)a_time;


return 0; return 0;


uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms; uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms;
while (t_ns >= ONE_BILLION) { while (t_ns >= ONE_BILLION) {
SHOW("event > add_time_in_ms ns: %d sec %Lu nsec \n", ts->tv_sec, t_ns);
ts->tv_sec += 1; ts->tv_sec += 1;
t_ns -= ONE_BILLION; t_ns -= ONE_BILLION;
} }

+ 22
- 110
src/libespeak-ng/wave_pulse.c View File

#include <unistd.h> #include <unistd.h>
#endif #endif
#include "wave.h" #include "wave.h"
#include "debug.h"


enum { ONE_BILLION = 1000000000 }; enum { ONE_BILLION = 1000000000 };


!context || pa_context_get_state(context) != PA_CONTEXT_READY || \ !context || pa_context_get_state(context) != PA_CONTEXT_READY || \
!stream || pa_stream_get_state(stream) != PA_STREAM_READY) { \ !stream || pa_stream_get_state(stream) != PA_STREAM_READY) { \
if (warn) \ if (warn) \
SHOW("Connection died: %s\n", context ? pa_strerror(pa_context_errno(context)) : "NULL"); \
fprintf(stderr, "Connection died: %s\n", context ? pa_strerror(pa_context_errno(context)) : "NULL"); \
goto label; \ goto label; \
} \ } \
} while (0); } while (0);


#define CHECK_CONNECTED_NO_RETVAL(id) \ #define CHECK_CONNECTED_NO_RETVAL(id) \
do { \ do { \
if (!connected) { SHOW("CHECK_CONNECTED_NO_RETVAL: !pulse_connected\n", ""); return; } \
if (!connected) { return; } \
} while (0); } while (0);


static void subscribe_cb(struct pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata) static void subscribe_cb(struct pa_context *c, enum pa_subscription_event_type t, uint32_t index, void *userdata)
{ {
ENTER(__FUNCTION__);

assert(c); assert(c);


if (!stream || if (!stream ||


static void context_state_cb(pa_context *c, void *userdata) static void context_state_cb(pa_context *c, void *userdata)
{ {
ENTER(__FUNCTION__);
assert(c); assert(c);


switch (pa_context_get_state(c)) switch (pa_context_get_state(c))


static void stream_state_cb(pa_stream *s, void *userdata) static void stream_state_cb(pa_stream *s, void *userdata)
{ {
ENTER(__FUNCTION__);
assert(s); assert(s);


switch (pa_stream_get_state(s)) switch (pa_stream_get_state(s))


static void stream_success_cb(pa_stream *s, int success, void *userdata) static void stream_success_cb(pa_stream *s, int success, void *userdata)
{ {
ENTER(__FUNCTION__);
assert(s); assert(s);


if (userdata) if (userdata)


static void context_success_cb(pa_context *c, int success, void *userdata) static void context_success_cb(pa_context *c, int success, void *userdata)
{ {
ENTER(__FUNCTION__);
assert(c); assert(c);


if (userdata) if (userdata)


static void stream_request_cb(pa_stream *s, size_t length, void *userdata) static void stream_request_cb(pa_stream *s, size_t length, void *userdata)
{ {
ENTER(__FUNCTION__);
assert(s); assert(s);


pa_threaded_mainloop_signal(mainloop, 0); pa_threaded_mainloop_signal(mainloop, 0);


static int pulse_free(void) static int pulse_free(void)
{ {
ENTER(__FUNCTION__);
size_t l = 0; size_t l = 0;
pa_operation *o = NULL; pa_operation *o = NULL;


CHECK_CONNECTED(0); CHECK_CONNECTED(0);


SHOW("pulse_free: %s (call)\n", "pa_threaded_main_loop_lock");
pa_threaded_mainloop_lock(mainloop); pa_threaded_mainloop_lock(mainloop);
CHECK_DEAD_GOTO(fail, 1); CHECK_DEAD_GOTO(fail, 1);


if ((l = pa_stream_writable_size(stream)) == (size_t)-1) { if ((l = pa_stream_writable_size(stream)) == (size_t)-1) {
SHOW("pa_stream_writable_size() failed: %s", pa_strerror(pa_context_errno(context)));
fprintf(stderr, "pa_stream_writable_size() failed: %s", pa_strerror(pa_context_errno(context)));
l = 0; l = 0;
goto fail; goto fail;
} }


SHOW("pulse_free: %s (ret=%d)\n", "pa_stream_writable_size", l);

/* If this function is called twice with no pulse_write() call in /* If this function is called twice with no pulse_write() call in
* between this means we should trigger the playback */ * between this means we should trigger the playback */
if (do_trigger) { if (do_trigger) {
int success = 0; int success = 0;


SHOW("pulse_free: %s (call)\n", "pa_stream_trigger");
if (!(o = pa_stream_trigger(stream, stream_success_cb, &success))) { if (!(o = pa_stream_trigger(stream, stream_success_cb, &success))) {
SHOW("pa_stream_trigger() failed: %s", pa_strerror(pa_context_errno(context)));
fprintf(stderr, "pa_stream_trigger() failed: %s", pa_strerror(pa_context_errno(context)));
goto fail; goto fail;
} }


SHOW("pulse_free: %s (call)\n", "pa_threaded_main_loop");
while (pa_operation_get_state(o) != PA_OPERATION_DONE) { while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
CHECK_DEAD_GOTO(fail, 1); CHECK_DEAD_GOTO(fail, 1);
pa_threaded_mainloop_wait(mainloop); pa_threaded_mainloop_wait(mainloop);
} }
SHOW("pulse_free: %s (ret)\n", "pa_threaded_main_loop");


if (!success) if (!success)
SHOW("pa_stream_trigger() failed: %s", pa_strerror(pa_context_errno(context)));
fprintf(stderr, "pa_stream_trigger() failed: %s", pa_strerror(pa_context_errno(context)));
} }


fail: fail:
SHOW("pulse_free: %s (call)\n", "pa_operation_unref");
if (o) if (o)
pa_operation_unref(o); pa_operation_unref(o);


SHOW("pulse_free: %s (call)\n", "pa_threaded_main_loop_unlock");
pa_threaded_mainloop_unlock(mainloop); pa_threaded_mainloop_unlock(mainloop);


do_trigger = !!l; do_trigger = !!l;
SHOW("pulse_free: %d (ret)\n", (int)l);
return (int)l; return (int)l;
} }


static int pulse_playing(const pa_timing_info *the_timing_info) static int pulse_playing(const pa_timing_info *the_timing_info)
{ {
ENTER(__FUNCTION__);
int r = 0; int r = 0;
const pa_timing_info *i; const pa_timing_info *i;


if ((i = pa_stream_get_timing_info(stream))) if ((i = pa_stream_get_timing_info(stream)))
break; break;
if (pa_context_errno(context) != PA_ERR_NODATA) { if (pa_context_errno(context) != PA_ERR_NODATA) {
SHOW("pa_stream_get_timing_info() failed: %s", pa_strerror(pa_context_errno(context)));
fprintf(stderr, "pa_stream_get_timing_info() failed: %s", pa_strerror(pa_context_errno(context)));
goto fail; goto fail;
} }




static void pulse_write(void *ptr, int length) static void pulse_write(void *ptr, int length)
{ {
ENTER(__FUNCTION__);

SHOW("pulse_write > length=%d\n", length);

CHECK_CONNECTED_NO_RETVAL(); CHECK_CONNECTED_NO_RETVAL();


pa_threaded_mainloop_lock(mainloop); pa_threaded_mainloop_lock(mainloop);
CHECK_DEAD_GOTO(fail, 1); CHECK_DEAD_GOTO(fail, 1);


if (pa_stream_write(stream, ptr, length, NULL, PA_SEEK_RELATIVE, (pa_seek_mode_t)0) < 0) { if (pa_stream_write(stream, ptr, length, NULL, PA_SEEK_RELATIVE, (pa_seek_mode_t)0) < 0) {
SHOW("pa_stream_write() failed: %s", pa_strerror(pa_context_errno(context)));
fprintf(stderr, "pa_stream_write() failed: %s", pa_strerror(pa_context_errno(context)));
goto fail; goto fail;
} }


int success = 0; int success = 0;
int ret = PULSE_ERROR; int ret = PULSE_ERROR;


ENTER(__FUNCTION__);

CHECK_CONNECTED(ret); CHECK_CONNECTED(ret);


pa_threaded_mainloop_lock(mainloop); pa_threaded_mainloop_lock(mainloop);
CHECK_DEAD_GOTO(fail, 0); CHECK_DEAD_GOTO(fail, 0);


SHOW_TIME("pa_stream_drain (call)");
if (!(o = pa_stream_drain(stream, stream_success_cb, &success))) { if (!(o = pa_stream_drain(stream, stream_success_cb, &success))) {
SHOW("pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context)));
fprintf(stderr, "pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context)));
goto fail; goto fail;
} }


SHOW_TIME("pa_threaded_mainloop_wait (call)");
while (pa_operation_get_state(o) != PA_OPERATION_DONE) { while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
CHECK_DEAD_GOTO(fail, 1); CHECK_DEAD_GOTO(fail, 1);
pa_threaded_mainloop_wait(mainloop); pa_threaded_mainloop_wait(mainloop);
} }
SHOW_TIME("pa_threaded_mainloop_wait (ret)");


if (!success) if (!success)
SHOW("pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context)));
fprintf(stderr, "pa_stream_drain() failed: %s\n", pa_strerror(pa_context_errno(context)));
else else
ret = PULSE_OK; ret = PULSE_OK;
fail: fail:
SHOW_TIME("pa_operation_unref (call)");
if (o) if (o)
pa_operation_unref(o); pa_operation_unref(o);


pa_threaded_mainloop_unlock(mainloop); pa_threaded_mainloop_unlock(mainloop);
SHOW_TIME("drain (ret)");


return ret; return ret;
} }


static void pulse_close(void) static void pulse_close(void)
{ {
ENTER(__FUNCTION__);

drain(); drain();


connected = 0; connected = 0;
connected = 0; connected = 0;


if (context) { if (context) {
SHOW_TIME("pa_context_disconnect (call)");
pa_context_disconnect(context); pa_context_disconnect(context);
pa_context_unref(context); pa_context_unref(context);
context = NULL; context = NULL;
} }


if (mainloop) { if (mainloop) {
SHOW_TIME("pa_threaded_mainloop_free (call)");
pa_threaded_mainloop_free(mainloop); pa_threaded_mainloop_free(mainloop);
mainloop = NULL; mainloop = NULL;
} }
SHOW_TIME("pulse_close (ret)");

} }


static int pulse_open() static int pulse_open()
{ {
ENTER(__FUNCTION__);
pa_sample_spec ss; pa_sample_spec ss;
pa_operation *o = NULL; pa_operation *o = NULL;
int success; int success;
if (!pa_sample_spec_valid(&ss)) if (!pa_sample_spec_valid(&ss))
return false; return false;


SHOW_TIME("pa_threaded_mainloop_new (call)");
if (!(mainloop = pa_threaded_mainloop_new())) {
SHOW("Failed to allocate main loop\n", "");
if (!(mainloop = pa_threaded_mainloop_new()))
goto fail; goto fail;
}


pa_threaded_mainloop_lock(mainloop); pa_threaded_mainloop_lock(mainloop);


SHOW_TIME("pa_context_new (call)");
if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "eSpeak"))) {
SHOW("Failed to allocate context\n", "");
if (!(context = pa_context_new(pa_threaded_mainloop_get_api(mainloop), "eSpeak")))
goto unlock_and_fail; goto unlock_and_fail;
}


pa_context_set_state_callback(context, context_state_cb, NULL); pa_context_set_state_callback(context, context_state_cb, NULL);
pa_context_set_subscribe_callback(context, subscribe_cb, NULL); pa_context_set_subscribe_callback(context, subscribe_cb, NULL);


SHOW_TIME("pa_context_connect (call)");
if (pa_context_connect(context, NULL, (pa_context_flags_t)0, NULL) < 0) { if (pa_context_connect(context, NULL, (pa_context_flags_t)0, NULL) < 0) {
SHOW("Failed to connect to server: %s", pa_strerror(pa_context_errno(context)));
fprintf(stderr, "Failed to connect to server: %s", pa_strerror(pa_context_errno(context)));
ret = PULSE_NO_CONNECTION; ret = PULSE_NO_CONNECTION;
goto unlock_and_fail; goto unlock_and_fail;
} }


SHOW_TIME("pa_threaded_mainloop_start (call)");
if (pa_threaded_mainloop_start(mainloop) < 0) {
SHOW("Failed to start main loop", "");
if (pa_threaded_mainloop_start(mainloop) < 0)
goto unlock_and_fail; goto unlock_and_fail;
}


// Wait until the context is ready // Wait until the context is ready
SHOW_TIME("pa_threaded_mainloop_wait");
pa_threaded_mainloop_wait(mainloop); pa_threaded_mainloop_wait(mainloop);


if (pa_context_get_state(context) != PA_CONTEXT_READY) { if (pa_context_get_state(context) != PA_CONTEXT_READY) {
SHOW("Failed to connect to server: %s", pa_strerror(pa_context_errno(context)));
fprintf(stderr, "Failed to connect to server: %s", pa_strerror(pa_context_errno(context)));
ret = PULSE_NO_CONNECTION; ret = PULSE_NO_CONNECTION;
if (mainloop) if (mainloop)
pa_threaded_mainloop_stop(mainloop); pa_threaded_mainloop_stop(mainloop);
goto unlock_and_fail; goto unlock_and_fail;
} }


SHOW_TIME("pa_stream_new");
if (!(stream = pa_stream_new(context, "unknown", &ss, NULL))) { if (!(stream = pa_stream_new(context, "unknown", &ss, NULL))) {
SHOW("Failed to create stream: %s", pa_strerror(pa_context_errno(context)));
fprintf(stderr, "Failed to create stream: %s", pa_strerror(pa_context_errno(context)));
goto unlock_and_fail; goto unlock_and_fail;
} }


a_attr.minreq = MINREQ; a_attr.minreq = MINREQ;
a_attr.fragsize = 0; a_attr.fragsize = 0;


SHOW_TIME("pa_connect_playback");
if (pa_stream_connect_playback(stream, NULL, &a_attr, (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE), NULL, NULL) < 0) { if (pa_stream_connect_playback(stream, NULL, &a_attr, (pa_stream_flags_t)(PA_STREAM_INTERPOLATE_TIMING|PA_STREAM_AUTO_TIMING_UPDATE), NULL, NULL) < 0) {
SHOW("Failed to connect stream: %s", pa_strerror(pa_context_errno(context)));
fprintf(stderr, "Failed to connect stream: %s", pa_strerror(pa_context_errno(context)));
goto unlock_and_fail; goto unlock_and_fail;
} }


// Wait until the stream is ready // Wait until the stream is ready
SHOW_TIME("pa_threaded_mainloop_wait");
pa_threaded_mainloop_wait(mainloop); pa_threaded_mainloop_wait(mainloop);


if (pa_stream_get_state(stream) != PA_STREAM_READY) { if (pa_stream_get_state(stream) != PA_STREAM_READY) {
SHOW("Failed to connect stream: %s", pa_strerror(pa_context_errno(context)));
fprintf(stderr, "Failed to connect stream: %s", pa_strerror(pa_context_errno(context)));
goto unlock_and_fail; goto unlock_and_fail;
} }


// Now subscribe to events // Now subscribe to events
SHOW_TIME("pa_context_subscribe");
if (!(o = pa_context_subscribe(context, PA_SUBSCRIPTION_MASK_SINK_INPUT, context_success_cb, &success))) { if (!(o = pa_context_subscribe(context, PA_SUBSCRIPTION_MASK_SINK_INPUT, context_success_cb, &success))) {
SHOW("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context)));
fprintf(stderr, "pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context)));
goto unlock_and_fail; goto unlock_and_fail;
} }


success = 0; success = 0;
SHOW_TIME("pa_threaded_mainloop_wait");
while (pa_operation_get_state(o) != PA_OPERATION_DONE) { while (pa_operation_get_state(o) != PA_OPERATION_DONE) {
CHECK_DEAD_GOTO(fail, 1); CHECK_DEAD_GOTO(fail, 1);
pa_threaded_mainloop_wait(mainloop); pa_threaded_mainloop_wait(mainloop);
pa_operation_unref(o); pa_operation_unref(o);


if (!success) { if (!success) {
SHOW("pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context)));
fprintf(stderr, "pa_context_subscribe() failed: %s", pa_strerror(pa_context_errno(context)));
goto unlock_and_fail; goto unlock_and_fail;
} }


connected = 1; connected = 1;


pa_threaded_mainloop_unlock(mainloop); pa_threaded_mainloop_unlock(mainloop);
SHOW_TIME("pulse_open (ret true)");


return PULSE_OK; return PULSE_OK;
unlock_and_fail: unlock_and_fail:
fail: fail:
if (ret == PULSE_NO_CONNECTION) { if (ret == PULSE_NO_CONNECTION) {
if (context) { if (context) {
SHOW_TIME("pa_context_disconnect (call)");
pa_context_disconnect(context); pa_context_disconnect(context);
pa_context_unref(context); pa_context_unref(context);
context = NULL; context = NULL;
} }


if (mainloop) { if (mainloop) {
SHOW_TIME("pa_threaded_mainloop_free (call)");
pa_threaded_mainloop_free(mainloop); pa_threaded_mainloop_free(mainloop);
mainloop = NULL; mainloop = NULL;
} }
} else } else
pulse_close(); pulse_close();


SHOW_TIME("pulse_open (ret false)");

return ret; return ret;
} }


void wave_flush(void *theHandler) void wave_flush(void *theHandler)
{ {
ENTER("wave_flush");
} }


void wave_set_callback_is_output_enabled(t_wave_callback *cb) void wave_set_callback_is_output_enabled(t_wave_callback *cb)


int wave_init(int srate) int wave_init(int srate)
{ {
ENTER("wave_init");

stream = NULL; stream = NULL;
wave_samplerate = srate; wave_samplerate = srate;




void *wave_open(const char *the_api) void *wave_open(const char *the_api)
{ {
ENTER("wave_open");
return (void *)1; return (void *)1;
} }


size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize) size_t wave_write(void *theHandler, char *theMono16BitsWaveBuffer, size_t theSize)
{ {
ENTER("wave_write");
size_t bytes_to_write = theSize; size_t bytes_to_write = theSize;
char *aBuffer = theMono16BitsWaveBuffer; char *aBuffer = theMono16BitsWaveBuffer;


while (1) { while (1) {
if (my_callback_is_output_enabled if (my_callback_is_output_enabled
&& (0 == my_callback_is_output_enabled())) { && (0 == my_callback_is_output_enabled())) {
SHOW_TIME("wave_write > my_callback_is_output_enabled: no!");
theSize = 0; theSize = 0;
goto terminate; goto terminate;
} }


aTotalFreeMem = pulse_free(); aTotalFreeMem = pulse_free();
if (aTotalFreeMem >= bytes_to_write) {
SHOW("wave_write > aTotalFreeMem(%d) >= bytes_to_write(%d)\n", aTotalFreeMem, bytes_to_write);
if (aTotalFreeMem >= bytes_to_write)
break; break;
}


// TBD: check if really helpful // TBD: check if really helpful
if (aTotalFreeMem >= MAXLENGTH*2) if (aTotalFreeMem >= MAXLENGTH*2)
aTotalFreeMem = MAXLENGTH*2; aTotalFreeMem = MAXLENGTH*2;


SHOW("wave_write > wait: aTotalFreeMem(%d) < bytes_to_write(%d)\n", aTotalFreeMem, bytes_to_write);

// 500: threshold for avoiding too many calls to pulse_write // 500: threshold for avoiding too many calls to pulse_write
if (aTotalFreeMem > 500) { if (aTotalFreeMem > 500) {
pulse_write(aBuffer, aTotalFreeMem); pulse_write(aBuffer, aTotalFreeMem);
pulse_write(aBuffer, bytes_to_write); pulse_write(aBuffer, bytes_to_write);
terminate: terminate:
pthread_mutex_unlock(&pulse_mutex); pthread_mutex_unlock(&pulse_mutex);
SHOW("wave_write: theSize=%d", theSize);
SHOW_TIME("wave_write > LEAVE");
return theSize; return theSize;
} }


int wave_close(void *theHandler) int wave_close(void *theHandler)
{ {
SHOW_TIME("wave_close > ENTER");
static int aStopStreamCount = 0; static int aStopStreamCount = 0;


// Avoid race condition by making sure this function only // Avoid race condition by making sure this function only
// gets called once at a time // gets called once at a time
aStopStreamCount++; aStopStreamCount++;
if (aStopStreamCount != 1) {
SHOW_TIME("wave_close > LEAVE (stopStreamCount)");
if (aStopStreamCount != 1)
return 0; return 0;
}


int a_status = pthread_mutex_lock(&pulse_mutex); int a_status = pthread_mutex_lock(&pulse_mutex);
if (a_status) { if (a_status) {
SHOW("Error: pulse_mutex lock=%d (%s)\n", a_status, __FUNCTION__);
aStopStreamCount = 0; // last action aStopStreamCount = 0; // last action
return PULSE_ERROR; return PULSE_ERROR;
} }
drain(); drain();


pthread_mutex_unlock(&pulse_mutex); pthread_mutex_unlock(&pulse_mutex);
SHOW_TIME("wave_close (ret)");


aStopStreamCount = 0; // last action aStopStreamCount = 0; // last action
return PULSE_OK; return PULSE_OK;


int wave_is_busy(void *theHandler) int wave_is_busy(void *theHandler)
{ {
SHOW_TIME("wave_is_busy");

pa_timing_info a_timing_info; pa_timing_info a_timing_info;
int active = pulse_playing(&a_timing_info); int active = pulse_playing(&a_timing_info);
SHOW("wave_is_busy: %d\n", active);
return active; return active;
} }


void wave_terminate() void wave_terminate()
{ {
ENTER("wave_terminate");

int a_status; int a_status;
pthread_mutex_t *a_mutex = NULL; pthread_mutex_t *a_mutex = NULL;
a_mutex = &pulse_mutex; a_mutex = &pulse_mutex;


pulse_close(); pulse_close();


SHOW_TIME("unlock mutex");
a_status = pthread_mutex_unlock(a_mutex); a_status = pthread_mutex_unlock(a_mutex);
pthread_mutex_destroy(a_mutex); pthread_mutex_destroy(a_mutex);
} }
{ {
pa_timing_info a_timing_info; pa_timing_info a_timing_info;
pulse_playing(&a_timing_info); pulse_playing(&a_timing_info);
SHOW("wave_get_read_position > %lx\n", a_timing_info.read_index);
return a_timing_info.read_index; return a_timing_info.read_index;
} }


{ {
pa_timing_info a_timing_info; pa_timing_info a_timing_info;
pulse_playing(&a_timing_info); pulse_playing(&a_timing_info);
SHOW("wave_get_read_position > %lx\n", a_timing_info.write_index);
return a_timing_info.write_index; return a_timing_info.write_index;
} }


{ {
double a_time = 0; double a_time = 0;


if (!time || !stream) {
SHOW("event get_remaining_time> %s\n", "audio device not available");
if (!time || !stream)
return -1; return -1;
}


pa_timing_info a_timing_info; pa_timing_info a_timing_info;
pulse_playing(&a_timing_info); pulse_playing(&a_timing_info);
} else } else
a_time = 0; a_time = 0;


SHOW("wave_get_remaining_time > sample=%d, time=%d\n", sample, (uint32_t)a_time);

*time = (uint32_t)a_time; *time = (uint32_t)a_time;


return 0; return 0;


uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms; uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms;
while (t_ns >= ONE_BILLION) { while (t_ns >= ONE_BILLION) {
SHOW("event > add_time_in_ms ns: %d sec %Lu nsec \n", ts->tv_sec, t_ns);
ts->tv_sec += 1; ts->tv_sec += 1;
t_ns -= ONE_BILLION; t_ns -= ONE_BILLION;
} }

+ 8
- 43
src/libespeak-ng/wave_sada.c View File

#include <sys/audioio.h> #include <sys/audioio.h>


#include "wave.h" #include "wave.h"
#include "debug.h"


enum { ONE_BILLION = 1000000000 }; enum { ONE_BILLION = 1000000000 };
#define SAMPLE_RATE 22050 #define SAMPLE_RATE 22050
// //
int wave_init(int srate) int wave_init(int srate)
{ {
ENTER("wave_init");

audio_info_t ainfo; audio_info_t ainfo;
char *audio_device = NULL; char *audio_device = NULL;


audio_device = getenv("AUDIODEV"); audio_device = getenv("AUDIODEV");
if (audio_device != NULL) { if (audio_device != NULL) {
if ((sun_audio_fd = open(audio_device, O_WRONLY)) < 0) { if ((sun_audio_fd = open(audio_device, O_WRONLY)) < 0) {
SHOW("wave_init() could not open: %s (%d)\n",
audio_device, sun_audio_fd);
fprintf(stderr, "wave_init() could not open: %s (%d)\n",
audio_device, sun_audio_fd);
} }
} }


if (sun_audio_fd < 0) { if (sun_audio_fd < 0) {
if ((sun_audio_fd = open(sun_audio_device, O_WRONLY)) < 0) { if ((sun_audio_fd = open(sun_audio_device, O_WRONLY)) < 0) {
SHOW("wave_init() could not open: %s (%d)\n",
sun_audio_device, sun_audio_fd);
fprintf(stderr, "wave_init() could not open: %s (%d)\n",
sun_audio_device, sun_audio_fd);
} }
} }


SHOW("wave_init() sun_audio_fd: %d\n", sun_audio_fd);

if (sun_audio_fd < 0) if (sun_audio_fd < 0)
return 0; return 0;


ioctl(sun_audio_fd, AUDIO_GETINFO, &ainfo); ioctl(sun_audio_fd, AUDIO_GETINFO, &ainfo);
SHOW("wave_init() play buffer size: %d\n", ainfo.play.buffer_size);
ainfo.play.encoding = AUDIO_ENCODING_LINEAR; ainfo.play.encoding = AUDIO_ENCODING_LINEAR;
ainfo.play.channels = 1; ainfo.play.channels = 1;
ainfo.play.sample_rate = wave_samplerate; ainfo.play.sample_rate = wave_samplerate;
ainfo.play.precision = SAMPLE_SIZE; ainfo.play.precision = SAMPLE_SIZE;


if (ioctl(sun_audio_fd, AUDIO_SETINFO, &ainfo) == -1) { if (ioctl(sun_audio_fd, AUDIO_SETINFO, &ainfo) == -1) {
SHOW("wave_init() failed to set audio params: %s\n", strerror(errno));
fprintf(stderr, "wave_init() failed to set audio params: %s\n", strerror(errno));
close(sun_audio_fd); close(sun_audio_fd);
return 0; return 0;
} }
// //
void *wave_open(const char *the_api) void *wave_open(const char *the_api)
{ {
ENTER("wave_open");
return (void *)sun_audio_fd; return (void *)sun_audio_fd;
} }


size_t theSize) size_t theSize)
{ {
size_t num; size_t num;
ENTER("wave_write");
if (my_callback_is_output_enabled && (0 == my_callback_is_output_enabled())) {
SHOW_TIME("wave_write > my_callback_is_output_enabled: no!");
if (my_callback_is_output_enabled && (0 == my_callback_is_output_enabled()))
return 0; return 0;
}


#if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN #if defined(BYTE_ORDER) && BYTE_ORDER == BIG_ENDIAN
// BIG-ENDIAN, swap the order of bytes in each sound sample // BIG-ENDIAN, swap the order of bytes in each sound sample
// //
total_samples_sent += num / 2; total_samples_sent += num / 2;


if (num < theSize)
SHOW("ERROR: wave_write only wrote %d of %d bytes\n", num, theSize);
else
SHOW("wave_write wrote %d bytes\n", theSize);

SHOW_TIME("wave_write > LEAVE");
return num; return num;
} }


int ret; int ret;
audio_info_t ainfo; audio_info_t ainfo;
int audio_fd = (int)theHandler; int audio_fd = (int)theHandler;
if (!audio_fd) {
if (!audio_fd)
audio_fd = sun_audio_fd; audio_fd = sun_audio_fd;
}
ENTER("wave_close");
// [[[WDW: maybe do a pause/resume ioctl???]]] // [[[WDW: maybe do a pause/resume ioctl???]]]
ret = ioctl(audio_fd, I_FLUSH, FLUSHRW); ret = ioctl(audio_fd, I_FLUSH, FLUSHRW);
ioctl(audio_fd, AUDIO_GETINFO, &ainfo); ioctl(audio_fd, AUDIO_GETINFO, &ainfo);
last_play_position = ainfo.play.samples; last_play_position = ainfo.play.samples;
total_samples_skipped = total_samples_sent - last_play_position; total_samples_skipped = total_samples_sent - last_play_position;
} }
SHOW_TIME("wave_close > LEAVE");
return ret; return ret;
} }


// //
void wave_terminate() void wave_terminate()
{ {
ENTER("wave_terminate");
close(sun_audio_fd); close(sun_audio_fd);
sun_audio_fd = -1; sun_audio_fd = -1;
SHOW_TIME("wave_terminate > LEAVE");
} }


// wave_flush // wave_flush
// //
void wave_flush(void *theHandler) void wave_flush(void *theHandler)
{ {
ENTER("wave_flush");
SHOW_TIME("wave_flush > LEAVE");
} }


// wave_set_callback_is_output_enabled // wave_set_callback_is_output_enabled
uint32_t wave_get_read_position(void *theHandler) uint32_t wave_get_read_position(void *theHandler)
{ {
audio_info_t ainfo; audio_info_t ainfo;
ENTER("wave_get_read_position");
ioctl((int)theHandler, AUDIO_GETINFO, &ainfo); ioctl((int)theHandler, AUDIO_GETINFO, &ainfo);
SHOW("wave_get_read_position: %d\n", ainfo.play.samples);
SHOW_TIME("wave_get_read_position > LEAVE");
return ainfo.play.samples; return ainfo.play.samples;
} }


// //
uint32_t wave_get_write_position(void *theHandler) uint32_t wave_get_write_position(void *theHandler)
{ {
ENTER("wave_get_write_position");
SHOW("wave_get_write_position: %d\n", total_samples_sent);
SHOW_TIME("wave_get_write_position > LEAVE");
return total_samples_sent; return total_samples_sent;
} }


uint32_t actual_index; uint32_t actual_index;


audio_info_t ainfo; audio_info_t ainfo;
ENTER("wave_get_remaining_time");
if (!time) {
if (!time)
return -1; return -1;
SHOW_TIME("wave_get_remaining_time > LEAVE");
}


ioctl(sun_audio_fd, AUDIO_GETINFO, &ainfo); ioctl(sun_audio_fd, AUDIO_GETINFO, &ainfo);


a_time = ((actual_index - ainfo.play.samples) * 1000) / wave_samplerate; a_time = ((actual_index - ainfo.play.samples) * 1000) / wave_samplerate;
*time = (uint32_t)a_time; *time = (uint32_t)a_time;
} }
SHOW("wave_get_remaining_time for %d: %d\n", sample, *time);
SHOW_TIME("wave_get_remaining_time > LEAVE");
return 0; return 0;
} }




uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms; uint64_t t_ns = (uint64_t)ts->tv_nsec + 1000000 * (uint64_t)time_in_ms;
while (t_ns >= ONE_BILLION) { while (t_ns >= ONE_BILLION) {
SHOW("event > add_time_in_ms ns: %d sec %Lu nsec \n", ts->tv_sec, t_ns);
ts->tv_sec += 1; ts->tv_sec += 1;
t_ns -= ONE_BILLION; t_ns -= ONE_BILLION;
} }

Loading…
Cancel
Save