Editing System-wide speakup

Warning: You are not logged in. Your IP address will be publicly visible if you make any edits. If you log in or create an account, your edits will be attributed to your username, along with other benefits.

The edit can be undone. Please check the comparison below to verify that this is what you want to do, and then publish the changes below to finish undoing the edit.

Latest revision Your text
Line 1: Line 1:
Is it possible to use Speakup across multiple users on Linux? This article is the notes and ramblings of someone who tries to do that.
Is it possible to play audio 'globally' or system-wide in Linux? This article is the notes and ramblings of someone who tries to do that.


== Background ==
== Background ==
Line 26: Line 26:
# Linux boots  
# Linux boots  
# systemd gives root the current audio device
# systemd gives root the current audio device
# systemd starts espeakup as root
# systemd starts starts espeakup as root
# You can read the login prompt or login as root
# You can read the login prompt or login as root
# You log in as your own user
# You log in as your own user
Line 41: Line 41:
Because espeakup can only speak when root is using the current seat effectively becomes useless outside logging in as root on your computer.
Because espeakup can only speak when root is using the current seat effectively becomes useless outside logging in as root on your computer.


It's also important to note that PulseAudio stores settings for volume and outputs. You might be using Bluetooth headphones at low volume but then switch to a root TTY and have espeakup blare loudly out your desktop speakers.
== Linux audio architecture ==
learn what's actually going on here


It really makes me wonder why PulseAudio doesn't have a per-seat instance that lets you switch between sessions and preserves audio configuration but still swaps out which user's applications can use audio.
hardware:


As a quick note: PipeWire has the exact same behaviour, so for the most part you can substitute 'PulseAudio' with 'PipeWire' or any sound other server when reading this page.
- oss


== Existing solutions ==
- alsa
Here are some solutions that kind of work but have severe trade-offs.


=== Running PulseAudio system-wide ===
- audio group
Running system-wide PulseAudio allows you to always have espeakup running and talking regardless of who is currently logged in.


However this has a major security concern, mainly that all users can now play and record audio from your sound card and other applications. For more information see [https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/User/WhatIsWrongWithSystemWide/ What is wrong with system mode?]
sound servers:


Running PipeWire system-wide is a little more complicated: You need to run both the PipeWire daemon and a session manager system-wide. This session manager needs to lock hardware and not give it up on seat switch.
- dmix


I'm not going to include instructions on how to do this as I can't provide support for it. Sorry.
- esd


=== Running espeakup as your user ===
- arts
It's possible to run espeakup as your own user by:


* Taking control of the audio device away from logind
- phonon
* Giving control of all audio devices to your user
* Giving control of the softsynth to your user
* Running espeakup as a daemon as your user
* Running espeakup at boot as your user


I've prepared steps to follow to get this working below.
- pulseaudio


Paste lines between <code>--- PASTE START ---</code> and <code>--- PASTE END ---</code> in to the file specified or a terminal.
- auth using a cookie (even dmix)


Step 1: Put this in <code>/etc/modules-load.d/speakup.conf</code>
- include multiple


--- PASTE START ---
one per user?
speakup
speakup_soft
--- PASTE END ---


This will cause the speakup kernel modules to load at boot.
- handover during login


Step 2: Put this in <code>/etc/udev/rules.d/99-speakup.rules</code>
- handover during seat swapping


--- PASTE START ---
- there's no system-wide audio
SUBSYSTEM=="sound", TAG-="seat", GROUP="audio"
KERNEL=="softsynth*", GROUP="audio"
--- PASTE END ---


This will will do three things:
TODO: when did sound servers lock hardware?


# Stop logind managing sound devices
TODO: when did sound servers give up hardware for seats?
# Give users in the audio group access to sound devices
# Give users in the audio group access to Speakup


Step 3: Put this in <code>~/.config/systemd/user/espeakup.service</code>
pulseaudio doesn't gracefully give up audio, only alsa -> pulseaudio


--- PASTE START ---
- pulseaudio startup race
[Unit]
Description=Software speech output for Speakup
After=pulseaudio.service
[Service]
Environment="default_voice= ALSA_CARD="
ExecStart=/usr/bin/espeakup -d --default-voice=${default_voice}
Restart=always
Nice=-10
OOMScoreAdjust=-900
[Install]
WantedBy=default.target
--- PASTE END ---


This is a service that just runs the espeakup daemon. It is set to start after pulseaudio.
- pipewire has most of the same problems


Change <code>pulseaudio.service</code> to <code>pipewire.service</code> if you're using on PipeWire.
- root cannot use pulseaudio prefs from your user


Step 4: Run these commands in a terminal as your user:
honestly the more i think about it the more it seems like pulseaudio should belong to a seat instead of a specific user, with sessions switching between use of it


--- PASTE START ---
== Attempt 1: Sharing Speakup ==
systemctl --user enable espeakup
- have a proxy that sits between speakup and espeak
loginctl enable-linger
sudo gpasswd -a $USER audio
--- PASTE END ---


This does three things:
- send messages to espeakup instances based on current active UID


# Enable the speakup user service
- during a switch between instances, wait for the current instance to finish talking OR the stop talking control is sent. then start feeding the new instance data
# Enable running user services when logged out
# Add your user to the audio group


Step 5: Reboot and enjoy!
- have a shim that blocks pulseaudio from starting until it has permission, but also don't consume the buffer


There's two downsides to this method:
i do not like how i'm basically reinventing flow control but poorly


# Your user can see what other users are reading, including root
ok so it turns out i was WRONG: you can't share speakup protocol between multiple synths! the protocol is stateful! ie if you tell it to change voice and switch synth the voice change won't be applied. YAY
# Other users can't play audio


== Ideas ==
== Attempt 2: Sharing synth data ==
There are some ideas I've considered to solve this problem.
so this makes the only viable solution to send PCM data from a root espeakup instance.


=== Loaning PulseAudio to root ===
on top of that this also means i have to modify espeakup to handle some flow control AND output to a buffer instead of the sound card
The main case here would be to allow root to use a user's PulseAudio install.


This would have mean that multiple people can privately use a computer, with the exception of one user being able to read root's screen.
== Attempt 3: One Pulse for all ==
 
- user pulse for all system
I'm not too sure what this buys compared to using sudo or something to act as root.
 
In practice I'm not sure how easy this would be to implement. You would need to replace the logind hardware management for sound with something else that allows finer grained management of hardware.
=== Sharing Speakup between multiple users ===
Things would be a lot easier if we could run one espeakup instance per user. This is tricky because the Speakup kernel modules don't have concepts of users or sessions.
 
There's a wide list of engineering problems to solve with this:
 
* Saving and restoring per-user Speakup settings
* Saving and restoring the softsynth state between users
* Proxying /dev/softsynth so users can't read other's data
* Restricting access to the fakekey input device
* Handling graceful handovers from ALSA to PulseAudio
* Handling forceful handovers from PulseAudio to ALSA
* Shim PulseAudio so it waits for hardware access before running
* Flow control to indicate when the proxy is ready to send
* Flow control to indicate when espeakup is ready to talk
* Buffering data when espeakup isn't ready
* Discarding buffered data when speakup signals espeakup to shut up
* Handling messages sent back from espeakup to speakup and flow control for that
 
There's a lot to untangle here since we're touching multiple levels of abstraction.
 
=== Sharing espeakup output between multiple users ===
A more practical solution might be to run a root espeakup instance and proxy its PCM output to users.
 
This would cut down the engineering problems to:
 
* Assigning /sys/accessibility/speakup to the current seat
* Proxying PCM data to stub programs users run
* Handling graceful handovers from ALSA to PulseAudio
* Shim PulseAudio so it waits for hardware access before running
* Flow control to indicate when a proxy is ready to send
* Buffering data when a stub isn't playing audio
* Discarding buffered data when espeakup wants to shut up
 
This gives most the benefits of sharing speakup but without saving or restoring state.
[[Category:Research]]
Please note that all contributions to JookWiki are considered to be released under the Creative Commons Zero (Public Domain) (see JookWiki:Copyrights for details). If you do not want your writing to be edited mercilessly and redistributed at will, then do not submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource. Do not submit copyrighted work without permission!

To edit this page, please answer the question that appears below (more info):

Cancel Editing help (opens in new window)