-
Notifications
You must be signed in to change notification settings - Fork 5
/
broo
executable file
·353 lines (255 loc) · 10.4 KB
/
broo
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
#!/bin/bash
##############################################################################
# Broo is a bash script that connects your phone as microphone wirelessly.
# Copyright (C) 2021 Siddh Raman Pant
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero 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 Affero General Public License for more details.
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
# The online repository can be found at <https://github.com/siddhpant/broo>.
##############################################################################
# Exit on errors.
set -euo pipefail
# Override localisation (for eg., pactl parsing is affected by it).
export LC_ALL=C.UTF-8
# Folder to store our ephemeral files containing PIDs etc.
VAR_RUN="/var/run/user/$UID/broo"
# Folder which stores broo config.
BROO_CFG_LOC="$HOME/.config/broo"
start_mumble_server() {
# Starts the Mumble server.
# The PID is saved automatically due to the config in ini file.
if [[ $(command -v mumble-server) ]]; then
nohup mumble-server -ini "$BROO_CFG_LOC/murmur.ini" -fg \
&>"$VAR_RUN/murmur.out" &
else
nohup murmurd -ini "$BROO_CFG_LOC/murmur.ini" -fg \
&>"$VAR_RUN/murmur.out" &
fi
echo $! > "$VAR_RUN/murmur.pid"
sleep 3 # Give time for it to set up.
} # End of start_mumble_server()
stop_mumble_server() {
# Stops the Mumble server, and deletes the PID file if it remains.
echo -n "."
kill $(<"$VAR_RUN/murmur.pid")
rm "$VAR_RUN/murmur.pid"
rm "$VAR_RUN/murmur.out"
} # End of stop_mumble_server()
start_mumble_client () {
# Starts the Mumble client (with logs for monitoring), stores its PID,
# and waits till the connection to server is established.
# We prefer offscreen, but there can be a rare case that using offscreen
# can cause mumble to not connect to server, so we test during setup.
# A file "no_offscreen" is created if we don't want to do offscreen.
if [[ -f "$BROO_CFG_LOC/no_offscreen" ]]; then
nohup mumble "mumble://127.0.0.1" \ &>"$VAR_RUN/mumble_client.out" &
else
nohup mumble "mumble://127.0.0.1" -platform offscreen \
&>"$VAR_RUN/mumble_client.out" &
fi
# Save the PID.
echo $! > "$VAR_RUN/mumble_client.pid"
# Wait till we establish a conenction. Upon connection, a line is logged
# starting with "ServerHandler: TLS cipher", so we monitor the logs.
# tail + grep idea by 00prometheus on <https://superuser.com/a/900134>.
(tail -f "$VAR_RUN/mumble_client.out" &) | grep --line-buffered -q \
"ServerHandler: TLS cipher"
} # End of start_mumble_client()
stop_mumble_client() {
# Stops the Mumble client, and deletes the PID file.
echo -n "."
kill $(<"$VAR_RUN/mumble_client.pid")
rm "$VAR_RUN/mumble_client.pid"
rm "$VAR_RUN/mumble_client.out"
} # End of stop_mumble_client()
get_mumble_sink_input_index() {
# Returns / echo's the sink input number/index of Mumble client.
mumble_sink_index=
prop="application.name = \"Mumble\""
# Loop over pactl output to get the sink index of Mumble.
while IFS= read -r line ; do
if [[ ${line:0:12} == "Sink Input #" ]]; then
mumble_sink_index="${line:12}"
elif [[ ${line:2} == $prop ]]; then
break
fi
done <<< $(pactl list sink-inputs)
echo $mumble_sink_index
} # End of get_mumble_sink_input_index()
configure_sink() {
# Configures the Broo sink created for further use.
# Suppress the output volume to speakers, otherwise we will hear ourselves.
pactl set-sink-volume Broo 0
# Set current sink as default sink, and set Broo as default source.
default_sink=$(pactl get-default-sink)
pactl set-default-source Broo
pactl set-default-sink $default_sink
# Move Mumble to Broo sink. For that, we need Mumble sink-input index.
pactl move-sink-input $(get_mumble_sink_input_index) Broo
echo -e \
'Done! Use "Broo" or "Monitor of Broo" as your input mic, and enjoy!\n'
} # End of configure_sink()
add_mic_pipewire() {
# Creates a virtual mic by creating a PipeWire loopback, and saves its PID.
nohup pw-loopback \
--capture-props='media.class=Audio/Duplex
node.name=Broo node.description=Broo' &>/dev/null 2>&1 &
echo $! > "$VAR_RUN/pw_loopback.pid"
sleep 3 # Give time for it to set up.
} # End of add_mic_pipewire()
remove_mic_pipewire() {
# Removes the PipeWire loopback, and deletes the PID file.
echo -n "."
kill $(<"$VAR_RUN/pw_loopback.pid")
rm "$VAR_RUN/pw_loopback.pid"
} # End of remove_mic_pipewire()
add_mic_pulseaudio() {
# Loads a sink using pactl, and saves the index returned.
# Add sink and save the index it returns.
pa_index=$(pactl load-module module-null-sink \
sink_name="Broo" \
sink_properties=device.description="Broo")
echo $pa_index > "$VAR_RUN/pa_sink.index"
sleep 3 # Give time for it to set up.
# Restore ALSA settings if the backup file exists.
if [ -f ~/.config/asound.state ]; then
alsactl --file ~/.config/asound.state restore
fi
} # End of add_mic_pulseaudio()
remove_mic_pulseaudio() {
# Unloads the PulseAudio sink, and deletes the index file.
echo -n "."
pactl unload-module $(<"$VAR_RUN/pa_sink.index")
rm "$VAR_RUN/pa_sink.index"
} # End of remove_mic_pulseaudio()
check_if_started() {
# Check if the fodler already exists.
if [ -d $VAR_RUN ]; then
echo "Broo was already started earlier."
echo "To force start, use 'f' on the starting prompt."
return 1
fi
} # End of check_if_started()
start_broo() {
# Starts Broo with its required things.
check_if_started
mkdir $VAR_RUN
echo -e "Initialising Broo...\n"
start_mumble_server
start_mumble_client
# Check if PipeWire used ("PipeWire" will be a substring in output).
if [[ $(pactl info | grep "^Server Name") =~ "PipeWire" ]]; then
add_mic_pipewire
else
add_mic_pulseaudio
fi
configure_sink
server_ip=$(ip -4 a | grep "state UP" -A1 |
awk 'NR!=1 {print $2}' | cut -d '/' -f1)
echo "Connect to Broo's server on $server_ip :)"
} # End of start_broo()
fix_certificate_problem() {
# Sometimes the client doesn't connect to server because of certificate.
# We will open Mumble GUI to make the user accept it to fix the problem.
check_if_started
mkdir $VAR_RUN
echo -e "
A Mumble window will open. After that, do the following:
- Connect to the server, and accept the certificate.
- Close and exit Mumble.
" | awk '{$1=$1};1'
read -n1 -sp "Press any key to proceed and open Mumble..."
start_mumble_server
mumble "mumble://127.0.0.1" >/dev/null 2>&1
stop_mumble_server
rmdir $VAR_RUN
echo -e "\rDone! Broo will hopefully work without hiccups now!"
} # End of fix_certificate_problem()
force_kill_broo() {
# Force kill Broo by killing processes and deleting $VAR_RUN folder.
rm -rf $VAR_RUN
pkill mumble || true
pkill mumble-server || true
pkill murmurd || true
if [[ $(pactl info | grep "^Server Name") =~ "PipeWire" ]]; then
pkill pw-loopback || true
else
pulseaudio -k || true
fi
} # End of force_kill_broo()
stop_broo() {
# Terminates Broo with its required things.
if [ ! -d $VAR_RUN ]; then
echo "Broo has already stopped."
echo "To force stop, use 'q' on the starting prompt."
return 1
fi
echo -n "Terminating Broo"
if [[ $(pactl info | grep "^Server Name") =~ "PipeWire" ]]; then
remove_mic_pipewire
else
remove_mic_pulseaudio
fi
stop_mumble_client
stop_mumble_server
rmdir $VAR_RUN
echo -e "\nTerminated."
} # End of stop_broo()
initiate() {
# The main stuff initiates here. Needs one 1-char argument.
case $1 in
f|F) force_kill_broo ;& # Fallthrough to the next case.
s|S) start_broo ;;
c|C) stop_broo ;;
q|Q) force_kill_broo; echo "Force killed the requisite processes." ;;
p|P) fix_certificate_problem ;;
*) echo "Shoo away your cat from the keyboard man!"
exit 1
;;
esac
} # End of initiate()
help_text() {
# Sends the help text when `-h` or `--help` in command-line arguments.
echo -e "
Usage: broo [OPTION]
Connect your phone / external devices as microphone wirelessly.
Broo will prompt for option on starting up. While it asks for starting
or stopping Broo, there are multiple valid inputs, listed below:
s Set up / start Broo.
c Stop / close Broo.
q Force quit Broo (kills requisite processes).
f Force quit Broo and start again.
p Open mumble to fix certificate if Broo user isn't in channel.
Command-line argument is optional. They are as follows:
-h, --help Show this help text and exit.
-s, --start Equivalent to using 's' on Broo's prompt.
-c, --close Equivalent to using 'c' on Broo's prompt.
-q, --force-quit Equivalent to using 'q' on Broo's prompt.
-f, --force-start Equivalent to using 'f' on Broo's prompt.
-p, --certificate Equivalent to using 'p' on Broo's prompt.
" | cut -c 9- | head -n -1 | tail -n +2
} # End of help_text()
# Script on calling starts from here.
case ${1-prompt} in
prompt) read -n1 -p "Do you want to start Broo, or close it? (s/c): " todo
echo -e "\n"
initiate "$todo"
;;
-h|--help) help_text ;;
-s|--start) initiate "s" ;;
-c|--close) initiate "c" ;;
-q|--force-quit) initiate "q" ;;
-f|--force-start) initiate "f" ;;
-p|--certificate) initiate "p" ;;
*) echo "Shoo away your cat from the keyboard man!"
exit 1
;;
esac
# End of file.