blob: 5c1189a7738e59f4474bba96363573975147d36f [file] [log] [blame] [edit]
/*
* 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-2023 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.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
/* spktest.c - Test progrm for the speech synthesizer drivers.
*/
#include "prologue.h"
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include "program.h"
#include "cmdline.h"
#include "log.h"
#include "spk.h"
#include "file.h"
#include "parse.h"
#include "async_wait.h"
static char *opt_textString;
static char *opt_speechVolume;
static char *opt_speechRate;
static char *opt_pcmDevice;
static char *opt_driversDirectory;
BEGIN_OPTION_TABLE(programOptions)
{ .word = "text-string",
.letter = 't',
.argument = "string",
.setting.string = &opt_textString,
.description = "Text to be spoken."
},
{ .word = "volume",
.letter = 'v',
.argument = "loudness",
.setting.string = &opt_speechVolume,
.description = "Floating-point speech volume multiplier."
},
{ .word = "rate",
.letter = 'r',
.argument = "speed",
.setting.string = &opt_speechRate,
.description = "Floating-point speech rate multiplier."
},
{ .word = "device",
.letter = 'd',
.argument = "device",
.setting.string = &opt_pcmDevice,
.description = "Digital audio soundcard device specifier."
},
{ .word = "drivers-directory",
.letter = 'D',
.argument = "directory",
.setting.string = &opt_driversDirectory,
.internal.setting = DRIVERS_DIRECTORY,
.internal.adjust = fixInstallPath,
.description = "Path to directory for loading drivers."
},
END_OPTION_TABLE(programOptions)
static int
say (SpeechSynthesizer *spk, const char *string) {
if (!sayString(spk, string, 0)) return 0;
asyncWait(250);
return 1;
}
static int
sayLine (const LineHandlerParameters *parameters) {
SpeechSynthesizer *spk = parameters->data;
say(spk, parameters->line.text);
return 1;
}
int
main (int argc, char *argv[]) {
ProgramExitStatus exitStatus;
SpeechSynthesizer spk;
const char *driver = NULL;
void *object;
int speechVolume = SPK_VOLUME_DEFAULT;
int speechRate = SPK_RATE_DEFAULT;
{
const CommandLineDescriptor descriptor = {
.options = &programOptions,
.applicationName = "spktest",
.usage = {
.purpose = strtext("Test a speech driver."),
.parameters = "[driver [parameter=value ...]]",
}
};
PROCESS_OPTIONS(descriptor, argc, argv);
}
if (opt_speechVolume && *opt_speechVolume) {
static const int minimum = 0;
static const int maximum = SPK_VOLUME_MAXIMUM;
if (!validateInteger(&speechVolume, opt_speechVolume, &minimum, &maximum)) {
logMessage(LOG_ERR, "%s: %s", "invalid volume multiplier", opt_speechVolume);
return PROG_EXIT_SYNTAX;
}
}
if (opt_speechRate && *opt_speechRate) {
static const int minimum = 0;
static const int maximum = SPK_RATE_MAXIMUM;
if (!validateInteger(&speechRate, opt_speechRate, &minimum, &maximum)) {
logMessage(LOG_ERR, "%s: %s", "invalid rate multiplier", opt_speechRate);
return PROG_EXIT_SYNTAX;
}
}
if (argc) {
driver = *argv++, --argc;
}
if ((speech = loadSpeechDriver(driver, &object, opt_driversDirectory))) {
const char *const *parameterNames = speech->parameters;
char **parameterSettings;
if (!parameterNames) {
static const char *const noNames[] = {NULL};
parameterNames = noNames;
}
{
const char *const *name = parameterNames;
unsigned int count;
char **setting;
while (*name) name += 1;
count = name - parameterNames;
if (!(parameterSettings = malloc((count + 1) * sizeof(*parameterSettings)))) {
logMallocError();
return PROG_EXIT_FATAL;
}
setting = parameterSettings;
while (count--) *setting++ = "";
*setting = NULL;
}
while (argc) {
char *assignment = *argv++;
int ok = 0;
char *delimiter = strchr(assignment, '=');
if (!delimiter) {
logMessage(LOG_ERR, "missing speech driver parameter value: %s", assignment);
} else if (delimiter == assignment) {
logMessage(LOG_ERR, "missing speech driver parameter name: %s", assignment);
} else {
size_t nameLength = delimiter - assignment;
const char *const *name = parameterNames;
while (*name) {
if (strncasecmp(assignment, *name, nameLength) == 0) {
parameterSettings[name - parameterNames] = delimiter + 1;
ok = 1;
break;
}
name += 1;
}
if (!ok) logMessage(LOG_ERR, "invalid speech driver parameter: %s", assignment);
}
if (!ok) return PROG_EXIT_SYNTAX;
argc -= 1;
}
constructSpeechSynthesizer(&spk);
identifySpeechDriver(speech, 0); /* start-up messages */
if (startSpeechDriverThread(&spk, parameterSettings)) {
setSpeechVolume(&spk, speechVolume, 0);
setSpeechRate(&spk, speechRate, 0);
if (opt_textString && *opt_textString) {
say(&spk, opt_textString);
} else {
processLines(stdin, sayLine, (void *)&spk);
}
drainSpeech(&spk);
stopSpeechDriverThread(&spk);
exitStatus = PROG_EXIT_SUCCESS;
} else {
logMessage(LOG_ERR, "can't initialize speech driver");
exitStatus = PROG_EXIT_FATAL;
}
} else {
logMessage(LOG_ERR, "can't load speech driver");
exitStatus = PROG_EXIT_FATAL;
}
return exitStatus;
}