mirror of
https://github.com/linux-speakup/espeakup.git
synced 2026-08-09 09:19:09 +02:00
Compare commits
209 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f266faaff7 | ||
|
|
c3c7c6a007 | ||
|
|
cc1447b16a | ||
|
|
76a2af5008 | ||
|
|
6835449642 | ||
|
|
71d0e1fa2a | ||
|
|
a504688f3d | ||
|
|
7405455baf | ||
|
|
e92edf96dc | ||
|
|
2b959e874e | ||
|
|
d1f7e2384b | ||
|
|
dc1f1b9efa |
||
|
|
fa848f509e | ||
|
|
98dc103749 | ||
|
|
d7c06a6f9f |
||
|
|
108c28ae08 | ||
|
|
78e561fae9 | ||
|
|
c99bfb8e45 | ||
|
|
ca234f2122 | ||
|
|
316e4fc51d | ||
|
|
e858481c0a | ||
|
|
8f18ad7f88 | ||
|
|
b7282c1a90 | ||
|
|
f077d0c042 | ||
|
|
3794e8e74b | ||
|
|
569e9f5b0e |
||
|
|
c50e4eb088 |
||
|
|
e13ceb3cce | ||
|
|
c528913ad9 | ||
|
|
dd2dcb68bf | ||
|
|
ca1d6b42e2 | ||
|
|
0c09fe5449 |
||
|
|
30ef2145e7 | ||
|
|
2d8d2c55c0 | ||
|
|
c69d98cde6 | ||
|
|
2f3d171863 | ||
|
|
512a958dad | ||
|
|
335d748a63 | ||
|
|
3fb775cc33 | ||
|
|
6689948a8a | ||
|
|
b5c1aef849 | ||
|
|
201ae667ee |
||
|
|
a464461f0e | ||
|
|
9ccabf55b6 | ||
|
|
ee05f278f2 | ||
|
|
70ae4dece7 | ||
|
|
171bb517a0 | ||
|
|
7159441731 |
||
|
|
7cfba0878b |
||
|
|
1c3285d249 |
||
|
|
53665b8eab | ||
|
|
a9657bbb2b | ||
|
|
1e31008091 | ||
|
|
e69d61b2d8 | ||
|
|
5f01999726 | ||
|
|
5339da3144 | ||
|
|
a5b655ddb0 | ||
|
|
b7fe3af320 | ||
|
|
e21b746eb9 | ||
|
|
e55f16b7fd | ||
|
|
2964310b24 | ||
|
|
918e8853cd | ||
|
|
3e5815429b | ||
|
|
8b49d9d211 | ||
|
|
97adab70de | ||
|
|
9290325489 | ||
|
|
ee099174d8 | ||
|
|
c1ad891f2e | ||
|
|
d977243735 | ||
|
|
d95ee07775 | ||
|
|
e84e000b3e | ||
|
|
3fbbdf1922 | ||
|
|
58ed438f00 | ||
|
|
b2bd1d33a8 | ||
|
|
3b4b6d0cbc | ||
|
|
6180ff6e49 | ||
|
|
ba316a4cd4 | ||
|
|
c06f18c454 | ||
|
|
70f74657c2 | ||
|
|
999e6551b5 | ||
|
|
3353241a79 | ||
|
|
49dcacb2ec | ||
|
|
2154d1a231 | ||
|
|
1990e8e25d | ||
|
|
701074fd96 | ||
|
|
7bf2eee07a | ||
|
|
c7ae47dfe5 | ||
|
|
3bfc662bae | ||
|
|
037e642217 | ||
|
|
0d9d7b6141 | ||
|
|
056dcf70fe | ||
|
|
d1630432ba | ||
|
|
8fda956e02 | ||
|
|
4cfcd7ba11 | ||
|
|
1a10788c5f | ||
|
|
edb5e50fcd | ||
|
|
dec561324d | ||
|
|
486fe27f47 | ||
|
|
311b691195 | ||
|
|
ffe397fb91 | ||
|
|
e66311bb99 | ||
|
|
11cc053d82 | ||
|
|
48fa03faf5 | ||
|
|
654fc810fe | ||
|
|
e4e3f0979e | ||
|
|
57547e8efa | ||
|
|
ac9e12414b | ||
|
|
18ebab3247 | ||
|
|
cc7e77eb9d | ||
|
|
32d848cb76 | ||
|
|
4de82bf24c | ||
|
|
739e074072 | ||
|
|
014d27b7aa | ||
|
|
82490a98d2 | ||
|
|
101e14901e | ||
|
|
dc056e6c77 | ||
|
|
0a9a8cce9b | ||
|
|
40479540b4 | ||
|
|
ccb8cea91e | ||
|
|
31ad5f04ca | ||
|
|
011ec27162 | ||
|
|
04d10d88ec | ||
|
|
ceaae3640a | ||
|
|
c9f871f687 | ||
|
|
a82bbd8140 | ||
|
|
58f09983f8 | ||
|
|
0238baa5c2 | ||
|
|
bcd64cb263 | ||
|
|
9b7dcebb6d | ||
|
|
46bf3d99d5 | ||
|
|
87ab6c6ea8 | ||
|
|
4889572ad1 | ||
|
|
a9398bdeb4 | ||
|
|
be879d206b | ||
|
|
c1d33d6738 | ||
|
|
0471ff47f4 | ||
|
|
520bbae365 | ||
|
|
8f3e8f1967 | ||
|
|
e49acbb59a | ||
|
|
24bcdf5666 | ||
|
|
5ec3809c59 | ||
|
|
5ebe506026 | ||
|
|
d5fd5d69b8 | ||
|
|
633743117c | ||
|
|
1750b92cfb | ||
|
|
eb74a3ce17 | ||
|
|
dd00775695 | ||
|
|
6a15f2cccf | ||
|
|
3687f16b09 | ||
|
|
938e10b66a | ||
|
|
975765289b | ||
|
|
b8c7247feb | ||
|
|
1091182f88 | ||
|
|
0bf2cae5a6 | ||
|
|
554a03d26c | ||
|
|
b7f324072f | ||
|
|
a8cad20de8 | ||
|
|
42c3f76a08 | ||
|
|
3e8e7d12ae | ||
|
|
af5717b8cb | ||
|
|
c6b57885c9 | ||
|
|
d82bfcd09c | ||
|
|
0fdca827b8 | ||
|
|
474580b08b | ||
|
|
c5fce64f25 | ||
|
|
a58f93cd74 | ||
|
|
dfc9bbff65 | ||
|
|
9d1cabdd0d | ||
|
|
f58d9984ce | ||
|
|
24e7d667bc | ||
|
|
26ab109bd8 | ||
|
|
b108764b02 | ||
|
|
739d79cff8 | ||
|
|
ec9d8b1ee2 | ||
|
|
a5d1a48f42 | ||
|
|
3bb78df86c | ||
|
|
94e23a3a02 | ||
|
|
a69342fcad | ||
|
|
91b8960b6a | ||
|
|
0ad70b4eaa | ||
|
|
1d02169aa5 | ||
|
|
1c440e5a42 | ||
|
|
08e46c58a8 | ||
|
|
d373fb2aa7 | ||
|
|
b31985f97c | ||
|
|
d7dd0f919d | ||
|
|
2db53856a9 | ||
|
|
55f8ebf98a | ||
|
|
69f1e8554a | ||
|
|
0e47c95015 | ||
|
|
c235a3b063 | ||
|
|
d7c81f5117 | ||
|
|
3ddbb94e37 | ||
|
|
3a8323f98c | ||
|
|
e7d300a183 | ||
|
|
a1510b5e93 | ||
|
|
3dbdcb21cb | ||
|
|
9551ba81d9 | ||
|
|
6366b41bdf | ||
|
|
40f152e11f | ||
|
|
4e7ae23757 | ||
|
|
9ee3fd433c | ||
|
|
e2493db48e | ||
|
|
2351b5d489 | ||
|
|
da15136aa7 | ||
|
|
8dca597f29 | ||
|
|
bbf77d918d | ||
|
|
7db5530308 | ||
|
|
341fa7ba42 |
32 changed files with 2050 additions and 873 deletions
41
.clang-format
Normal file
41
.clang-format
Normal file
|
|
@ -0,0 +1,41 @@
|
||||||
|
---
|
||||||
|
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
3
.gitignore
vendored
|
|
@ -1,2 +1 @@
|
||||||
espeakup
|
build*
|
||||||
*.o
|
|
||||||
|
|
|
||||||
2
.indent.pro
vendored
Normal file
2
.indent.pro
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
-kr
|
||||||
|
-ts4
|
||||||
54
Makefile
54
Makefile
|
|
@ -1,54 +0,0 @@
|
||||||
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
|
|
||||||
|
|
||||||
ifeq ("$(origin TAG)", "command line")
|
|
||||||
TIMESTAMP := _p$(shell date +%Y%m%d)
|
|
||||||
endif
|
|
||||||
|
|
||||||
VERSION := $(shell grep 'Version.*=' espeakup.c | sed 's/.*"\(.*\)";/\1/')
|
|
||||||
TAG := v$(VERSION)
|
|
||||||
TARPREFIX := espeakup-$(VERSION)
|
|
||||||
TARFILE := $(TARPREFIX)$(TIMESTAMP).tar
|
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
tarball:
|
|
||||||
git archive --format=tar --prefix=$(TARPREFIX)/ $(TAG) > $(TARFILE)
|
|
||||||
tar f $(TARFILE) --delete $(TARPREFIX)/.gitignore
|
|
||||||
bzip2 $(TARFILE)
|
|
||||||
|
|
||||||
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
|
|
||||||
62
README
62
README
|
|
@ -1,62 +0,0 @@
|
||||||
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:
|
|
||||||
|
|
||||||
--debug, -d Debug mode (stay in the foreground)
|
|
||||||
--help, -h Show 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.
|
|
||||||
|
|
||||||
48
README.md
Normal file
48
README.md
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
# 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
11
ToDo
|
|
@ -1,11 +0,0 @@
|
||||||
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.
|
|
||||||
|
|
||||||
77
doc/espeakup.8.ronn
Normal file
77
doc/espeakup.8.ronn
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
<!-- 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)
|
||||||
8
doc/meson.build
Normal file
8
doc/meson.build
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
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
|
||||||
66
espeakup.8
66
espeakup.8
|
|
@ -1,66 +0,0 @@
|
||||||
.\" 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.4"
|
|
||||||
.nh
|
|
||||||
.SH NAME
|
|
||||||
espeakup \(em connect Speakup to the ESpeak TTS engine
|
|
||||||
.SH SYNOPSIS
|
|
||||||
.B espeakup
|
|
||||||
[
|
|
||||||
.B \-\^\-debug
|
|
||||||
]
|
|
||||||
[
|
|
||||||
.B \-\^\-help
|
|
||||||
]
|
|
||||||
[
|
|
||||||
.B \-\^\-version
|
|
||||||
]
|
|
||||||
.SH OPTIONS
|
|
||||||
.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>.
|
|
||||||
135
espeakup.c
135
espeakup.c
|
|
@ -1,135 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.5";
|
|
||||||
|
|
||||||
/* 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;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
if (!debug) {
|
|
||||||
if (espeakup_is_running()) {
|
|
||||||
printf("Espeakup is already running!\n");
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 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 */
|
|
||||||
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
82
espeakup.h
|
|
@ -1,82 +0,0 @@
|
||||||
/*
|
|
||||||
* 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
|
|
||||||
25
meson.build
Normal file
25
meson.build
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
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)
|
||||||
4
meson_options.txt
Normal file
4
meson_options.txt
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
option('man', type : 'feature', value : 'auto',
|
||||||
|
description : 'build manpage with ronn')
|
||||||
|
option('systemd', type : 'feature', value : 'auto',
|
||||||
|
description :'enable systemd support')
|
||||||
142
queue.c
142
queue.c
|
|
@ -1,142 +0,0 @@
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
if (error == EE_OK)
|
|
||||||
queue_remove();
|
|
||||||
}
|
|
||||||
4
services/meson.build
Normal file
4
services/meson.build
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
systemd = dependency('systemd')
|
||||||
|
if (systemd.found() and get_option('systemd').allowed()) or get_option('systemd').enabled()
|
||||||
|
subdir('systemd')
|
||||||
|
endif
|
||||||
18
services/systemd/espeakup.service.in
Normal file
18
services/systemd/espeakup.service.in
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
[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
|
||||||
20
services/systemd/meson.build
Normal file
20
services/systemd/meson.build
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
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
|
||||||
|
)
|
||||||
182
softsynth.c
182
softsynth.c
|
|
@ -1,182 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 <string.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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* espeakup - interface which allows speakup to use espeak
|
* espeakup - interface which allows speakup to use espeak-ng
|
||||||
*
|
*
|
||||||
* Copyright (C) 2008 William Hubbs
|
* Copyright (C) 2008 William Hubbs
|
||||||
*
|
*
|
||||||
|
|
@ -20,37 +20,53 @@
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "espeakup.h"
|
#include "espeakup.h"
|
||||||
|
#include "stringhandling.h"
|
||||||
|
#include "version.h"
|
||||||
|
|
||||||
/* program version */
|
/* pid path */
|
||||||
extern const char *Version;
|
extern char *pidPath;
|
||||||
|
|
||||||
|
/* default voice */
|
||||||
|
extern char *defaultVoice;
|
||||||
|
|
||||||
|
/* Whether to drive ALSA volume */
|
||||||
|
extern int alsaVolume;
|
||||||
|
|
||||||
/* command line options */
|
/* command line options */
|
||||||
const char *shortOptions = "dhv";
|
const char *shortOptions = "P:V:adhv";
|
||||||
const struct option longOptions[] = {
|
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'},
|
{"debug", no_argument, NULL, 'd'},
|
||||||
{"help", no_argument, NULL, 'h'},
|
{"help", no_argument, NULL, 'h'},
|
||||||
{"version", no_argument, NULL, 'v'},
|
{"version", no_argument, NULL, 'v'},
|
||||||
{0, 0, 0, 0}
|
{0, 0, 0, 0}};
|
||||||
};
|
|
||||||
|
|
||||||
static void show_help()
|
static void show_help()
|
||||||
{
|
{
|
||||||
printf("Usage: espeakup [options]\n\n");
|
printf("Usage: espeakup [options]\n\n");
|
||||||
printf("Options are as follows:\n");
|
printf("Options are as follows:\n");
|
||||||
printf(" --debug, -d\tDebug mode (stay in the foreground)\n");
|
printf(" --pid-path=path, -P path\t\tSet path for pid file.\n");
|
||||||
printf(" --help, -h\tShow this help\n");
|
printf(" --default-voice=voice, -V voice\tSet default voice.\n");
|
||||||
printf(" --version, -v\tDisplay the software version.\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");
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void show_version(void)
|
static void show_version(void)
|
||||||
{
|
{
|
||||||
printf("espeakup %s\n", Version);
|
printf("ESpeakup %s\n", PACKAGE_VERSION);
|
||||||
printf("Copyright (C) 2008 William Hubbs\n");
|
printf("Copyright (C) 2008 William Hubbs <w.d.hubbs@gmail.com>\n");
|
||||||
printf("License GPLv3+: GNU GPL version 3 or later\n");
|
printf("License GPLv3+: GNU GPL version 3 or later\n");
|
||||||
printf("You are free to change and redistribute this software.\n");
|
printf("You are free to change and redistribute this software.\n");
|
||||||
|
printf("Please report bugs to %s\n", PACKAGE_BUGREPORT);
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,6 +77,15 @@ void process_cli(int argc, char **argv)
|
||||||
do {
|
do {
|
||||||
opt = getopt_long(argc, argv, shortOptions, longOptions, NULL);
|
opt = getopt_long(argc, argv, shortOptions, longOptions, NULL);
|
||||||
switch (opt) {
|
switch (opt) {
|
||||||
|
case 'P':
|
||||||
|
pidPath = dupeString(optarg);
|
||||||
|
break;
|
||||||
|
case 'V':
|
||||||
|
defaultVoice = dupeString(optarg);
|
||||||
|
break;
|
||||||
|
case 'a':
|
||||||
|
espeakup_mode = ESPEAKUP_MODE_ACSINT;
|
||||||
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
debug = 1;
|
debug = 1;
|
||||||
break;
|
break;
|
||||||
|
|
@ -71,6 +96,7 @@ void process_cli(int argc, char **argv)
|
||||||
show_version();
|
show_version();
|
||||||
break;
|
break;
|
||||||
case -1:
|
case -1:
|
||||||
|
case 0:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
show_help();
|
show_help();
|
||||||
638
src/espeak.c
Normal file
638
src/espeak.c
Normal file
|
|
@ -0,0 +1,638 @@
|
||||||
|
/*
|
||||||
|
* 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\"> </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 = "<"; break;
|
||||||
|
case '>': entity = ">"; break;
|
||||||
|
case '&': entity = "&"; break;
|
||||||
|
case '\'': entity = "'"; break;
|
||||||
|
case '"': entity = """; 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;
|
||||||
|
}
|
||||||
260
src/espeakup.c
Normal file
260
src/espeakup.c
Normal file
|
|
@ -0,0 +1,260 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
106
src/espeakup.h
Normal file
106
src/espeakup.h
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
10
src/meson.build
Normal file
10
src/meson.build
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
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')
|
||||||
88
src/queue.c
Normal file
88
src/queue.c
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
30
src/queue.h
Normal file
30
src/queue.h
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
81
src/signal.c
Normal file
81
src/signal.c
Normal file
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
370
src/softsynth.c
Normal file
370
src/softsynth.c
Normal file
|
|
@ -0,0 +1,370 @@
|
||||||
|
/*
|
||||||
|
* 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");
|
||||||
|
}
|
||||||
|
}
|
||||||
123
src/stringhandling.c
Normal file
123
src/stringhandling.c
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
34
src/stringhandling.h
Normal file
34
src/stringhandling.h
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
25
src/version.h.in
Normal file
25
src/version.h.in
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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
126
synth.c
|
|
@ -1,126 +0,0 @@
|
||||||
/*
|
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue