Commit graph

287 commits

Author SHA1 Message Date
Achill Gilgenast
c3c7c6a007 services: add fallback systemd unitdir if systemd is not available
Allows building the service files without a systemd dependency by
defining a default systemd user unit directory.

Relevant in Alpine, where we can package the service files in a
subpackage without having systemd in Alpine.

Addresses feedback of https://github.com/linux-speakup/espeakup/pull/64,
therefore superseeds it.
2026-06-29 18:55:16 +02:00
Alexander Epaneshnikov
cc1447b16a fixup! espeak: detect a wedged engine, restart it, and eventually give up 2026-06-29 10:29:36 +02:00
Alexander Epaneshnikov
76a2af5008 fixup! espeak: back off before retrying after any espeak error 2026-06-29 10:29:36 +02:00
Alexander Epaneshnikov
6835449642 fixup! softsynth: bound the wait for espeak to acknowledge a stop request 2026-06-29 10:29:36 +02:00
Alexander Epaneshnikov
71d0e1fa2a espeak: detect a wedged engine, restart it, and eventually give up
Issue #62 reports espeakup going permanently silent after libespeak-ng
prints "error: Device or resource busy" (EBUSY from the ALSA device,
via pcaudiolib).  Once the audio output is wedged, espeak's internal
command queue never drains, every espeak_Synth call fails with
EE_BUFFER_FULL forever, and espeakup just kept retrying silently.

EE_BUFFER_FULL is also perfectly normal while a long backlog is being
played back, so persistent failure alone is not a reliable signal.  To
tell a backlogged engine from a wedged one, note progress whenever the
synth callback fires (it is invoked for every chunk espeak
synthesizes, and synthesis is paced by audio playback): if entries
keep failing for ~10 seconds with no callback activity at all, declare
the engine wedged.

Recovery is layered:
- restart the engine in-process (espeak_Cancel + espeak_Terminate +
  reinitialize), which recovers transient device problems;
- if the engine has not been healthy for at least a minute between
  such restarts, after 3 restarts give up and exit, letting the init
  system (Restart=always in our systemd unit) respawn espeakup in a
  completely clean state;
- if the restart itself blocks on the wedged device, the
  stop-acknowledgement timeout in the softsynth thread eventually
  terminates the process as a last resort.

A quick espeak_Synth success right after a restart does not count as
healthy on purpose: espeak's freshly emptied internal queue accepts
entries even while the device is still wedged.

Helps: https://github.com/linux-speakup/espeakup/issues/45
Helps: https://github.com/linux-speakup/espeakup/issues/62

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 10:29:36 +02:00
Alexander Epaneshnikov
a504688f3d espeak: do not call espeak functions after a failed reinitialization
When resuming from CMD_PAUSE, queue_process_entry ignored the result
of reinitialize_espeak: if espeak_Initialize failed, paused_espeak
remained set, yet the entry was processed anyway, calling
espeak_Synth & co on a terminated engine.  Combined with the
busy-retry loop, this produced an endless stream of failing calls
against a dead engine.

Make reinitialize_espeak report failure, and when espeak is
unavailable, leave the entry queued and back off before trying to
reinitialize again.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 10:29:36 +02:00
Alexander Epaneshnikov
7405455baf espeak: back off before retrying after any espeak error
When processing an entry failed, only EE_BUFFER_FULL throttled before
the retry; any other persistent error (e.g. EE_INTERNAL_ERROR after
espeak was terminated) made queue_process_entry retry the same entry
in a tight loop with no sleep, burning a whole CPU while printing to a
stderr that points to /dev/null in daemon mode.

Factor the one-second throttle out into espeak_wait_retry() and apply
it to every failed entry.  The wake_stop condition variable still
interrupts the wait immediately when a flush comes in.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 10:29:36 +02:00
Alexander Epaneshnikov
e92edf96dc softsynth: bound the wait for espeak to acknowledge a stop request
request_espeak_stop() waited forever for the espeak thread to
acknowledge the stop.  If espeak-ng is wedged inside the audio output
(e.g. an ALSA device blocked or stuck returning EBUSY, as reported in
issue #62), the acknowledgement never comes, and the softsynth thread
stops draining /dev/softsynth forever.  Speakup's kernel buffer then
fills up and console output stalls, which matches the "blocks dmesg
output after a couple of pages" observation in issue #45.  The process
goes silent and only SIGKILL gets rid of it.

Wait at most 10 seconds for the acknowledgement (a normal cancellation
takes milliseconds; the timeout can only trigger when espeak is truly
stuck).  On timeout, exit with a clear message so that the init system
respawns espeakup in a clean state: our systemd unit already has
Restart=always.  A one-second restart beats an unkillable silent
daemon, and was explicitly requested by the reporter of issue #62.

Helps: https://github.com/linux-speakup/espeakup/issues/45
Helps: https://github.com/linux-speakup/espeakup/issues/62

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 10:29:36 +02:00
Alexander Epaneshnikov
2b959e874e signal: wake all condition-variable waiters on shutdown
On SIGINT/SIGTERM, the signal thread only set should_run to 0 and
relied on a wake-up chain to propagate the shutdown: the self-pipe
wakes the softsynth thread out of select(), which on exit signals
runner_awake to wake the espeak thread.

That chain breaks whenever the softsynth thread is not sitting in
select() but waiting on stop_acknowledged in request_espeak_stop():
nobody ever signals that condition variable on shutdown, so the thread
never re-evaluates should_run and the process never exits.

Broadcast all three condition variables after clearing should_run, so
that every parked thread re-checks its predicate, whichever wait it is
blocked in.

Helps: https://github.com/linux-speakup/espeakup/issues/45
Helps: https://github.com/linux-speakup/espeakup/issues/62

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 10:29:36 +02:00
Alexander Epaneshnikov
d1f7e2384b espeak: do not call espeak_Cancel() while holding queue_guard
When a flush is requested, the espeak thread called stop_speech() ->
espeak_Cancel() with queue_guard held.  espeak_Cancel() waits for
espeak-ng's internal say thread to acknowledge the cancellation, and
that thread can be blocked indefinitely inside a blocking ALSA call
(snd_pcm_writei/snd_pcm_drain on a wedged device, as seen with EBUSY
errors).  In that case queue_guard was held forever, which in turn:

- blocked the signal thread on pthread_mutex_lock(), so SIGINT/SIGTERM
  appeared to be ignored and only SIGKILL could end the process;
- left the softsynth thread stuck in request_espeak_stop(), so
  /dev/softsynth was no longer drained, speakup's kernel buffer filled
  up, and console output (e.g. dmesg) stalled.

Release queue_guard around the espeak_Cancel() call.  This is safe
because the only queue producer, the softsynth thread, is blocked
waiting for stop_acknowledged for as long as stop_requested is set, so
the queue cannot be mutated concurrently.

Helps: https://github.com/linux-speakup/espeakup/issues/45
Helps: https://github.com/linux-speakup/espeakup/issues/62

Co-Authored-By: Claude <noreply@anthropic.com>
2026-06-29 10:29:36 +02:00
Copilot
dc1f1b9efa
Fix deprecated Meson setup command warning in README (#61)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: alex19EP <4889846+alex19EP@users.noreply.github.com>
2025-08-22 14:12:32 +03:00
Samuel Thibault
fa848f509e set_punctuation: Fix punctuation levels values
They do not actually follow the espeak values (and have no reason to,
anyway).
2025-05-29 01:00:18 +03:00
Samuel Thibault
98dc103749 queue_process_entry: Avoid leaving error uninitialized
CMD_PAUSE was not actually setting error to EE_OK, and the default case,
even if it is not supposed to happen, should set error to something sane.
2025-05-29 00:59:51 +03:00
Alexander Epaneshnikov
d7c06a6f9f
meson: switch from deprecated functions (#49) 2022-03-11 14:51:28 +01:00
Samuel Thibault
108c28ae08 Throttle on EE_BUFFER_FULL errors
When espeak returns EE_BUFFER_FULL we should just wait a bit before
retrying. Also, we don't want to lose the current entry. We however want
to wake up as soon as possible on stop request.
2022-03-03 05:53:04 +03:00
Brandon McGinty
78e561fae9 use correct case for --pid-file option
This permits --pid-file to be specified.
The getopt_long option was looking for a lowercase p,
and the pid-file option used an uppercase P.
This meant that pid-file was never settable.
2022-02-14 16:14:23 +03:00
Samuel Thibault
c99bfb8e45 Request systemd to protect espeakup from OOM
Otherwise it might get killed on memory pressure.
2022-02-10 18:37:23 -06:00
Samuel Thibault
ca234f2122 Request systemd to prioritize espeakup
So speech doesn't get choppy on a loaded system.

As suggested by Nick Gawronski.

This fixes #46.
2022-02-10 18:30:08 -06:00
Samuel Thibault
316e4fc51d softsynth: on error, be clear we are talking about /dev/softsynth 2021-08-21 16:30:32 +03:00
William Hubbs
e858481c0a use dupeString in command line processing 2021-07-07 22:16:25 -05:00
William Hubbs
8f18ad7f88 add dupeString wrapper function
This function calls strdup and exits on failure.
2021-07-07 22:16:25 -05:00
William Hubbs
b7282c1a90 use systemd modprobe service for speakup_soft 2021-07-03 23:37:00 +03:00
William Hubbs
f077d0c042 format build files consistently 2021-07-03 03:53:58 +03:00
Christopher Brannon
3794e8e74b Update my email address in the docs. 2021-06-20 15:58:20 +03:00
Alexander Epaneshnikov
569e9f5b0e
bump version v0.90 2021-06-19 10:26:15 +03:00
Alexander Epaneshnikov
c50e4eb088
remove command line from README (#33)
now when we have man documentation in markdown, no need to store the same
information in two places.
2021-06-19 10:05:57 +03:00
Alexander Epaneshnikov
e13ceb3cce edit by hand
more formatting improvements
2021-06-19 01:57:49 -05:00
Alexander Epaneshnikov
c528913ad9 format code 2021-06-19 01:57:49 -05:00
Alexander Epaneshnikov
dd2dcb68bf use clang-format for code styling 2021-06-19 01:57:49 -05:00
Alexander Epaneshnikov
ca1d6b42e2 generate man page from markdown
Use ronn to convert markdown to a man page
This simplifies maintaining the documentation.

This fixes #31.
2021-06-19 01:34:18 -05:00
Alexander Epaneshnikov
0c09fe5449
switch to meson (#30)
switch to a meson-based build system
2021-06-18 21:53:04 -05:00
William Hubbs
30ef2145e7 remove TODO
This file is no longer needed since we have an official bug tracker.
2021-06-15 09:19:43 +03:00
William Hubbs
2d8d2c55c0 rename autostart directory to services 2021-06-15 09:19:18 +03:00
Alexander Epaneshnikov
c69d98cde6 clean makefile
get rid of unnecessary targets. add warning options.
2021-06-15 06:08:50 +03:00
Alexander Epaneshnikov
2f3d171863 update issue url 2021-06-15 06:08:50 +03:00
Alexander Epaneshnikov
512a958dad remove changelog
we can do better than that.
2021-06-15 06:08:50 +03:00
Alexander Epaneshnikov
335d748a63 new src structure
to make it more understandable and convenient for further improvements.
2021-06-15 06:08:50 +03:00
Alexander Epaneshnikov
3fb775cc33 load speakup_soft kernel module 2021-06-14 22:14:47 +03:00
Alexander Epaneshnikov
6689948a8a add espeakup manual in unit file 2021-06-14 22:14:47 +03:00
Alexander Epaneshnikov
b5c1aef849 add systemd unit 2021-06-14 22:14:47 +03:00
Alexander Epaneshnikov
201ae667ee
link with espeak-ng by default (#25) 2021-06-14 20:45:53 +02:00
William Hubbs
a464461f0e convert README to markdown 2021-06-14 21:12:33 +03:00
William Hubbs
9ccabf55b6 update README
- Remove the unnecessary references to tarballs since this is now a
  standard github project.

- Point people to the bug tracker for filing bugs.
2021-06-14 21:12:33 +03:00
Samuel Thibault
ee05f278f2 Add support for indexing 2021-06-14 20:47:04 +03:00
Alexander Epaneshnikov
70ae4dece7 enlaarge voice buf
this will fix #9
2021-06-14 20:41:42 +03:00
Samuel Thibault
171bb517a0 Let espeak choose the buffer size
The default is already quite small, and choosing a too small one may
lead to broken speech.

Fixes #13
2021-06-14 20:37:53 +03:00
Alexander Epaneshnikov
7159441731
Merge pull request #19 from sthibaul/alsa-volume
Support setting ALSA volume in addition to espeak volume
2021-06-14 20:09:19 +03:00
Alexander Epaneshnikov
7cfba0878b
Merge pull request #18 from sthibaul/range
Support pitch range configuration
2021-06-14 20:01:52 +03:00
Alexander Epaneshnikov
1c3285d249
Merge pull request #15 from sthibaul/pause
pass '\n' to espeak too for e.g. proper pause
2021-06-14 19:53:15 +03:00
Samuel Thibault
53665b8eab Support setting ALSA volume in addition to espeak volume
This allows to make sure to reach the maximum volume permitted by the
hardware. This makes the default volume (5) set ALSA volume to 80%, like
the default ALSA scripts do.

This is not enabled by default, since users will probably want to use a
mixer to fine-tune their volume. But for e.g. installation images, this
allows to spare the use of a mixer and just use the speakup volume
control.
2020-04-29 02:48:39 +02:00