Compare commits

..

No commits in common. "master" and "v0.6" have entirely different histories.

34 changed files with 903 additions and 2039 deletions

View file

@ -1,41 +0,0 @@
---
Language: Cpp
BasedOnStyle: LLVM
AlignAfterOpenBracket: Align
AlignEscapedNewlines: Left
AlignOperands: AlignAfterOperator
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortEnumsOnASingleLine: false
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
BinPackArguments: true
BinPackParameters: true
BreakBeforeBraces: Linux
ColumnLimit: 80
IndentPPDirectives: BeforeHash
IndentWidth: 4
InsertTrailingCommas: None
KeepEmptyLinesAtTheStartOfBlocks: false
PointerAlignment: Right
ReflowComments: true
SortIncludes: true
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: ControlStatements
SpaceAroundPointerQualifiers: Before
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 5
TabWidth: 4
UseTab: AlignWithSpaces
...

3
.gitignore vendored
View file

@ -1 +1,2 @@
build*
espeakup
*.o

2
.indent.pro vendored
View file

@ -1,2 +0,0 @@
-kr
-ts4

45
Makefile Normal file
View file

@ -0,0 +1,45 @@
INSTALL = install
SRCS = \
cli.c \
espeakup.c \
queue.c \
softsynth.c \
synth.c
OBJS = $(SRCS:.c=.o)
LDLIBS = -lespeak
PREFIX = /usr
MANDIR = $(PREFIX)/share/man/man8
BINDIR = $(PREFIX)/bin
all: espeakup
install: espeakup
$(INSTALL) -d $(DESTDIR)$(BINDIR)
$(INSTALL) -m 0755 $< $(DESTDIR)$(BINDIR)
$(INSTALL) -d $(DESTDIR)$(MANDIR)
$(INSTALL) -m 0644 espeakup.8 $(DESTDIR)$(MANDIR)
clean:
$(RM) $(OBJS)
distclean: clean
$(RM) espeakup
espeakup: $(OBJS)
cli.o: cli.c espeakup.h
espeakup.o: espeakup.c espeakup.h
queue.o: queue.c espeakup.h
softsynth.o: softsynth.c espeakup.h
synth.o: synth.c espeakup.h
%.o: %.c
$(CC) -c -Wall $(CFLAGS) $(CPPFLAGS) -o $@ $<

63
README Normal file
View file

@ -0,0 +1,63 @@
espeakup connector
=======================
This is very early alpha software, so please keep that in mind if you
use this program.
espeakup is a program which makes it possible for speakup to use
the espeak software synthesizer. It does this by reading speakup's
softsynth device and passing the text to espeak which actually speaks.
Requirements
============
This program works with the speakup screen reader, which can be obtained
from http://linux-speakup.org, and the espeak software speech
synthesizer which can be obtained from http://espeak.sourceforge.net.
You must have both of these installed and operational. Setting them up
is beyond the scope of this document.
Installation
============
The preferred way to install espeakup is using your distribution's
packaging system, but if your distribution does not have a package for
espeakup yet, here are some basic instructions.
To install espeakup, first cd into the directory where you
unpacked the tarball, then issue make to compile the program. Once this
is done, as root, issue make install. This will install espeakup in /usr/bin.
Starting Up
===========
This program should be run after speakup is set up to communicate with a
software synthesizer and after /dev/softsynth exists. The way this is
done is distribution specific, so it is beyond the scope of this
documentation.
Command Line Options
====================
Espeakup currently accepts the following command line options:
--default-voice=voice, -V voice Set default voice.
--debug, -d Debug mode (stay in the foreground).
--help, -h Show this help.
--version, -v Display the software version.
Questions
=========
You can contact me with questions, bugs, patches, etc, at
w.d.hubbs@gmail.com or on the speakup mailing list. I hope you find
this software to be useful.
Acknowledgements
================
I would like to thank Marc Mulcahy, the author of the speakup to
TTSynth connector, on which this work is based. Also, I would like
to thank Kirk Reiser and Jonathan Duttington, the
authors of Speakup and Espeak, respectively, for their work.

View file

@ -1,48 +0,0 @@
# espeakup connector
espeakup is a program which makes it possible for speakup to use
the espeak-ng software synthesizer. It does this by reading speakup's
softsynth device and passing the text to espeak-ng which actually speaks.
## Requirements
This program works with the speakup screen reader, which can be obtained
from http://linux-speakup.org, and the
[espeak-ng](https://github.com/espeak-ng/espeak-ng) software speech
synthesizer.
You must have both of these installed and operational. Setting them up
is beyond the scope of this document.
## Installation
The preferred way to install espeakup is using your distribution's
packaging system, but if your distribution does not have a package for
espeakup yet, espeakup just uses meson, so you should be able to
change to the source directory, then type:
```bash
meson setup . ./build
cd ./build
ninja
sudo ninja install
```
## Starting Up
This program should be run after speakup is set up to communicate with a
software synthesizer and after /dev/softsynth exists. The way this is
done is distribution specific, so it is beyond the scope of this
documentation.
## Acknowledgements
I would like to thank Marc Mulcahy, the author of the speakup to
TTSynth connector, on which this work is based. Also, I would like
to thank Kirk Reiser and Jonathan Duddington, the
authors of Speakup and Espeak, respectively, for their work.
## Filing Bugs
Bugs should be filed on our bug tracker at
https://github.com/linux-speakup/issues.

11
ToDo Normal file
View file

@ -0,0 +1,11 @@
This file contains a list of improvements that need to be made to
espeakup.
Voice and language support should be added at some point.
This will consist of the following:
- add a function which will allow switching voices. (completed on 9/27)
- add a function/command which will allow speakup to find out which
voices espeak supports.
- define and add the command to speakup which will allow it to switch
voices.

View file

@ -1,5 +1,5 @@
/*
* espeakup - interface which allows speakup to use espeak-ng
* espeakup - interface which allows speakup to use espeak
*
* Copyright (C) 2008 William Hubbs
*
@ -23,37 +23,28 @@
#include <string.h>
#include "espeakup.h"
#include "stringhandling.h"
#include "version.h"
/* pid path */
extern char *pidPath;
/* program version */
extern const char *Version;
/* default voice */
extern char *defaultVoice;
/* Whether to drive ALSA volume */
extern int alsaVolume;
/* command line options */
const char *shortOptions = "P:V:adhv";
const char *shortOptions = "dhV:v";
const struct option longOptions[] = {
{"pid-path", required_argument, NULL, 'P'},
{"default-voice", required_argument, NULL, 'V'},
{"alsa-volume", no_argument, &alsaVolume, 1},
{"acsint", no_argument, NULL, 'a'},
{"debug", no_argument, NULL, 'd'},
{"help", no_argument, NULL, 'h'},
{"version", no_argument, NULL, 'v'},
{0, 0, 0, 0}};
{0, 0, 0, 0}
};
static void show_help()
{
printf("Usage: espeakup [options]\n\n");
printf("Options are as follows:\n");
printf(" --pid-path=path, -P path\t\tSet path for pid file.\n");
printf(" --default-voice=voice, -V voice\tSet default voice.\n");
printf(" --alsa-volume\t\t\t\tDrive the ALSA volume.\n");
printf(" --debug, -d\t\t\t\tDebug mode (stay in the foreground).\n");
printf(" --help, -h\t\t\t\tShow this help.\n");
printf(" --version, -v\t\t\t\tDisplay the software version.\n");
@ -62,11 +53,10 @@ static void show_help()
static void show_version(void)
{
printf("ESpeakup %s\n", PACKAGE_VERSION);
printf("Copyright (C) 2008 William Hubbs <w.d.hubbs@gmail.com>\n");
printf("espeakup %s\n", Version);
printf("Copyright (C) 2008 William Hubbs\n");
printf("License GPLv3+: GNU GPL version 3 or later\n");
printf("You are free to change and redistribute this software.\n");
printf("Please report bugs to %s\n", PACKAGE_BUGREPORT);
exit(0);
}
@ -77,14 +67,8 @@ void process_cli(int argc, char **argv)
do {
opt = getopt_long(argc, argv, shortOptions, longOptions, NULL);
switch (opt) {
case 'P':
pidPath = dupeString(optarg);
break;
case 'V':
defaultVoice = dupeString(optarg);
break;
case 'a':
espeakup_mode = ESPEAKUP_MODE_ACSINT;
defaultVoice = strdup(optarg);
break;
case 'd':
debug = 1;
@ -96,7 +80,6 @@ void process_cli(int argc, char **argv)
show_version();
break;
case -1:
case 0:
break;
default:
show_help();

View file

@ -1,77 +0,0 @@
<!-- markdownlint-disable MD036 -->
# espeakup(8) --- connect Speakup to the espeak-ng TTS engine
## SYNOPSIS
`espeakup` [`--pid-path=`<path>] [`--alsa-volume`]
[`--default-voice=`[<voicename>]] [`--debug`] [`--help`] [`--version`]
## OPTIONS
* `-P` <path>, `--pid-path=`<path>:
Set the full path for the pid file espeakup uses when in daemon mode.
* `--alsa-volume`:
Drive the ALSA volume. useful for live environments where volume
adjustments maybe impossible.
* `-V` <voicename>, `--default-voice=`<voicename>:
Set the espeak-ng voice to be used by default.
* `-d`, `--debug`:
run in the foreground, rather than becoming a daemon process.
* `-h`, `--help`:
display a brief help message and exit.
* `-v`, `--version`:
output version information and exit.
## DESCRIPTION
espeakup bridges the gap between two tools: the Speakup screen review system and
the espeak-ng text-to-speech engine. Each of these tools performs a
well-defined task.
Speakup is a kernel-based screen reader for the Linux console. It extracts and
processes the text that is displayed on the foreground virtual console. It
supports several hardware based speech synthesizers directly. However, since it
is in kernel space, it cannot support a software speech synthesizer directly
since these are in user space.
espeak-ng is a popular software speech synthesizer. It is small, light weight,
very responsive, and supports multiple languages. espeakup is a connector which
will read text sent to it by speakup and forward it to espeak-ng. This allows
Speakup to use espeak-ng as its speech synthesizer.
espeakup is a daemon. Typically, it is started at boot time, and it terminates
when the system is halted or rebooted. It should be started by the system's init
scripts. This process varies among Linux distributions, but the details are
usually managed by the person who packaged espeakup for your distribution. From
the perspective of an average user, espeakup's operation is invisible.
## BUGS
If you find a bug, please create a
[github issue](https://github.com/linux-speakup/espeakup/issues)
You might also consider mentioning it on the mailing list for the Speakup
screenreader. Visit
[list page](https://linux-speakup.org/cgi-bin/mailman/listinfo/speakup)
to learn more about the mailing list.
## SEE ALSO
For more information about Speakup, visit its
[homepage](https://linux-speakup.org).
espeak-ng can be found at [github](https://github.com/espeak-ng/espeak-ng)
## AUTHOR
William Hubbs <w.d.hubbs@gmail.com> is the author of espeakup.
This manual page was written by Chris Brannon <chris@the-brannons.com>.
current authors and maintainers can be found at
[github](https://github.com/linux-speakup/espeakup/graphs/contributors)

View file

@ -1,8 +0,0 @@
ronn = find_program('ronn', required: get_option('man'))
if ronn.found()
custom_target('man',
input : files('espeakup.8.ronn'),
output : 'espeakup.8',
command : [ronn, '--output-dir', '@OUTDIR@', '--roff', '@INPUT@'],
install : true, install_dir: join_paths(get_option('mandir'),'man8'))
endif

72
espeakup.8 Normal file
View file

@ -0,0 +1,72 @@
.\" Hey, Emacs! This is an -*- nroff -*- source file.
.\" Espeakup is Copyright 2008 by William Hubbs.
.\" This is free software; see the GNU General Public Licence version 3
.\" or later for copying conditions. There is NO warranty.
.TH ESPEAKUP 8 "5 Nov 2008" "0.6"
.nh
.SH NAME
espeakup \(em connect Speakup to the ESpeak TTS engine
.SH SYNOPSIS
.B espeakup
[
.B \-\^\-default-voice=voicename
]
[
.B \-\^\-debug
]
[
.B \-\^\-help
]
[
.B \-\^\-version
]
.SH OPTIONS
.TP
.B \-V voicename, \-\^\-default-voice=voicename
Set the espeak voice to be used by default.
.TP
.B \-d, \-\^\-debug
run in the foreground, rather than becoming a daemon process.
.TP
.B \-h, \-\^\-help
display a brief help message and exit.
.TP
.B \-v, \-\^\-version
output version information and exit.
.SH DESCRIPTION
Espeakup bridges the gap between two tools: the Speakup screen review
system and the ESppeak text-to-speech engine. Each of these tools
performs a well-defined task. Speakup is a kernel-based screen reader
for the Linux console. It extracts and processes the text that is
displayed on the foreground virtual console. It supports several
hardware based speech synthesizers directly. However, since it is in
kernel space, it cannot support a software speech synthesizer directly
since these are in user space.
ESpeak is a popular software speech synthesizer. It is small, light
weight, very responsive, and supports multiple languages.
Espeakup is a connector which will read text sent to it by speakup and
forward it to ESpeak. This allows Speakup to use ESpeak as its speech
synthesizer.
.PP
Espeakup is a daemon. Typically, it is started at boot time, and it terminates
when the system is halted or rebooted. It should be started by the
system's init scripts. This process varies among Linux distributions,
but the details are usually managed by the person who packaged Espeakup for
your distribution.
From the perspective of an average user, Espeakup's operation is invisible.
.SH BUGS
.PP
Espeakup is still classified as alpha software. Bugs are periodically found
and fixed. If you find a bug, please do report it to the author. You
might also consider mentioning it on the mailing list for the Speakup
screenreader. Visit http://speech.braille.uwo.ca/mailman/listinfo/speakup
to learn more about the mailing list.
.SH SEE ALSO
.PP
For more information about Speakup, visit its homepage: http://linux-speakup.org.
ESpeak's home page is http://espeak.sourceforge.net.
.SH AUTHOR
.PP
William Hubbs is the author and maintainer of Espeakup. He may be reached
via the email address <w.d.hubbs@gmail.com>. This manual page was written
by Chris Brannon, and his email address is <cmbrannon79@gmail.com>.

142
espeakup.c Normal file
View file

@ -0,0 +1,142 @@
/*
* espeakup - interface which allows speakup to use espeak
*
* Copyright (C) 2008 William Hubbs
*
* 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 <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "espeakup.h"
/* program version */
const char *Version = "0.6";
/* path to our pid file */
const char *pidPath = "/var/run/espeakup.pid";
/* default voice settings */
const int defaultFrequency = 5;
const int defaultPitch = 5;
const int defaultRate = 5;
const int defaultVolume = 5;
char *defaultVoice = NULL;
int debug = 0;
int espeakup_is_running(void)
{
int rc;
FILE *pidFile;
pid_t pid;
rc = 0;
pidFile = fopen(pidPath, "r");
if (pidFile) {
fscanf(pidFile, "%d", &pid);
fclose(pidFile);
if (!kill(pid, 0) || errno != ESRCH)
rc = 1;
}
return rc;
}
int create_pid_file(void)
{
FILE *pidFile;
pidFile = fopen(pidPath, "w");
if (!pidFile)
return -1;
fprintf(pidFile, "%d\n", getpid());
fclose(pidFile);
return 0;
}
void espeakup_sighandler(int sig)
{
if (debug)
printf("Caught signal %i\n", sig);
/* clear the queue */
queue_clear();
/* shut down espeak and close the softsynth */
espeak_Terminate();
close_softsynth();
if (!debug)
unlink(pidPath);
exit(0);
}
int main(int argc, char **argv)
{
struct synth_t s = {
.voice = "",
};
/* process command line options */
process_cli(argc, argv);
/* Is the espeakup daemon running? */
if (espeakup_is_running()) {
printf("Espeakup is already running!\n");
return 1;
}
if (!debug) {
/* become a daemon */
daemon(0, 1);
/* write our pid file. */
if (create_pid_file() < 0) {
perror("Unable to create pid file");
return 2;
}
}
/* open the softsynth. */
open_softsynth();
/* initialize espeak */
espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, NULL, 0);
/* Setup initial voice parameters */
if (defaultVoice) {
set_voice(&s, defaultVoice);
free(defaultVoice);
defaultVoice = NULL;
}
set_frequency(&s, defaultFrequency, ADJ_SET);
set_pitch(&s, defaultPitch, ADJ_SET);
set_rate(&s, defaultRate, ADJ_SET);
set_volume(&s, defaultVolume, ADJ_SET);
/* register signal handler */
signal(SIGINT, espeakup_sighandler);
signal(SIGTERM, espeakup_sighandler);
/* run the main loop */
main_loop(&s);
return 0;
}

82
espeakup.h Normal file
View file

@ -0,0 +1,82 @@
/*
* espeakup - interface which allows speakup to use espeak
*
* Copyright (C) 2008 William Hubbs
*
* 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 __ESPEAKUP_H
#define __ESPEAKUP_H
/* This was added for gcc 4.3 */
#include <stddef.h>
#include <espeak/speak_lib.h>
enum command_t {
CMD_SET_FREQUENCY,
CMD_SET_PITCH,
CMD_SET_PUNCTUATION,
CMD_SET_RATE,
CMD_SET_VOICE,
CMD_SET_VOLUME,
CMD_SPEAK_TEXT,
CMD_FLUSH,
CMD_UNKNOWN,
};
enum adjust_t {
ADJ_DEC,
ADJ_SET,
ADJ_INC,
};
struct synth_t {
int frequency;
int pitch;
int punct;
int rate;
char voice[10];
int volume;
char *buf;
int len;
};
extern int debug;
extern void process_cli(int argc, char **argv);
extern void queue_clear(void);
extern void queue_add_cmd(enum command_t cmd, enum adjust_t adj,
int value);
extern void queue_add_text(char *txt, size_t length);
extern void queue_process_entry(struct synth_t *s);
extern espeak_ERROR set_frequency(struct synth_t *s, int freq,
enum adjust_t adj);
extern espeak_ERROR set_pitch(struct synth_t *s, int pitch,
enum adjust_t adj);
extern espeak_ERROR set_punctuation(struct synth_t *s, int punct,
enum adjust_t adj);
extern espeak_ERROR set_rate(struct synth_t *s, int rate,
enum adjust_t adj);
extern espeak_ERROR set_voice(struct synth_t *s, char *voice);
extern espeak_ERROR set_volume(struct synth_t *s, int vol,
enum adjust_t adj);
extern espeak_ERROR stop_speech(void);
extern espeak_ERROR speak_text(struct synth_t *s);
extern void open_softsynth(void);
extern void close_softsynth(void);
extern void main_loop(struct synth_t *s);
#endif

View file

@ -1,25 +0,0 @@
project('espeakup', 'c',
default_options : [
'buildtype=debugoptimized',
'c_std=gnu11',
'warning_level=3'
],
license : 'GPL-3.0-or-later',
version : '0.90',
meson_version : '>=0.51.0')
cc = meson.get_compiler('c')
thread_dep = dependency('threads')
espeak_dep = dependency('espeak-ng')
alsa_dep = dependency('alsa')
math_dep = cc.find_library('m', required : false)
subdir('doc')
subdir('services')
subdir('src')
executable('espeakup',
espeakup_version,
espeakup_sources,
dependencies : [thread_dep, espeak_dep, alsa_dep, math_dep],
install : true)

View file

@ -1,4 +0,0 @@
option('man', type : 'feature', value : 'auto',
description : 'build manpage with ronn')
option('systemd', type : 'feature', value : 'auto',
description :'enable systemd support')

144
queue.c Normal file
View file

@ -0,0 +1,144 @@
/*
* espeakup - interface which allows speakup to use espeak
*
* Copyright (C) 2008 William Hubbs
*
* 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 <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "espeakup.h"
struct queue_entry_t {
enum command_t cmd;
enum adjust_t adjust;
int value;
char *buf;
int len;
struct queue_entry_t *next;
};
static struct queue_entry_t *first = NULL;
static struct queue_entry_t *last = NULL;
static void queue_add(struct queue_entry_t *entry)
{
assert(entry);
entry->next = NULL;
if (!last)
last = entry;
if (!first) {
first = entry;
} else {
first->next = entry;
first = first->next;
}
}
static void queue_remove(void)
{
struct queue_entry_t *temp;
assert(last);
temp = last;
last = temp->next;
if (temp->cmd == CMD_SPEAK_TEXT)
free(temp->buf);
free(temp);
if (!last)
first = last;
}
void queue_clear(void)
{
while (last)
queue_remove();
}
void queue_add_cmd(enum command_t cmd, enum adjust_t adj, int value)
{
struct queue_entry_t *entry;
entry = malloc(sizeof(struct queue_entry_t));
if (!entry) {
perror("unable to allocate memory for queue entry");
return;
}
entry->cmd = cmd;
entry->adjust = adj;
entry->value = value;
queue_add(entry);
}
void queue_add_text(char *txt, size_t length)
{
struct queue_entry_t *entry;
entry = malloc(sizeof(struct queue_entry_t));
if (!entry) {
perror("unable to allocate memory for queue entry");
return;
}
entry->cmd = CMD_SPEAK_TEXT;
entry->adjust = ADJ_SET;
entry->buf = strdup(txt);
if (!entry->buf) {
perror("unable to allocate space for text");
free(entry);
return;
}
entry->len = length;
queue_add(entry);
}
void queue_process_entry(struct synth_t *s)
{
espeak_ERROR error;
if (!last)
return;
switch (last->cmd) {
case CMD_SET_FREQUENCY:
error = set_frequency(s, last->value, last->adjust);
break;
case CMD_SET_PITCH:
error = set_pitch(s, last->value, last->adjust);
break;
case CMD_SET_PUNCTUATION:
error = set_punctuation(s, last->value, last->adjust);
break;
case CMD_SET_RATE:
error = set_rate(s, last->value, last->adjust);
break;
case CMD_SET_VOICE:
break;
case CMD_SET_VOLUME:
error = set_volume(s, last->value, last->adjust);
break;
case CMD_SPEAK_TEXT:
s->buf = last->buf;
s->len = last->len;
error = speak_text(s);
break;
default:
break;
}
if (error == EE_OK)
queue_remove();
}

View file

@ -1,4 +0,0 @@
systemd = dependency('systemd')
if (systemd.found() and get_option('systemd').allowed()) or get_option('systemd').enabled()
subdir('systemd')
endif

View file

@ -1,18 +0,0 @@
[Unit]
Description=Software speech output for Speakup
Documentation=man:espeakup(8)
Wants=modprobe@speakup_soft.service
After=modprobe@speakup_soft.service sound.target
[Service]
Type=forking
PIDFile=/run/espeakup.pid
Environment="default_voice="
ExecStart=@bindir@/espeakup --default-voice=${default_voice}
ExecReload=kill -HUP $MAINPID
Restart=always
Nice=-10
OOMScoreAdjust=-900
[Install]
WantedBy=sound.target

View file

@ -1,20 +0,0 @@
if systemd.found()
unitdir = systemd.get_variable(pkgconfig: 'systemdsystemunitdir')
else
unitdir = join_paths(prefixdir, get_option('libdir'), 'systemd', 'system')
endif
prefixdir = get_option('prefix')
bindir = join_paths(prefixdir, get_option('bindir'))
unit_conf = configuration_data()
unit_conf.set('bindir', bindir)
service_file = configure_file(
input : 'espeakup.service.in',
output : 'espeakup.service',
configuration : unit_conf
)
install_data(service_file,
install_dir : unitdir
)

184
softsynth.c Normal file
View file

@ -0,0 +1,184 @@
/*
* espeakup - interface which allows speakup to use espeak
*
* Copyright (C) 2008 William Hubbs
*
* 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 <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include "espeakup.h"
/* max buffer size */
const size_t maxBufferSize = 1025;
/* synth flush character */
const int synthFlushChar = 0x18;
static int softFD = 0;
static int process_command(struct synth_t *s, char *buf, int start)
{
char *cp;
int value;
enum adjust_t adj;
enum command_t cmd;
cp = buf + start;
switch (*cp) {
case 1:
cp++;
switch (*cp) {
case '+':
adj = ADJ_INC;
cp++;
break;
case '-':
adj = ADJ_DEC;
cp++;
break;
default:
adj = ADJ_SET;
break;
}
value = 0;
while (isdigit(*cp)) {
value = value * 10 + (*cp - '0');
cp++;
}
switch (*cp) {
case 'b':
cmd = CMD_SET_PUNCTUATION;
break;
case 'f':
cmd = CMD_SET_FREQUENCY;
break;
case 'p':
cmd = CMD_SET_PITCH;
break;
case 's':
cmd = CMD_SET_RATE;
break;
case 'v':
cmd = CMD_SET_VOLUME;
break;
default:
cmd = CMD_UNKNOWN;
break;
}
cp++;
break;
default:
cmd = CMD_UNKNOWN;
cp++;
break;
}
if (cmd != CMD_FLUSH && cmd != CMD_UNKNOWN)
queue_add_cmd(cmd, adj, value);
return cp - (buf + start);
}
static void process_buffer(struct synth_t *s, char *buf, ssize_t length)
{
int start;
int end;
char txtBuf[maxBufferSize];
size_t txtLen;
start = 0;
end = 0;
while (start < length) {
while (isprint(buf[end]) && end < length)
end++;
if (end != start) {
txtLen = end - start;
strncpy(txtBuf, buf + start, txtLen);
*(txtBuf + txtLen) = 0;
queue_add_text(txtBuf, txtLen);
}
if (end < length)
start = end = end + process_command(s, buf, end);
else
start = length;
}
}
void open_softsynth(void)
{
softFD = open("/dev/softsynth", O_RDWR | O_NONBLOCK);
if (softFD < 0) {
perror("Unable to open the softsynth device");
exit(3);
}
}
void close_softsynth(void)
{
close(softFD);
}
void main_loop(struct synth_t *s)
{
fd_set set;
struct timeval tv;
ssize_t length;
char buf[maxBufferSize];
char *cp;
while (1) {
queue_process_entry(s);
FD_ZERO(&set);
FD_SET(softFD, &set);
tv.tv_sec = 0;
tv.tv_usec = 500;
if (select(softFD + 1, &set, NULL, NULL, &tv) < 0) {
if (errno == EINTR)
continue;
perror("Select failed");
break;
}
if (!FD_ISSET(softFD, &set))
continue;
length = read(softFD, buf, maxBufferSize - 1);
if (length < 0) {
if (errno == EAGAIN || errno == EINTR)
continue;
perror("Read from softsynth failed");
break;
}
*(buf + length) = 0;
cp = strrchr(buf, synthFlushChar);
if (cp) {
queue_clear();
stop_speech();
memmove(buf, cp + 1, strlen(cp + 1) + 1);
length = strlen(buf);
}
process_buffer(s, buf, length);
}
}

View file

@ -1,638 +0,0 @@
/*
* espeakup - interface which allows speakup to use espeak-ng
*
* Copyright (C) 2008 William Hubbs
*
* 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/>.
*/
#define _GNU_SOURCE
#include <alsa/asoundlib.h>
#include <assert.h>
#include <math.h>
#include <stdatomic.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "espeakup.h"
/* default voice settings */
const int defaultFrequency = 5;
const int defaultPitch = 5;
const int defaultRange = 5;
const int defaultRate = 2;
const int defaultVolume = 5;
char *defaultVoice = NULL;
int alsaVolume = 0;
/* multipliers and offsets */
const int frequencyMultiplier = 11;
const int pitchMultiplier = 11;
const int rangeMultiplier = 11;
const int rateMultiplier = 41;
const int rateOffset = 80;
const int volumeMultiplier = 22;
volatile int stop_requested = 0;
int paused_espeak = 1;
/* Wedged-engine detection. Espeak may legitimately refuse entries for a
* while (EE_BUFFER_FULL while a long backlog is being played back), so a
* failing entry is normally just retried. But when entries keep failing
* while the synth callback reports no progress at all, the audio output
* is most likely wedged (e.g. an ALSA device stuck returning EBUSY).
* After ESPEAK_STALL_RETRIES consecutive such retries (about one second
* each), restart the engine; after ESPEAK_MAX_RESTARTS restarts without
* the engine having been healthy for ESPEAK_HEALTHY_SECS in between,
* give up and exit, so that the init system can respawn us. */
#define ESPEAK_STALL_RETRIES 10
#define ESPEAK_MAX_RESTARTS 3
#define ESPEAK_HEALTHY_SECS 60
/* Set by the synth callback whenever espeak makes synthesis progress;
* used to tell a merely backlogged engine from a wedged one. The
* callback runs in espeak's own thread, so the flag is atomic. */
static atomic_int synth_progressed = 0;
static int stalled_retries = 0;
static int restart_attempts = 0;
static struct timespec last_restart;
static int callback(short *wav, int numsamples, espeak_EVENT *events)
{
int i;
atomic_store(&synth_progressed, 1);
for (i = 0; events[i].type != espeakEVENT_LIST_TERMINATED; i++) {
if (events[i].type == espeakEVENT_MARK) {
int mark = atoi(events[i].id.name);
if ((mark < 0) || (mark > 255))
continue;
softsynth_reportindex(mark);
}
}
return 0;
}
static espeak_ERROR set_frequency(struct synth_t *s, int freq,
enum adjust_t adj)
{
espeak_ERROR rc;
if (adj == ADJ_DEC)
freq = -freq;
if (adj != ADJ_SET)
freq += s->frequency;
rc = espeak_SetParameter(espeakRANGE, freq * frequencyMultiplier, 0);
if (rc == EE_OK)
s->frequency = freq;
return rc;
}
static espeak_ERROR set_pitch(struct synth_t *s, int pitch, enum adjust_t adj)
{
espeak_ERROR rc;
if (adj == ADJ_DEC)
pitch = -pitch;
if (adj != ADJ_SET)
pitch += s->pitch;
rc = espeak_SetParameter(espeakPITCH, pitch * pitchMultiplier, 0);
if (rc == EE_OK)
s->pitch = pitch;
return rc;
}
static espeak_ERROR set_range(struct synth_t *s, int range, enum adjust_t adj)
{
espeak_ERROR rc;
if (adj == ADJ_DEC)
range = -range;
if (adj != ADJ_SET)
range += s->range;
rc = espeak_SetParameter(espeakRANGE, range * rangeMultiplier, 0);
if (rc == EE_OK)
s->range = range;
return rc;
}
static espeak_ERROR set_punctuation(struct synth_t *s, int punct,
enum adjust_t adj)
{
espeak_ERROR rc;
espeak_PUNCT_TYPE espeak_punct;
if (adj == ADJ_DEC)
punct = -punct;
if (adj != ADJ_SET)
punct += s->punct;
switch (punct) {
case 0:
espeak_punct = espeakPUNCT_NONE;
break;
case 1:
espeak_punct = espeakPUNCT_SOME;
break;
case 2:
/* XXX: approximation */
espeak_punct = espeakPUNCT_SOME;
break;
case 3:
default:
espeak_punct = espeakPUNCT_ALL;
break;
}
rc = espeak_SetParameter(espeakPUNCTUATION, espeak_punct, 0);
if (rc == EE_OK)
s->punct = punct;
return rc;
}
static espeak_ERROR set_rate(struct synth_t *s, int rate, enum adjust_t adj)
{
espeak_ERROR rc;
if (adj == ADJ_DEC)
rate = -rate;
if (adj != ADJ_SET)
rate += s->rate;
rc = espeak_SetParameter(espeakRATE, rate * rateMultiplier + rateOffset, 0);
if (rc == EE_OK)
s->rate = rate;
return rc;
}
static espeak_ERROR set_voice(struct synth_t *s, char *voice)
{
espeak_ERROR rc;
espeak_VOICE voice_select;
rc = espeak_SetVoiceByName(voice);
if (rc != EE_OK) {
memset(&voice_select, 0, sizeof(voice_select));
voice_select.languages = voice;
rc = espeak_SetVoiceByProperties(&voice_select);
}
if (rc == EE_OK)
strcpy(s->voice, voice);
return rc;
}
static void set_alsa_volume(int vol)
{
snd_mixer_t *m;
snd_mixer_elem_t *e;
int err;
err = snd_mixer_open(&m, 0);
if (err < 0) {
fprintf(stderr, "ALSA mixer open error: %s\n", snd_strerror(err));
return;
}
err = snd_mixer_attach(m, "default");
if (err < 0) {
fprintf(stderr, "ALSA mixer attach error: %s\n", snd_strerror(err));
return;
}
err = snd_mixer_selem_register(m, NULL, NULL);
if (err < 0) {
fprintf(stderr, "ALSA mixer load error: %s\n", snd_strerror(err));
return;
}
err = snd_mixer_load(m);
if (err < 0) {
fprintf(stderr, "ALSA mixer load error: %s\n", snd_strerror(err));
return;
}
/* Turn vol value to volume %.
* We do not want to soften that much with ALSA, espeak is already
* doing it. We want the default value (5) to be the usual default
* volume (80%), and make higher values increase ALSA volume, up to
* 100%. */
int volume = (vol + 1) * 50 / 10 + 50;
for (e = snd_mixer_first_elem(m); e; e = snd_mixer_elem_next(e)) {
if (snd_mixer_elem_get_type(e) != SND_MIXER_ELEM_SIMPLE)
continue;
if (snd_mixer_selem_is_enumerated(e))
continue;
if (snd_mixer_selem_has_playback_switch(e)) {
snd_mixer_selem_set_playback_switch_all(e, 1);
}
if (snd_mixer_selem_has_playback_volume(e)) {
long min, max, set;
err = snd_mixer_selem_get_playback_dB_range(e, &min, &max);
if (err == 0 && min < max) {
if (max - min < 2400) {
/* 24dB amplitude is too small for using a logscale */
set = min + volume * (max - min) / 100;
} else {
/* Use a logscale */
double volf = volume / 100.;
if (min != SND_CTL_TLV_DB_GAIN_MUTE) {
double minf = pow(10, (min - max) / 6000.);
volf = volf * (1 - minf) + minf;
}
set = 6000. * log10(volf) + max;
}
snd_mixer_selem_set_playback_dB_all(e, set, 0);
} else {
/* No dB setting, try a linear scale */
snd_mixer_selem_get_playback_volume_range(e, &min, &max);
set = min + volume * (max - min) / 100;
snd_mixer_selem_set_playback_volume_all(e, set);
}
}
}
}
static espeak_ERROR set_volume(struct synth_t *s, int vol, enum adjust_t adj)
{
espeak_ERROR rc;
if (adj == ADJ_DEC)
vol = -vol;
if (adj != ADJ_SET)
vol += s->volume;
rc = espeak_SetParameter(espeakVOLUME, (vol + 1) * volumeMultiplier, 0);
if (rc == EE_OK) {
s->volume = vol;
if (alsaVolume)
set_alsa_volume(vol);
}
return rc;
}
static espeak_ERROR stop_speech(void)
{
espeak_ERROR rc;
rc = espeak_Cancel();
return rc;
}
static espeak_ERROR speak_text(struct synth_t *s)
{
espeak_ERROR rc;
int synth_mode = 0;
if (espeakup_mode == ESPEAKUP_MODE_ACSINT)
synth_mode |= espeakSSML;
if (espeakup_mode == ESPEAKUP_MODE_SPEAKUP && (s->len == 1)) {
char *buf = NULL;
int n;
unsigned char c = s->buf[0];
if (c == ' ')
n = asprintf(&buf,
"<say-as interpret-as=\"tts:char\">&#32;</say-as>");
else if (c < 0x20 || c > 0x7e) {
/* Not a printable character; do not embed it in SSML, as
* that would produce invalid markup. Fall through to the
* raw-synthesis path below. */
n = -1;
} else {
/* Escape characters that are special in XML/SSML so the
* resulting markup stays well-formed; otherwise espeak-ng
* misparses the <say-as> element, which is the source of
* the spurious high-pitched "ringing" on these characters. */
const char *entity = NULL;
switch (c) {
case '<': entity = "&lt;"; break;
case '>': entity = "&gt;"; break;
case '&': entity = "&amp;"; break;
case '\'': entity = "&apos;"; break;
case '"': entity = "&quot;"; break;
}
if (entity)
n = asprintf(&buf,
"<say-as interpret-as=\"characters\">%s</say-as>",
entity);
else
n = asprintf(&buf,
"<say-as interpret-as=\"characters\">%c</say-as>",
c);
}
if (n == -1) {
/* D'oh. Not much to do on allocation failure.
* Perhaps espeak will happen to say the character */
rc = espeak_Synth(s->buf, s->len + 1, 0, POS_CHARACTER, 0,
synth_mode, NULL, NULL);
} else {
rc = espeak_Synth(buf, n + 1, 0, POS_CHARACTER, 0, espeakSSML, NULL,
NULL);
free(buf);
}
} else
rc = espeak_Synth(s->buf, s->len + 1, 0, POS_CHARACTER, 0, synth_mode,
NULL, NULL);
return rc;
}
static void free_espeak_entry(struct espeak_entry_t *entry)
{
assert(entry);
if (entry->cmd == CMD_SPEAK_TEXT)
free(entry->buf);
free(entry);
}
static void synth_queue_clear()
{
struct espeak_entry_t *current;
while (queue_peek(synth_queue)) {
current = (struct espeak_entry_t *) queue_remove(synth_queue);
free_espeak_entry(current);
}
}
static int reinitialize_espeak(struct synth_t *s)
{
int rate;
/* Re-initialize espeak */
rate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, NULL, 0);
if (rate < 0) {
fprintf(stderr, "Unable to initialize espeak.\n");
return -1;
}
espeak_SetSynthCallback(callback);
/* Set parameters again */
espeak_SetVoiceByName(s->voice);
espeak_SetParameter(espeakRANGE, s->frequency * frequencyMultiplier, 0);
espeak_SetParameter(espeakPITCH, s->pitch * pitchMultiplier, 0);
espeak_SetParameter(espeakRATE, s->rate * rateMultiplier + rateOffset, 0);
espeak_SetParameter(espeakVOLUME, (s->volume + 1) * volumeMultiplier, 0);
espeak_SetParameter(espeakCAPITALS, 0, 0);
paused_espeak = 0;
return 0;
}
/* Wait for up to a second before retrying an entry which could not be
* processed, so that we do not busy-loop on a persistent error. Called
* and returns with queue_guard held. Wakes up immediately if a stop is
* requested. */
static void espeak_wait_retry(void)
{
struct timespec timeout;
clock_gettime(CLOCK_MONOTONIC, &timeout);
timeout.tv_sec++;
pthread_cond_timedwait(&wake_stop, &queue_guard, &timeout);
}
/* Handle an entry which could not be processed. Called and returns
* with queue_guard held.
* Normally just back off before the retry, but watch out for a wedged
* engine: if entries keep failing while the synth callback shows no
* progress at all, restart the engine, and if restarting does not help
* either, exit so that the init system respawns us in a clean state. */
static void espeak_handle_failure(struct synth_t *s)
{
if (atomic_exchange(&synth_progressed, 0)) {
/* Espeak is making progress, it is merely backlogged. */
stalled_retries = 0;
} else if (++stalled_retries >= ESPEAK_STALL_RETRIES) {
stalled_retries = 0;
if (++restart_attempts > ESPEAK_MAX_RESTARTS) {
fprintf(stderr, "espeakup: espeak keeps failing without "
"making progress and restarting it did not help, "
"aborting\n");
/* Use _exit because exit could hang in library destructors
* while the audio device is wedged. */
_exit(3);
}
fprintf(stderr, "espeakup: espeak has been failing without "
"making progress for %d seconds, restarting it\n",
ESPEAK_STALL_RETRIES);
/* Call into espeak with queue_guard released: these calls can
* take time, or block on a wedged audio device. If they do
* block forever, the stop-acknowledgement timeout in the
* softsynth thread is our last resort. */
pthread_mutex_unlock(&queue_guard);
if (!paused_espeak) {
espeak_Cancel();
espeak_Terminate();
paused_espeak = 1;
}
reinitialize_espeak(s);
clock_gettime(CLOCK_MONOTONIC, &last_restart);
pthread_mutex_lock(&queue_guard);
return;
}
espeak_wait_retry();
}
static struct espeak_entry_t *current = NULL;
static void queue_process_entry(struct synth_t *s)
{
espeak_ERROR error = EE_OK;
char markbuff[50];
if (current != queue_peek(synth_queue)) {
if (current)
free_espeak_entry(current);
current = queue_peek(synth_queue);
}
pthread_mutex_unlock(&queue_guard);
if (current->cmd != CMD_PAUSE && paused_espeak) {
if (reinitialize_espeak(s) < 0) {
/* Espeak is unavailable, so the entry cannot be processed.
* Calling espeak functions on a terminated engine would
* just fail (or worse). Leave the entry queued and retry
* after a small pause. */
pthread_mutex_lock(&queue_guard);
espeak_handle_failure(s);
return;
}
}
switch (current->cmd) {
case CMD_SET_FREQUENCY:
error = set_frequency(s, current->value, current->adjust);
break;
case CMD_SET_MARK:
snprintf(markbuff, sizeof(markbuff), "<mark name=\"%d\"/>",
current->value);
error = espeak_Synth(markbuff, strlen(markbuff) + 1, 0, POS_CHARACTER,
0, espeakSSML, NULL, NULL);
break;
case CMD_SET_PITCH:
error = set_pitch(s, current->value, current->adjust);
break;
case CMD_SET_RANGE:
error = set_range(s, current->value, current->adjust);
break;
case CMD_SET_PUNCTUATION:
error = set_punctuation(s, current->value, current->adjust);
break;
case CMD_SET_RATE:
error = set_rate(s, current->value, current->adjust);
break;
case CMD_SET_VOICE:
error = EE_OK;
break;
case CMD_SET_VOLUME:
error = set_volume(s, current->value, current->adjust);
break;
case CMD_SPEAK_TEXT:
s->buf = current->buf;
s->len = current->len;
error = speak_text(s);
break;
case CMD_PAUSE:
if (!paused_espeak) {
error = espeak_Cancel();
if (error == EE_OK)
error = espeak_Terminate();
if (error == EE_OK)
paused_espeak = 1;
} else {
error = EE_OK;
}
break;
default:
/* Uh? */
error = EE_OK;
break;
}
pthread_mutex_lock(&queue_guard);
if (error == EE_OK) {
/* Processed, drop it */
struct espeak_entry_t *unqueued = queue_remove(synth_queue);
assert(unqueued == current);
free_espeak_entry(current);
current = NULL;
stalled_retries = 0;
if (restart_attempts) {
/* Forget about past restarts once the engine has been
* healthy for a while. Entries can spuriously succeed
* right after a restart while the audio output is still
* wedged (espeak's internal queue is empty again), so a
* quick success must not reset the counter. */
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
if (now.tv_sec - last_restart.tv_sec >= ESPEAK_HEALTHY_SECS)
restart_attempts = 0;
}
} else {
if (error != EE_BUFFER_FULL)
fprintf(stderr, "espeak error: %d\n", error);
/* The entry stays queued and will be retried. Give espeak a
* little break before that, whatever the error: previously only
* EE_BUFFER_FULL throttled, and any other persistent error made
* us retry the same entry in a tight loop, burning a whole CPU. */
espeak_handle_failure(s);
}
}
int initialize_espeak(struct synth_t *s)
{
int rate;
/* initialize espeak */
rate = espeak_Initialize(AUDIO_OUTPUT_PLAYBACK, 0, NULL, 0);
if (rate < 0) {
fprintf(stderr, "Unable to initialize espeak.\n");
return -1;
}
espeak_SetSynthCallback(callback);
/* Setup initial voice parameters */
if (defaultVoice && defaultVoice[0]) {
set_voice(s, defaultVoice);
free(defaultVoice);
defaultVoice = NULL;
}
set_frequency(s, defaultFrequency, ADJ_SET);
set_pitch(s, defaultPitch, ADJ_SET);
set_range(s, defaultRange, ADJ_SET);
set_rate(s, defaultRate, ADJ_SET);
set_volume(s, defaultVolume, ADJ_SET);
espeak_SetParameter(espeakCAPITALS, 0, 0);
paused_espeak = 0;
return 0;
}
/* espeak_thread is the "main" function of our secondary (queue-processing)
* thread.
* First, lock queue_guard, because it needs to be locked when we call
* pthread_cond_wait on the runner_awake condition variable.
* Next, enter an infinite loop.
* The wait call also unlocks queue_guard, so that the other thread can
* manipulate the queue.
* When runner_awake is signaled, the pthread_cond_wait call re-locks
* queue_guard, and the "queue processor" thread has access to the queue.
* While there is an entry in the queue, call queue_process_entry.
* queue_process_entry unlocks queue_guard after removing an item from the
* queue, so that the main thread doesn't have to wait for us to finish
* processing the entry. So re-lock queue_guard after each call to
* queue_process_entry.
*
* The main thread can add items to the queue in exactly two situations:
* 1. We are waiting on runner_awake, or
* 2. We are processing an entry that has just been removed from the queue.
*/
void *espeak_thread(void *arg)
{
struct synth_t *s = (struct synth_t *) arg;
pthread_mutex_lock(&queue_guard);
while (should_run) {
while (should_run && !queue_peek(synth_queue) && !stop_requested)
pthread_cond_wait(&runner_awake, &queue_guard);
if (stop_requested) {
current = NULL;
/* Call into espeak with queue_guard released: espeak_Cancel
* can take time, or even block indefinitely when the audio
* output is wedged, and holding the lock here would prevent
* the other threads from ever making progress again. The
* queue cannot change concurrently: the only producer (the
* softsynth thread) is blocked waiting for stop_acknowledged
* as long as stop_requested is set. */
pthread_mutex_unlock(&queue_guard);
stop_speech();
pthread_mutex_lock(&queue_guard);
synth_queue_clear();
stop_requested = 0;
pthread_cond_signal(&stop_acknowledged);
}
while (should_run && queue_peek(synth_queue) && !stop_requested) {
queue_process_entry(s);
}
}
pthread_cond_signal(&stop_acknowledged);
pthread_mutex_unlock(&queue_guard);
return NULL;
}

View file

@ -1,260 +0,0 @@
/*
* espeakup - interface which allows speakup to use espeak-ng
*
* Copyright (C) 2008 William Hubbs
*
* 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 <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/file.h>
#include <time.h>
#include <unistd.h>
#include "espeakup.h"
// path to our pid file
char *pidPath = "/var/run/espeakup.pid";
int debug = 0;
enum espeakup_mode_t espeakup_mode = ESPEAKUP_MODE_SPEAKUP;
struct queue_t *synth_queue = NULL;
int self_pipe_fds[2];
volatile int should_run = 1;
espeak_AUDIO_OUTPUT audio_mode;
pthread_cond_t runner_awake = PTHREAD_COND_INITIALIZER;
/* Initialized in main: use the monotonic clock for timed waits. */
pthread_cond_t wake_stop;
pthread_cond_t stop_acknowledged;
pthread_mutex_t queue_guard = PTHREAD_MUTEX_INITIALIZER;
int espeakup_start_daemon(void)
{
int fds[2];
pid_t pid;
char c;
if (pipe(fds) < 0) {
perror("pipe");
exit(1);
}
pid = fork();
if (pid < 0) {
perror("fork");
exit(1);
}
if (pid) {
// Parent, just wait for daemon
if (read(fds[0], &c, 1) < 0) {
printf("Espeakup is already running!\n");
exit(1);
}
exit(c);
}
// Child, create new session
setsid();
pid = fork();
if (pid)
// Intermediate child, just exit
exit(0);
// Child
if (chdir("/") < 0) {
c = 1;
(void) write(fds[1], &c, 1);
exit(1);
}
return fds[1];
}
int espeakup_is_running(void)
{
int pidFile;
int n;
char s[16];
pid_t pid;
pidFile = open(pidPath, O_RDWR | O_CREAT, 0666);
if (pidFile < 0) {
printf("Can not work with the pid file %s: %s\n", pidPath,
strerror(errno));
return -1;
}
if (flock(pidFile, LOCK_EX) < 0) {
printf("Can not lock the pid file %s: %s\n", pidPath, strerror(errno));
goto error;
}
n = read(pidFile, s, sizeof(s) - 1);
if (n < 0) {
printf("Can not read the pid file %s: %s\n", pidPath, strerror(errno));
goto error;
}
s[n] = 0;
n = sscanf(s, "%d", &pid);
if (n == 1 && (!kill(pid, 0) || errno != ESRCH)) {
// Already running
close(pidFile);
return 1;
}
if (ftruncate(pidFile, 0) < 0) {
printf("Could not truncate the pid file %s: %s\n", pidPath,
strerror(errno));
goto error;
}
lseek(pidFile, 0, SEEK_SET);
n = snprintf(s, sizeof(s), "%d", getpid());
if (write(pidFile, s, n) < 0) {
printf("Could not write to the pid file %s: %s\n", pidPath,
strerror(errno));
goto error;
}
close(pidFile);
return 0;
error:
close(pidFile);
return -1;
}
int main(int argc, char **argv)
{
int fd, devnull;
char ret = 0;
sigset_t sigset;
int err;
pthread_t signal_thread_id;
pthread_t espeak_thread_id;
pthread_t softsynth_thread_id;
struct synth_t s = {
.voice = "",
};
pthread_condattr_t monotonic_attr;
/* Condition variables used with pthread_cond_timedwait must use the
* monotonic clock, so that wall-clock adjustments (NTP, an
* installer setting the system time) cannot make the timeouts fire
* too early or far too late. */
pthread_condattr_init(&monotonic_attr);
pthread_condattr_setclock(&monotonic_attr, CLOCK_MONOTONIC);
pthread_cond_init(&wake_stop, &monotonic_attr);
pthread_cond_init(&stop_acknowledged, &monotonic_attr);
pthread_condattr_destroy(&monotonic_attr);
synth_queue = new_queue();
if (!synth_queue) {
fprintf(stderr, "Unable to allocate memory.\n");
return 2;
}
// set up the pipe used to wake the espeak thread
if (pipe(self_pipe_fds) < 0) {
perror("Unable to create pipe");
return 5;
}
// process command line options
process_cli(argc, argv);
if (!debug && espeakup_mode == ESPEAKUP_MODE_SPEAKUP) {
fd = espeakup_start_daemon();
if (espeakup_is_running()) {
printf("Espeakup is already running!\n");
ret = 1;
goto out;
}
devnull = open("/dev/null", O_RDWR);
dup2(devnull, STDIN_FILENO);
dup2(devnull, STDOUT_FILENO);
dup2(devnull, STDERR_FILENO);
if (devnull > 2)
close(devnull);
}
// create the signal processing thread here.
err = pthread_create(&signal_thread_id, NULL, signal_thread, NULL);
if (err != 0) {
ret = 4;
goto out;
}
/*
* Set up the signal mask which will be the default for all threads.
* We are handling sigint and sigterm, so block them.
*/
sigemptyset(&sigset);
sigaddset(&sigset, SIGINT);
sigaddset(&sigset, SIGTERM);
sigprocmask(SIG_BLOCK, &sigset, NULL);
// Initialize espeak
if (initialize_espeak(&s) < 0) {
ret = 2;
goto out;
}
// open the softsynth
if (open_softsynth() < 0) {
ret = 2;
goto out;
}
// Spawn our softsynth thread.
err = pthread_create(&softsynth_thread_id, NULL, softsynth_thread, &s);
if (err != 0) {
ret = 4;
goto out;
}
// Spawn our espeak-interacting thread.
err = pthread_create(&espeak_thread_id, NULL, espeak_thread, &s);
if (err != 0) {
ret = 4;
goto out;
}
if (!debug && espeakup_mode == ESPEAKUP_MODE_SPEAKUP)
(void) write(fd, &ret, 1);
// wait for the threads to shut down.
pthread_join(signal_thread_id, NULL);
pthread_join(softsynth_thread_id, NULL);
pthread_join(espeak_thread_id, NULL);
if (!paused_espeak)
espeak_Terminate();
close_softsynth();
out:
if (!debug && espeakup_mode == ESPEAKUP_MODE_SPEAKUP) {
if (ret != 1)
unlink(pidPath);
if (ret != 0)
(void) write(fd, &ret, 1);
// If ret was 0, the status byte was written before joining the threads.
}
return ret;
}

View file

@ -1,106 +0,0 @@
/*
* espeakup - interface which allows speakup to use espeak-ng
*
* Copyright (C) 2008 William Hubbs
*
* 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 __ESPEAKUP_H
#define __ESPEAKUP_H
// This was added for gcc 4.3
#include <pthread.h>
#include <stddef.h>
#include <espeak-ng/speak_lib.h>
#include "queue.h"
#define PACKAGE_BUGREPORT "https://github.com/linux-speakup/espeakup/issues"
enum espeakup_mode_t
{
ESPEAKUP_MODE_SPEAKUP,
ESPEAKUP_MODE_ACSINT
};
enum command_t
{
CMD_SET_FREQUENCY,
CMD_SET_MARK,
CMD_SET_PITCH,
CMD_SET_RANGE,
CMD_SET_PUNCTUATION,
CMD_SET_RATE,
CMD_SET_VOICE,
CMD_SET_VOLUME,
CMD_SPEAK_TEXT,
CMD_FLUSH,
CMD_PAUSE,
CMD_UNKNOWN,
};
enum adjust_t
{
ADJ_DEC,
ADJ_SET,
ADJ_INC,
};
struct espeak_entry_t {
enum command_t cmd;
enum adjust_t adjust;
int value;
char *buf;
int len;
};
struct synth_t {
int frequency;
int pitch;
int range;
int punct;
int rate;
char voice[20];
int volume;
char *buf;
int len;
};
extern struct queue_t *synth_queue;
extern int debug;
extern enum espeakup_mode_t espeakup_mode;
extern void process_cli(int argc, char **argv);
extern void *signal_thread(void *arg);
extern int initialize_espeak(struct synth_t *s);
extern void *espeak_thread(void *arg);
extern int open_softsynth(void);
extern void close_softsynth(void);
extern void *softsynth_thread(void *arg);
extern void softsynth_reportindex(int index);
extern volatile int should_run;
extern volatile int stop_requested;
extern int paused_espeak;
extern int self_pipe_fds[2];
#define PIPE_READ_FD (self_pipe_fds[0])
#define PIPE_WRITE_FD (self_pipe_fds[1])
extern pthread_cond_t runner_awake;
extern pthread_cond_t wake_stop;
extern pthread_cond_t stop_acknowledged;
extern pthread_mutex_t queue_guard;
#endif

View file

@ -1,10 +0,0 @@
espeakup_sources = files([
'cli.c',
'espeak.c',
'espeakup.c',
'queue.c',
'signal.c',
'softsynth.c',
'stringhandling.c'
])
espeakup_version = vcs_tag(input : 'version.h.in', output : 'version.h')

View file

@ -1,88 +0,0 @@
/*
* espeakup - interface which allows speakup to use espeak-ng
*
* Note that these functions are meant to be used in either a single or
* multi-threaded environment, so they know nothing about mutexes, etc.
* Handling this is up to the caller.
*
* Copyright (C) 2008 William Hubbs
*
* 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 <assert.h>
#include <stdlib.h>
#include "stringhandling.h"
struct queue_entry_t {
void *data;
struct queue_entry_t *next;
};
struct queue_t {
struct queue_entry_t *head;
struct queue_entry_t *tail;
};
struct queue_t *new_queue(void)
{
struct queue_t *q = allocMem(sizeof(struct queue_t));
q->head = NULL;
q->tail = NULL;
return q;
}
int queue_add(struct queue_t *q, void *data)
{
struct queue_entry_t *tmp;
assert(data);
tmp = allocMem(sizeof(struct queue_entry_t));
tmp->data = data;
tmp->next = NULL;
if (!q->tail) {
q->tail = tmp;
} else {
q->tail->next = tmp;
q->tail = q->tail->next;
}
if (!q->head)
q->head = tmp;
return 1;
}
void *queue_remove(struct queue_t *q)
{
void *data = NULL;
struct queue_entry_t *tmp;
if (q->head) {
tmp = q->head;
data = tmp->data;
q->head = tmp->next;
free(tmp);
if (!q->head)
q->tail = q->head;
}
return data;
}
void *queue_peek(struct queue_t *q)
{
if (q->head)
return q->head->data;
else
return NULL;
}

View file

@ -1,30 +0,0 @@
/*
* espeakup - interface which allows speakup to use espeak-ng
*
* Copyright (C) 2008 William Hubbs
*
* 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 __QUEUE_H
#define __QUEUE_H
struct queue_t; // An opaque type.
extern struct queue_t *new_queue(void);
extern int queue_add(struct queue_t *q, void *entry);
extern void *queue_remove(struct queue_t *q);
extern void *queue_peek(struct queue_t *q);
#endif

View file

@ -1,81 +0,0 @@
/*
* espeakup - interface which allows speakup to use espeak-ng
*
* Copyright (C) 2008 William Hubbs
*
* 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 <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "espeakup.h"
#define STOP_MSG "s"
/*
* We install a dummy signal handler to let the o/s know that we
* do not want the default action to be performed since we are
* handling the signal.
*/
static void dummy_handler(int sig)
{
}
void *signal_thread(void *arg)
{
struct sigaction temp;
sigset_t sigset;
int sig;
memset(&temp, 0, sizeof(struct sigaction));
// install dummy handlers for the signals we want to process
temp.sa_handler = dummy_handler;
sigemptyset(&temp.sa_mask);
sigaction(SIGINT, &temp, NULL);
sigaction(SIGTERM, &temp, NULL);
pthread_mutex_lock(&queue_guard);
while (should_run) {
pthread_mutex_unlock(&queue_guard);
sigfillset(&sigset);
sigwait(&sigset, &sig);
switch (sig) {
case SIGINT:
case SIGTERM:
pthread_mutex_lock(&queue_guard);
should_run = 0;
/* Wake up any thread waiting on a condition variable so
* that it notices the shutdown request: the softsynth
* thread may be waiting for a stop acknowledgement, and
* the espeak thread may be waiting for work or throttling
* before a retry. */
pthread_cond_broadcast(&runner_awake);
pthread_cond_broadcast(&wake_stop);
pthread_cond_broadcast(&stop_acknowledged);
pthread_mutex_unlock(&queue_guard);
break;
default:
printf("espeakup caught signal %d\n", sig);
break;
}
pthread_mutex_lock(&queue_guard);
}
pthread_mutex_unlock(&queue_guard);
// Tell the reader to stop, if it is in a select() call.
write(PIPE_WRITE_FD, STOP_MSG, strlen(STOP_MSG));
return NULL;
}

View file

@ -1,370 +0,0 @@
/*
* espeakup - interface which allows speakup to use espeak-ng
*
* Copyright (C) 2008 William Hubbs
*
* 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 <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <time.h>
#include <unistd.h>
#include "espeakup.h"
#include "stringhandling.h"
// max buffer size
static const size_t maxBufferSize = 16 * 1024 + 1;
// synth flush character
static const int synthFlushChar = 0x18;
static int softFD = 0;
// Text accumulator:
char *textAccumulator;
int textAccumulator_l;
static void queue_add_cmd(enum command_t cmd, enum adjust_t adj, int value)
{
struct espeak_entry_t *entry;
int added = 0;
entry = allocMem(sizeof(struct espeak_entry_t));
entry->cmd = cmd;
entry->adjust = adj;
entry->value = value;
pthread_mutex_lock(&queue_guard);
added = queue_add(synth_queue, (void *) entry);
if (!added)
free(entry);
else
pthread_cond_signal(&runner_awake);
pthread_mutex_unlock(&queue_guard);
}
static void queue_add_text(char *txt, size_t length)
{
struct espeak_entry_t *entry;
int added = 0;
entry = allocMem(sizeof(struct espeak_entry_t));
entry->cmd = CMD_SPEAK_TEXT;
entry->adjust = ADJ_SET;
entry->buf = strdup(txt);
if (!entry->buf) {
perror("unable to allocate space for text");
free(entry);
return;
}
entry->len = length;
pthread_mutex_lock(&queue_guard);
added = queue_add(synth_queue, (void *) entry);
if (!added) {
free(entry->buf);
free(entry);
} else {
pthread_cond_signal(&runner_awake);
}
pthread_mutex_unlock(&queue_guard);
}
static int process_command(struct synth_t *s, char *buf, int start)
{
char *cp;
int value;
enum adjust_t adj;
enum command_t cmd;
cp = buf + start;
switch (*cp) {
case 1:
cp++;
switch (*cp) {
case '+':
adj = ADJ_INC;
cp++;
break;
case '-':
adj = ADJ_DEC;
cp++;
break;
default:
adj = ADJ_SET;
break;
}
value = 0;
while (isdigit(*cp)) {
value = value * 10 + (*cp - '0');
cp++;
}
switch (*cp) {
case 'b':
cmd = CMD_SET_PUNCTUATION;
break;
case 'f':
cmd = CMD_SET_FREQUENCY;
break;
case 'i':
cmd = CMD_SET_MARK;
break;
case 'p':
cmd = CMD_SET_PITCH;
break;
case 'r':
cmd = CMD_SET_RANGE;
break;
case 's':
cmd = CMD_SET_RATE;
break;
case 'v':
cmd = CMD_SET_VOLUME;
break;
case 'P':
cmd = CMD_PAUSE;
break;
default:
cmd = CMD_UNKNOWN;
break;
}
cp++;
break;
default:
cmd = CMD_UNKNOWN;
cp++;
break;
}
if (cmd != CMD_FLUSH && cmd != CMD_UNKNOWN) {
if (espeakup_mode == ESPEAKUP_MODE_ACSINT && textAccumulator_l != 0) {
queue_add_text(textAccumulator, textAccumulator_l);
free(textAccumulator);
textAccumulator = initString(&textAccumulator_l);
}
queue_add_cmd(cmd, adj, value);
}
return cp - (buf + start);
}
static void process_buffer(struct synth_t *s, char *buf, ssize_t length)
{
int start;
int end;
char txtBuf[maxBufferSize];
size_t txtLen;
start = 0;
end = 0;
while (start < length) {
while ((buf[end] < 0 || buf[end] >= ' ' || buf[end] == '\n') &&
end < length)
end++;
if (end != start) {
txtLen = end - start;
strncpy(txtBuf, buf + start, txtLen);
*(txtBuf + txtLen) = 0;
queue_add_text(txtBuf, txtLen);
}
if (end < length)
start = end = end + process_command(s, buf, end);
else
start = length;
}
}
static void process_buffer_acsint(struct synth_t *s, char *buf, ssize_t length)
{
int start = 0;
int i;
int flushIt = 0;
while (start < length) {
for (i = start; i < length; i++) {
if (buf[i] == '\r' || buf[i] == '\n')
flushIt = 1;
if (buf[i] >= 0 && buf[i] < ' ')
break;
}
if (i > start)
stringAndBytes(&textAccumulator, &textAccumulator_l, buf + start,
i - start);
if (flushIt) {
if (textAccumulator != EMPTYSTRING) {
queue_add_text(textAccumulator, textAccumulator_l);
free(textAccumulator);
textAccumulator = initString(&textAccumulator_l);
}
flushIt = 0;
}
if (i < length)
start = i = i + process_command(s, buf, i);
else
start = length;
}
}
/* How long to wait for the espeak thread to acknowledge a stop request
* before concluding that espeak is wedged beyond in-process recovery. */
static const int stopAckTimeout = 10;
static void request_espeak_stop(void)
{
struct timespec timeout;
int err = 0;
pthread_mutex_lock(&queue_guard);
stop_requested = 1;
pthread_cond_signal(&runner_awake); // Wake runner, if necessary.
pthread_cond_signal(&wake_stop); // Wake runner, if necessary.
clock_gettime(CLOCK_MONOTONIC, &timeout);
timeout.tv_sec += stopAckTimeout;
while (should_run && stop_requested && err != ETIMEDOUT)
// wait for acknowledgement.
err = pthread_cond_timedwait(&stop_acknowledged, &queue_guard,
&timeout);
if (should_run && stop_requested) {
/* The espeak thread is stuck in a call into espeak, most likely
* on a wedged audio device. There is no way to recover from
* within the process: exit so that the init system can respawn
* us in a clean state, rather than staying silent, ignoring
* SIGTERM, and stalling the whole console by not draining
* /dev/softsynth anymore. Use _exit because exit could hang in
* library destructors while the audio device is wedged. */
fprintf(stderr, "espeakup: espeak did not acknowledge a stop "
"request within %d seconds, aborting\n", stopAckTimeout);
_exit(3);
}
pthread_mutex_unlock(&queue_guard);
}
int open_softsynth(void)
{
int rc = 0;
// If we're in acsint mode, we read from stdin. No need to open.
if (espeakup_mode == ESPEAKUP_MODE_ACSINT) {
softFD = STDIN_FILENO;
return 0;
}
// open the softsynth.
softFD = open("/dev/softsynthu", O_RDWR | O_NONBLOCK);
if (softFD < 0 && errno == ENOENT)
// Kernel without unicode support? Try without unicode.
softFD = open("/dev/softsynth", O_RDWR | O_NONBLOCK);
if (softFD < 0) {
perror("Unable to open the /dev/softsynth device");
rc = -1;
}
return rc;
}
void close_softsynth(void)
{
if (softFD)
close(softFD);
}
void *softsynth_thread(void *arg)
{
struct synth_t *s = (struct synth_t *) arg;
fd_set set;
ssize_t length;
char buf[maxBufferSize];
char *cp;
int terminalFD = PIPE_READ_FD;
int greatestFD;
textAccumulator = initString(&textAccumulator_l);
if (terminalFD > softFD)
greatestFD = terminalFD;
else
greatestFD = softFD;
pthread_mutex_lock(&queue_guard);
while (should_run) {
pthread_mutex_unlock(&queue_guard);
FD_ZERO(&set);
FD_SET(softFD, &set);
FD_SET(terminalFD, &set);
if (select(greatestFD + 1, &set, NULL, NULL, NULL) < 0) {
if (errno == EINTR) {
pthread_mutex_lock(&queue_guard);
continue;
}
perror("Select failed");
pthread_mutex_lock(&queue_guard);
break;
}
if (FD_ISSET(terminalFD, &set)) {
pthread_mutex_lock(&queue_guard);
break;
}
if (!FD_ISSET(softFD, &set)) {
pthread_mutex_lock(&queue_guard);
continue;
}
length = read(softFD, buf, maxBufferSize - 1);
if (length < 0) {
if (errno == EAGAIN || errno == EINTR) {
pthread_mutex_lock(&queue_guard);
continue;
}
perror("Read from softsynth failed");
pthread_mutex_lock(&queue_guard);
break;
}
*(buf + length) = 0;
cp = strrchr(buf, synthFlushChar);
if (cp) {
request_espeak_stop();
memmove(buf, cp + 1, strlen(cp + 1) + 1);
length = strlen(buf);
}
if (espeakup_mode == ESPEAKUP_MODE_SPEAKUP)
process_buffer(s, buf, length);
else
process_buffer_acsint(s, buf, length);
pthread_mutex_lock(&queue_guard);
}
pthread_cond_signal(&runner_awake);
pthread_mutex_unlock(&queue_guard);
return NULL;
}
void softsynth_reportindex(int index)
{
if (espeakup_mode == ESPEAKUP_MODE_ACSINT) {
putchar(index);
fflush(stdout);
} else {
char buf[16];
snprintf(buf, sizeof(buf), "%d", index);
if (write(softFD, buf, strlen(buf)) < 0)
perror("Writing index failed");
}
}

View file

@ -1,123 +0,0 @@
/*
* espeakup - interface which allows speakup to use espeak-ng
*
* Copyright (C) 2011 William Hubbs
*
* 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/>.
*/
/*
* These string routines were borrowed from edbrowse, which was
* originally written by Karl Dahlke, and is being currently maintained
* by Christopher Brannon.
* I would like to thank both of them for their work on this software.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *EMPTYSTRING = "";
void *allocMem(size_t n)
{
void *s;
if (!n)
return EMPTYSTRING;
if (!(s = malloc(n))) {
fprintf(stderr, "Out of memory!\n");
exit(1);
}
return s;
}
void *reallocMem(void *p, size_t n)
{
void *s;
if (!n) {
fprintf(stderr, "Trying to reallocate memory with size of 0.\n");
exit(1);
}
if (!p) {
fprintf(stderr, "realloc called with a NULL pointer!\n");
exit(1);
}
if (p == EMPTYSTRING)
return allocMem(n);
if (!(s = realloc(p, n))) {
fprintf(stderr, "Failed to allocate memory.\n");
exit(1);
}
return s;
}
char *dupeString(char *s)
{
char *c;
if (!(c = strdup(s))) {
fprintf(stderr, "Out of memory!\n");
exit(1);
}
return c;
}
char *initString(int *l)
{
*l = 0;
return EMPTYSTRING;
}
void stringAndString(char **s, int *l, const char *t)
{
char *p = *s;
int oldlen, newlen, x;
oldlen = *l;
newlen = oldlen + strlen(t);
*l = newlen;
++newlen; // room for the 0
x = oldlen ^ newlen;
if (x > oldlen) { // must realloc
newlen |= (newlen >> 1);
newlen |= (newlen >> 2);
newlen |= (newlen >> 4);
newlen |= (newlen >> 8);
newlen |= (newlen >> 16);
p = reallocMem(p, newlen);
*s = p;
}
strcpy(p + oldlen, t);
}
void stringAndBytes(char **s, int *l, const char *t, int cnt)
{
char *p = *s;
int oldlen, newlen, x;
oldlen = *l;
newlen = oldlen + cnt;
*l = newlen;
++newlen;
x = oldlen ^ newlen;
if (x > oldlen) { // must realloc
newlen |= (newlen >> 1);
newlen |= (newlen >> 2);
newlen |= (newlen >> 4);
newlen |= (newlen >> 8);
newlen |= (newlen >> 16);
p = reallocMem(p, newlen);
*s = p;
}
memcpy(p + oldlen, t, cnt);
p[oldlen + cnt] = 0;
}

View file

@ -1,34 +0,0 @@
/*
* espeakup - interface which allows speakup to use espeak-ng
*
* Copyright (C) 2011 William Hubbs
*
* 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 __STRINGHANDLING_H
#define __STRINGHANDLING_H
#include <stddef.h>
extern char *EMPTYSTRING;
void *allocMem(size_t n);
void *reallocMem(void *p, size_t n);
char *dupeString(char *s);
char *initString(int *l);
void stringAndString(char **s, int *l, const char *t);
void stringAndBytes(char **s, int *l, const char *t, int cnt);
#endif

View file

@ -1,25 +0,0 @@
/*
* espeakup - interface which allows speakup to use espeak-ng
*
* Copyright (C) 2008 William Hubbs
*
* 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 __VERSION_H
#define __VERSION_H
#define PACKAGE_VERSION "@VCS_TAG@"
#endif

126
synth.c Normal file
View file

@ -0,0 +1,126 @@
/*
* espeakup - interface which allows speakup to use espeak
*
* Copyright (C) 2008 William Hubbs
*
* 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 <string.h>
#include "espeakup.h"
/* multipliers and offsets */
const int frequencyMultiplier = 11;
const int pitchMultiplier = 11;
const int rateMultiplier = 34;
const int rateOffset = 84;
const int volumeMultiplier = 22;
espeak_ERROR set_frequency(struct synth_t * s, int freq, enum adjust_t adj)
{
espeak_ERROR rc;
if (adj == ADJ_DEC)
freq = -freq;
if (adj != ADJ_SET)
freq += s->frequency;
rc = espeak_SetParameter(espeakRANGE, freq * frequencyMultiplier, 0);
if (rc == EE_OK)
s->frequency = freq;
return rc;
}
espeak_ERROR set_pitch(struct synth_t * s, int pitch, enum adjust_t adj)
{
espeak_ERROR rc;
if (adj == ADJ_DEC)
pitch = -pitch;
if (adj != ADJ_SET)
pitch += s->pitch;
rc = espeak_SetParameter(espeakPITCH, pitch * pitchMultiplier, 0);
if (rc == EE_OK)
s->pitch = pitch;
return rc;
}
espeak_ERROR set_punctuation(struct synth_t * s, int punct, enum adjust_t adj)
{
espeak_ERROR rc;
if (adj == ADJ_DEC)
punct = -punct;
if (adj != ADJ_SET)
punct += s->punct;
rc = espeak_SetParameter(espeakPUNCTUATION, punct, 0);
if (rc == EE_OK)
s->punct = punct;
return rc;
}
espeak_ERROR set_rate(struct synth_t * s, int rate, enum adjust_t adj)
{
espeak_ERROR rc;
if (adj == ADJ_DEC)
rate = -rate;
if (adj != ADJ_SET)
rate += s->rate;
rc = espeak_SetParameter(espeakRATE,
rate * rateMultiplier + rateOffset, 0);
if (rc == EE_OK)
s->rate = rate;
return rc;
}
espeak_ERROR set_voice(struct synth_t * s, char *voice)
{
espeak_ERROR rc;
rc = espeak_SetVoiceByName(voice);
if (rc == EE_OK)
strcpy(s->voice, voice);
return rc;
}
espeak_ERROR set_volume(struct synth_t * s, int vol, enum adjust_t adj)
{
espeak_ERROR rc;
if (adj == ADJ_DEC)
vol = -vol;
if (adj != ADJ_SET)
vol += s->volume;
rc = espeak_SetParameter(espeakVOLUME, (vol + 1) * volumeMultiplier,
0);
if (rc == EE_OK)
s->volume = vol;
return rc;
}
espeak_ERROR stop_speech(void)
{
return (espeak_Cancel());
}
espeak_ERROR speak_text(struct synth_t * s)
{
espeak_ERROR rc;
rc = espeak_Synth(s->buf, s->len + 1, 0, POS_CHARACTER, 0, 0, NULL,
NULL);
return rc;
}

18
tarball Executable file
View file

@ -0,0 +1,18 @@
#!/bin/bash
# Makes a tarball release
VER=$(./version)
PREFIX=espeakup-${VER}
REL=${1:-v${VER}}
if [ "$REL" != "v${VER}" ]; then
TIMESTAMP=`git show $REL --pretty=format:%ai |head -1`
PATCHLEVEL=`date --utc -d "$TIMESTAMP" +_p%Y%m%d%H%M`
fi
TARFILE=${PREFIX}${PATCHLEVEL}.tar
git archive --format=tar --prefix=${PREFIX}/ $REL > ${TARFILE}
tar f ${TARFILE} --delete ${PREFIX}/.gitignore --delete ${PREFIX}/tarball
bzip2 ${TARFILE}
echo "Produced ${TARFILE}.bz2"

5
version Executable file
View file

@ -0,0 +1,5 @@
#!/bin/bash
ver=$(grep "const char.*Version" espeakup.c)
ver=${ver%\"*}
ver=${ver#*\"}
echo ${ver}