Newer
Older
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2018 by The BRLTTY Developers.
*
* BRLTTY comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed under the terms of the
* GNU Lesser General Public License, as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any
* later version. Please see the file LICENSE-LGPL for details.
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#include "prologue.h"
#include <string.h>
#include "parameters.h"
#include "async_wait.h"
#include "async_event.h"
#include "thread.h"
#include "queue.h"
#ifdef ENABLE_SPEECH_SUPPORT
typedef enum {
THD_CONSTRUCTING,
THD_STARTING,
THD_READY,
THD_STOPPING,
THD_FINISHED
} ThreadState;
typedef struct {
const char *name;
} ThreadStateEntry;
static const ThreadStateEntry threadStateTable[] = {
[THD_CONSTRUCTING] = {
.name = "constructing"
},
[THD_STARTING] = {
.name = "starting"
},
[THD_READY] = {
.name = "ready"
},
[THD_STOPPING] = {
.name = "stopping"
},
.name = "finished"
},
};
static inline const ThreadStateEntry *
getThreadStateEntry (ThreadState state) {
if (state >= ARRAY_COUNT(threadStateTable)) return NULL;
return &threadStateTable[state];
}
typedef enum {
RSP_PENDING,
RSP_INTEGER
} SpeechResponseType;
struct SpeechDriverThreadStruct {
ThreadState threadState;
volatile SpeechSynthesizer *speechSynthesizer;
char **driverParameters;
pthread_t threadIdentifier;
AsyncEvent *requestEvent;
AsyncEvent *messageEvent;
struct {
SpeechResponseType type;
union {
int INTEGER;
} value;
} response;
};
typedef enum {
REQ_SAY_TEXT,
REQ_MUTE_SPEECH,
REQ_SET_VOLUME,
REQ_SET_RATE,
REQ_SET_PITCH,
REQ_SET_PUNCTUATION
} SpeechRequestType;
static const char *const speechRequestNames[] = {
[REQ_SAY_TEXT] = "say text",
[REQ_MUTE_SPEECH] = "mute speech",
[REQ_DRAIN_SPEECH] = "drain speech",
[REQ_SET_VOLUME] = "set volume",
[REQ_SET_RATE] = "set rate",
[REQ_SET_PITCH] = "set pitch",
[REQ_SET_PUNCTUATION] = "set punctuation"
};
typedef struct {
SpeechRequestType type;
union {
struct {
const unsigned char *text;
size_t length;
size_t count;
const unsigned char *attributes;
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
} sayText;
struct {
unsigned char setting;
} setVolume;
struct {
unsigned char setting;
} setRate;
struct {
unsigned char setting;
} setPitch;
struct {
SpeechPunctuation setting;
} setPunctuation;
} arguments;
unsigned char data[0];
} SpeechRequest;
typedef struct {
const void *address;
size_t size;
unsigned end:1;
} SpeechDatum;
#define BEGIN_SPEECH_DATA SpeechDatum data[] = {
#define END_SPEECH_DATA {.end=1} };
typedef enum {
MSG_SPEECH_FINISHED,
MSG_SPEECH_LOCATION
} SpeechMessageType;
static const char *const speechMessageNames[] = {
[MSG_REQUEST_FINISHED] = "request finished",
[MSG_SPEECH_FINISHED] = "speech finished",
[MSG_SPEECH_LOCATION] = "speech location"
};
typedef struct {
SpeechMessageType type;
union {
struct {
int result;
} requestFinished;
struct {
int location;
} speechLocation;
} arguments;
unsigned char data[0];
} SpeechMessage;
static const char *
getActionName (unsigned int action, const char *const *names, size_t count) {
return (action < count)? names[action]: NULL;
}
typedef struct {
const char *action;
const char *type;
const char *name;
unsigned int value;
} LogSpeechActionData;
static
STR_BEGIN_FORMATTER(formatLogSpeechActionData, const void *data)
const LogSpeechActionData *lsa = data;
STR_PRINTF("%s speech %s: ", lsa->action, lsa->type);
if (lsa->name) {
STR_PRINTF("%s", lsa->name);
} else {
STR_PRINTF("%u", lsa->value);
}
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
static void
logSpeechAction (const LogSpeechActionData *lsa) {
logData(LOG_CATEGORY(SPEECH_EVENTS), formatLogSpeechActionData, lsa);
}
static void
logSpeechRequest (SpeechRequest *req, const char *action) {
const LogSpeechActionData lsa = {
.action = action,
.type = "request",
.name = req? getActionName(req->type, speechRequestNames, ARRAY_COUNT(speechRequestNames)): "stop",
.value = req? req->type: 0
};
logSpeechAction(&lsa);
}
static void
logSpeechMessage (SpeechMessage *msg, const char *action) {
const LogSpeechActionData lsa = {
.action = action,
.type = "message",
.name = getActionName(msg->type, speechMessageNames, ARRAY_COUNT(speechMessageNames)),
.value = msg->type
};
logSpeechAction(&lsa);
}
static int
testThreadValidity (volatile SpeechDriverThread *sdt) {
#ifdef GOT_PTHREADS
if (sdt->isBeingDestroyed) return 0;
#endif /* GOT_PTHREADS */
volatile SpeechSynthesizer *spk = sdt->speechSynthesizer;
if (!spk) return 0;
if (sdt != spk->driver.thread) return 0;
if (sdt->threadState != THD_READY) return 0;
return 1;
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
static void
setThreadState (volatile SpeechDriverThread *sdt, ThreadState state) {
const ThreadStateEntry *entry = getThreadStateEntry(state);
const char *name = entry? entry->name: NULL;
if (!name) name = "?";
logMessage(LOG_CATEGORY(SPEECH_EVENTS), "driver thread %s", name);
sdt->threadState = state;
}
static size_t
getSpeechDataSize (const SpeechDatum *data) {
size_t size = 0;
if (data) {
const SpeechDatum *datum = data;
while (!datum->end) {
if (datum->address) size += datum->size;
datum += 1;
}
}
return size;
}
static void
moveSpeechData (unsigned char *target, SpeechDatum *data) {
if (data) {
SpeechDatum *datum = data;
while (!datum->end) {
if (datum->address) {
memcpy(target, datum->address, datum->size);
datum->address = target;
target += datum->size;
}
datum += 1;
}
}
}
static inline void
setResponsePending (volatile SpeechDriverThread *sdt) {
sdt->response.type = RSP_PENDING;
}
static void
setIntegerResponse (volatile SpeechDriverThread *sdt, int value) {
sdt->response.type = RSP_INTEGER;
sdt->response.value.INTEGER = value;
}
ASYNC_CONDITION_TESTER(testSpeechResponseReceived) {
volatile SpeechDriverThread *sdt = data;
return sdt->response.type != RSP_PENDING;
}
static int
awaitSpeechResponse (volatile SpeechDriverThread *sdt, int timeout) {
return asyncAwaitCondition(timeout, testSpeechResponseReceived, (void *)sdt);
}
static void sendSpeechRequest (volatile SpeechDriverThread *sdt);
static void
handleSpeechMessage (volatile SpeechDriverThread *sdt, SpeechMessage *msg) {
case MSG_REQUEST_FINISHED:
setIntegerResponse(sdt, msg->arguments.requestFinished.result);
sendSpeechRequest(sdt);
break;
case MSG_SPEECH_FINISHED: {
volatile SpeechSynthesizer *spk = sdt->speechSynthesizer;
SetSpeechFinishedMethod *setFinished = spk->setFinished;
if (setFinished) setFinished(spk);
case MSG_SPEECH_LOCATION: {
volatile SpeechSynthesizer *spk = sdt->speechSynthesizer;
SetSpeechLocationMethod *setLocation = spk->setLocation;
if (setLocation) setLocation(spk, msg->arguments.speechLocation.location);
logMessage(LOG_CATEGORY(SPEECH_EVENTS), "unimplemented message: %u", msg->type);
break;
}
free(msg);
}
}
static int
sendSpeechMessage (volatile SpeechDriverThread *sdt, SpeechMessage *msg) {
return asyncSignalEvent(sdt->messageEvent, msg);
}
static SpeechMessage *
newSpeechMessage (SpeechMessageType type, SpeechDatum *data) {
SpeechMessage *msg;
size_t size = sizeof(*msg) + getSpeechDataSize(data);
if ((msg = malloc(size))) {
memset(msg, 0, sizeof(*msg));
msg->type = type;
moveSpeechData(msg->data, data);
return msg;
} else {
logMallocError();
}
return NULL;
}
static int
speechMessage_requestFinished (
volatile SpeechDriverThread *sdt,
int result
) {
SpeechMessage *msg;
if ((msg = newSpeechMessage(MSG_REQUEST_FINISHED, NULL))) {
msg->arguments.requestFinished.result = result;
if (sendSpeechMessage(sdt, msg)) return 1;
free(msg);
}
return 0;
}
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
int
speechMessage_speechFinished (
volatile SpeechDriverThread *sdt
) {
SpeechMessage *msg;
if ((msg = newSpeechMessage(MSG_SPEECH_FINISHED, NULL))) {
if (sendSpeechMessage(sdt, msg)) return 1;
free(msg);
}
return 0;
}
int
speechMessage_speechLocation (
volatile SpeechDriverThread *sdt,
int location
) {
SpeechMessage *msg;
if ((msg = newSpeechMessage(MSG_SPEECH_LOCATION, NULL))) {
msg->arguments.speechLocation.location = location;
if (sendSpeechMessage(sdt, msg)) return 1;
free(msg);
}
return 0;
}
static int
sendIntegerResponse (volatile SpeechDriverThread *sdt, int result) {
return speechMessage_requestFinished(sdt, result);
}
static void
handleSpeechRequest (volatile SpeechDriverThread *sdt, SpeechRequest *req) {
volatile SpeechSynthesizer *spk = sdt->speechSynthesizer;
logSpeechRequest(req, "handling");
if (req) {
switch (req->type) {
case REQ_SAY_TEXT: {
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
SayOptions options = req->arguments.sayText.options;
int restorePitch = 0;
int restorePunctuation = 0;
if (options & SAY_OPT_MUTE_FIRST) speech->mute(spk);
if (options & SAY_OPT_HIGHER_PITCH) {
if (spk->setPitch) {
unsigned char pitch = prefs.speechPitch + 7;
if (pitch > SPK_PITCH_MAXIMUM) pitch = SPK_PITCH_MAXIMUM;
if (pitch != prefs.speechPitch) {
spk->setPitch(spk, pitch);
restorePitch = 1;
}
}
}
if (options & SAY_OPT_ALL_PUNCTUATION) {
if (spk->setPunctuation) {
unsigned char punctuation = SPK_PUNCTUATION_ALL;
if (punctuation != prefs.speechPunctuation) {
spk->setPunctuation(spk, punctuation);
restorePunctuation = 1;
}
}
}
req->arguments.sayText.text, req->arguments.sayText.length,
req->arguments.sayText.count, req->arguments.sayText.attributes
);
if (restorePunctuation) spk->setPunctuation(spk, prefs.speechPunctuation);
if (restorePitch) spk->setPitch(spk, prefs.speechPitch);
sendIntegerResponse(sdt, 1);
break;
}
case REQ_MUTE_SPEECH: {
speech->mute(spk);
sendIntegerResponse(sdt, 1);
break;
}
case REQ_DRAIN_SPEECH: {
spk->drain(spk);
sendIntegerResponse(sdt, 1);
break;
}
case REQ_SET_VOLUME: {
spk->setVolume(spk, req->arguments.setVolume.setting);
sendIntegerResponse(sdt, 1);
break;
}
case REQ_SET_RATE: {
spk->setRate(spk, req->arguments.setRate.setting);
sendIntegerResponse(sdt, 1);
break;
}
case REQ_SET_PITCH: {
spk->setPitch(spk, req->arguments.setPitch.setting);
sendIntegerResponse(sdt, 1);
break;
}
case REQ_SET_PUNCTUATION: {
spk->setPunctuation(spk, req->arguments.setPunctuation.setting);
sendIntegerResponse(sdt, 1);
break;
}
default:
logMessage(LOG_CATEGORY(SPEECH_EVENTS), "unimplemented request: %u", req->type);
sendIntegerResponse(sdt, 0);
break;
}
free(req);
} else {
setThreadState(sdt, THD_STOPPING);
typedef struct {
SpeechRequestType const type;
} TestSpeechRequestData;
testSpeechRequest (const void *item, void *data) {
const SpeechRequest *req = item;
const TestSpeechRequestData *tsr = data;
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
return req->type == tsr->type;
}
static Element *
findSpeechRequestElement (volatile SpeechDriverThread *sdt, SpeechRequestType type) {
TestSpeechRequestData tsr = {
.type = type
};
if (!testThreadValidity(sdt)) return NULL;
return findElement(sdt->requestQueue, testSpeechRequest, &tsr);
}
static void
removeSpeechRequests (volatile SpeechDriverThread *sdt, SpeechRequestType type) {
Element *element;
while ((element = findSpeechRequestElement(sdt, type))) deleteElement(element);
}
static void
muteSpeechRequestQueue (volatile SpeechDriverThread *sdt) {
removeSpeechRequests(sdt, REQ_SAY_TEXT);
removeSpeechRequests(sdt, REQ_MUTE_SPEECH);
}
static void
sendSpeechRequest (volatile SpeechDriverThread *sdt) {
while (getQueueSize(sdt->requestQueue) > 0) {
SpeechRequest *req = dequeueItem(sdt->requestQueue);
setResponsePending(sdt);
#ifdef GOT_PTHREADS
if (!asyncSignalEvent(sdt->requestEvent, req)) {
if (req) free(req);
setIntegerResponse(sdt, 0);
continue;
}
#else /* GOT_PTHREADS */
handleSpeechRequest(sdt, req);
#endif /* GOT_PTHREADS */
}
}
static int
enqueueSpeechRequest (volatile SpeechDriverThread *sdt, SpeechRequest *req) {
if (testThreadValidity(sdt)) {
if (enqueueItem(sdt->requestQueue, req)) {
if (sdt->response.type != RSP_PENDING) {
if (getQueueSize(sdt->requestQueue) == 1) {
sendSpeechRequest(sdt);
}
}
return 1;
}
}
return 0;
}
static SpeechRequest *
newSpeechRequest (SpeechRequestType type, SpeechDatum *data) {
SpeechRequest *req;
size_t size = sizeof(*req) + getSpeechDataSize(data);
if ((req = malloc(size))) {
memset(req, 0, sizeof(*req));
req->type = type;
moveSpeechData(req->data, data);
return req;
} else {
logMallocError();
}
return NULL;
}
int
speechRequest_sayText (
volatile SpeechDriverThread *sdt,
const char *text, size_t length,
size_t count, const unsigned char *attributes,
SayOptions options
) {
SpeechRequest *req;
BEGIN_SPEECH_DATA
{.address=text, .size=length+1},
{.address=attributes, .size=count},
END_SPEECH_DATA
if ((req = newSpeechRequest(REQ_SAY_TEXT, data))) {
req->arguments.sayText.text = data[0].address;
req->arguments.sayText.length = length;
req->arguments.sayText.count = count;
req->arguments.sayText.attributes = data[1].address;
req->arguments.sayText.options = options;
if (options & SAY_OPT_MUTE_FIRST) muteSpeechRequestQueue(sdt);
if (enqueueSpeechRequest(sdt, req)) return 1;
free(req);
}
return 0;
}
int
speechRequest_muteSpeech (
volatile SpeechDriverThread *sdt
) {
SpeechRequest *req;
if ((req = newSpeechRequest(REQ_MUTE_SPEECH, NULL))) {
muteSpeechRequestQueue(sdt);
if (enqueueSpeechRequest(sdt, req)) return 1;
free(req);
}
return 0;
}
int
speechRequest_drainSpeech (
volatile SpeechDriverThread *sdt
) {
SpeechRequest *req;
if ((req = newSpeechRequest(REQ_DRAIN_SPEECH, NULL))) {
if (enqueueSpeechRequest(sdt, req)) {
awaitSpeechResponse(sdt, SPEECH_RESPONSE_WAIT_TIMEOUT);
return 1;
}
free(req);
}
return 0;
}
int
speechRequest_setVolume (
volatile SpeechDriverThread *sdt,
unsigned char setting
) {
SpeechRequest *req;
if ((req = newSpeechRequest(REQ_SET_VOLUME, NULL))) {
req->arguments.setVolume.setting = setting;
if (enqueueSpeechRequest(sdt, req)) return 1;
free(req);
}
return 0;
}
int
speechRequest_setRate (
volatile SpeechDriverThread *sdt,
unsigned char setting
) {
SpeechRequest *req;
if ((req = newSpeechRequest(REQ_SET_RATE, NULL))) {
req->arguments.setRate.setting = setting;
if (enqueueSpeechRequest(sdt, req)) return 1;
free(req);
}
return 0;
}
int
speechRequest_setPitch (
volatile SpeechDriverThread *sdt,
unsigned char setting
) {
SpeechRequest *req;
if ((req = newSpeechRequest(REQ_SET_PITCH, NULL))) {
req->arguments.setPitch.setting = setting;
if (enqueueSpeechRequest(sdt, req)) return 1;
free(req);
}
return 0;
}
int
speechRequest_setPunctuation (
volatile SpeechDriverThread *sdt,
SpeechPunctuation setting
) {
SpeechRequest *req;
if ((req = newSpeechRequest(REQ_SET_PUNCTUATION, NULL))) {
req->arguments.setPunctuation.setting = setting;
if (enqueueSpeechRequest(sdt, req)) return 1;
free(req);
}
return 0;
}
static void
setThreadReady (volatile SpeechDriverThread *sdt) {
setThreadState(sdt, THD_READY);
sendIntegerResponse(sdt, 1);
}
static int
startSpeechDriver (volatile SpeechDriverThread *sdt) {
logMessage(LOG_CATEGORY(SPEECH_EVENTS), "starting driver");
return speech->construct(sdt->speechSynthesizer, sdt->driverParameters);
}
static void
stopSpeechDriver (volatile SpeechDriverThread *sdt) {
logMessage(LOG_CATEGORY(SPEECH_EVENTS), "stopping driver");
speech->destruct(sdt->speechSynthesizer);
}
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
ASYNC_CONDITION_TESTER(testSpeechDriverThreadStopping) {
volatile SpeechDriverThread *sdt = data;
return sdt->threadState == THD_STOPPING;
}
ASYNC_EVENT_CALLBACK(handleSpeechMessageEvent) {
volatile SpeechDriverThread *sdt = parameters->eventData;
SpeechMessage *msg = parameters->signalData;
handleSpeechMessage(sdt, msg);
}
ASYNC_EVENT_CALLBACK(handleSpeechRequestEvent) {
volatile SpeechDriverThread *sdt = parameters->eventData;
SpeechRequest *req = parameters->signalData;
handleSpeechRequest(sdt, req);
}
static void
awaitSpeechDriverThreadTermination (volatile SpeechDriverThread *sdt) {
void *result;
pthread_join(sdt->threadIdentifier, &result);
}
THREAD_FUNCTION(runSpeechDriverThread) {
volatile SpeechDriverThread *sdt = argument;
setThreadState(sdt, THD_STARTING);
if ((sdt->requestEvent = asyncNewEvent(handleSpeechRequestEvent, (void *)sdt))) {
if (startSpeechDriver(sdt)) {
setThreadReady(sdt);
asyncWaitFor(testSpeechDriverThreadStopping, (void *)sdt);
stopSpeechDriver(sdt);
} else {
logMessage(LOG_CATEGORY(SPEECH_EVENTS), "driver construction failure");
}
asyncDiscardEvent(sdt->requestEvent);
sdt->requestEvent = NULL;
} else {
logMessage(LOG_CATEGORY(SPEECH_EVENTS), "request event construction failure");
}
{
int ok = sdt->threadState == THD_STOPPING;
sendIntegerResponse(sdt, ok);
}
#endif /* GOT_PTHREADS */
static void
deallocateSpeechRequest (void *item, void *data) {
SpeechRequest *req = item;
volatile SpeechSynthesizer *spk,
char **parameters
) {
volatile SpeechDriverThread *sdt;
if ((sdt = malloc(sizeof(*sdt)))) {
memset((void *)sdt, 0, sizeof(*sdt));
setThreadState(sdt, THD_CONSTRUCTING);
setResponsePending(sdt);
sdt->speechSynthesizer = spk;
sdt->driverParameters = parameters;
if ((sdt->requestQueue = newQueue(deallocateSpeechRequest, NULL))) {
spk->driver.thread = sdt;
#ifdef GOT_PTHREADS
if ((sdt->messageEvent = asyncNewEvent(handleSpeechMessageEvent, (void *)sdt))) {
pthread_t threadIdentifier;
int createError = createThread("speech-driver",
&threadIdentifier, NULL,
if (!createError) {
sdt->threadIdentifier = threadIdentifier;
if (awaitSpeechResponse(sdt, SPEECH_DRIVER_THREAD_START_TIMEOUT)) {
if (sdt->response.type == RSP_INTEGER) {
if (sdt->response.value.INTEGER) {
logMessage(LOG_CATEGORY(SPEECH_EVENTS), "driver thread initialization failure");
awaitSpeechDriverThreadTermination(sdt);
} else {
logMessage(LOG_CATEGORY(SPEECH_EVENTS), "driver thread initialization timeout");
}
logMessage(LOG_CATEGORY(SPEECH_EVENTS), "driver thread creation failure: %s", strerror(createError));
asyncDiscardEvent(sdt->messageEvent);
sdt->messageEvent = NULL;
logMessage(LOG_CATEGORY(SPEECH_EVENTS), "response event construction failure");
}
#else /* GOT_PTHREADS */
if (startSpeechDriver(sdt)) {
setThreadReady(sdt);
spk->driver.thread = NULL;
deallocateQueue(sdt->requestQueue);
}
free((void *)sdt);
} else {
logMallocError();
}
destroySpeechDriverThread (volatile SpeechSynthesizer *spk) {
volatile SpeechDriverThread *sdt = spk->driver.thread;
deleteElements(sdt->requestQueue);
#ifdef GOT_PTHREADS
if (enqueueSpeechRequest(sdt, NULL)) {
awaitSpeechResponse(sdt, SPEECH_DRIVER_THREAD_STOP_TIMEOUT);
setResponsePending(sdt);
awaitSpeechResponse(sdt, SPEECH_DRIVER_THREAD_STOP_TIMEOUT);
awaitSpeechDriverThreadTermination(sdt);
}
if (sdt->messageEvent) asyncDiscardEvent(sdt->messageEvent);
stopSpeechDriver(sdt);
setThreadState(sdt, THD_FINISHED);
sdt->speechSynthesizer->driver.thread = NULL;
deallocateQueue(sdt->requestQueue);
free((void *)sdt);
}
#endif /* ENABLE_SPEECH_SUPPORT */