Skip to content

Commit 6284e6a

Browse files
authored
Merge pull request #181 from eduardomozart/patch-1
Add PulseAudio support for macOS
2 parents 2b125de + 12eac9e commit 6284e6a

2 files changed

Lines changed: 165 additions & 3 deletions

File tree

README.md

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@
1010

1111
>Docker image that includes Wine and Winetricks for running Windows applications on Linux and macOS
1212
13-
The docker-wine container can either be run with X11 forwarding or as an RDP server to suit your use case. The default is to use X11 forwarding which utilizes your machine's X server to render graphics directly into your current session and play sounds through pulseaudio (audio redirection on Linux only).
13+
The docker-wine container can either be run with X11 forwarding or as an RDP server to suit your use case. The default is to use X11 forwarding which utilizes your machine's X server to render graphics directly into your current session and play sounds through pulseaudio.
1414

15-
Using docker-wine with an RDP server allows the container to be run on a headless machine or a machine that may not be running an X server. You can then use a Remote Desktop client to connect to the container which may be located either on your local or a remote machine. This is currently the only solution if you require sound on macOS.
15+
Using docker-wine with an RDP server allows the container to be run on a headless machine or a machine that may not be running an X server. You can then use a Remote Desktop client to connect to the container which may be located either on your local or a remote machine.
1616

1717
---
1818

@@ -263,3 +263,5 @@ To test sound, try using `pacat`:
263263
```bash
264264
./docker-wine pacat -vv /dev/urandom
265265
```
266+
267+
You can find additional pulseaudio troubleshooting steps at https://github.com/OpenVoiceOS/ovos-docker/blob/dev/README_MACOS.md

docker-wine

Lines changed: 161 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,84 @@ install_xquartz() {
234234
return $installed
235235
}
236236

237+
configure_pulseaudio () {
238+
239+
# Return 0 (true) if this function makes any changes
240+
local changes_made=1
241+
242+
# Check PulseAudio installed
243+
if ! command -v pulseaudio >/dev/null 2>&1; then
244+
local answer
245+
local attempts
246+
local max_attempts=5
247+
248+
# Prompt to allow install
249+
echo "PulseAudio needs to be installed for sound forwarding to operate. If necessary, Homebrew will also be installed to perform the installation of PulseAudio."
250+
for (( attempts = 0; attempts < max_attempts; attempts++ )); do
251+
252+
read -r -p "Do you want to continue? [y/N] " answer
253+
254+
# Default is No
255+
[ -z "${answer}" ] && answer="n"
256+
257+
case "${answer}" in
258+
[Yy]|[Yy][Ee][Ss])
259+
install_pulseaudio || exit 1
260+
changes_made=0
261+
break
262+
;;
263+
[Nn]|[Nn][Oo])
264+
echo "Unable to start container with sound forwarding. Please install PulseAudio or alternatively use Remote Desktop. e.g. $0 --rdp"
265+
#exit 0
266+
;;
267+
*)
268+
echo "Invalid response. Please use y or n"
269+
;;
270+
esac
271+
done
272+
273+
# Fail after too many attempts
274+
if [ "${attempts}" -ge "${max_attempts}" ]; then
275+
echo "ERROR: Too many invalid responses"
276+
exit 1
277+
fi
278+
fi
279+
280+
return $changes_made
281+
}
282+
283+
install_pulseaudio() {
284+
285+
# Return 0 if PulseAudio is successfully installed
286+
local installed=1
287+
288+
# Install Homebrew
289+
if ! command -v brew >/dev/null 2>&1; then
290+
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"
291+
292+
# Confirm installed
293+
if ! command -v brew >/dev/null 2>&1; then
294+
echo "ERROR: Failed to install Homebrew, unable to proceed with PulseAudio installation"
295+
#exit 1
296+
fi
297+
fi
298+
299+
# Install PulseAudio
300+
if command -v brew >/dev/null 2>&1 && ! command -v pulseaudio >/dev/null 2>&1; then
301+
brew install pulseaudio
302+
fi
303+
304+
# Install SwitchAudioSource
305+
if command -v brew >/dev/null 2>&1 && ! command -v pulseaudio >/dev/null 2>&1; then
306+
brew install switchaudio-osx
307+
fi
308+
309+
# Confirm installed
310+
if command -v pulseaudio >/dev/null 2>&1 && command -v SwitchAudioSource; then installed=0; fi
311+
312+
return $installed
313+
}
314+
237315
add_x11_key () {
238316
local display="$1"
239317

@@ -264,7 +342,7 @@ configure_sound () {
264342
;;
265343
macos)
266344
if [ "${SOUND}" == "default" ]; then
267-
SOUND="dummy"
345+
SOUND="macos"
268346
fi
269347
;;
270348
*)
@@ -277,6 +355,9 @@ configure_sound () {
277355
unix)
278356
configure_pulseaudio_unix_socket
279357
;;
358+
macos)
359+
configure_pulseaudio_macos_socket
360+
;;
280361
dummy|none)
281362
add_run_arg --env="DUMMY_PULSEAUDIO=yes"
282363
;;
@@ -289,6 +370,8 @@ configure_sound () {
289370

290371
configure_pulseaudio_unix_socket () {
291372

373+
configure_pulseaudio
374+
292375
# Use audio if pulseaudio is installed
293376
if command -v pulseaudio >/dev/null 2>&1; then
294377

@@ -318,6 +401,83 @@ configure_pulseaudio_unix_socket () {
318401
fi
319402
}
320403

404+
configure_pulseaudio_macos_socket () {
405+
406+
# Use audio if pulseaudio is installed
407+
if command -v pulseaudio >/dev/null 2>&1; then
408+
409+
# One-off setup for creation of UNIX socket for pulseaudio to allow access for other users
410+
if [ ! -f "${HOME}/.config/pulse/default.pa" ]; then
411+
echo "INFO: Creating pulseaudio config file ${HOME}/.config/pulse/default.pa"
412+
mkdir -p "${HOME}/.config/pulse"
413+
echo -e ".include $(brew --prefix)/var/homebrew/linked/pulseaudio/etc/pulse/default.pa\nload-module module-native-protocol-tcp" > "${HOME}/.config/pulse/default.pa"
414+
fi
415+
416+
# Restart pulseaudio daemon to create the TCP socket
417+
if ! lsof -i :4713 > /dev/null; then
418+
echo "INFO: No socket found for pulseaudio so restarting service..."
419+
pulseaudio -k >/dev/null 2>&1
420+
pulseaudio --exit-idle-time\=-1 >/dev/null 2>&1 &
421+
sleep 1
422+
fi
423+
424+
# Get current macOS audio output
425+
target_description=$(SwitchAudioSource -c | tr -d '\r')
426+
# echo "🔊 SwitchAudioSource -c: '$target_description'"
427+
428+
# Normalize in bash
429+
normalized_target=$(echo "$target_description" | tr -d '\r[:space:]' | tr '[:upper:]' '[:lower:]')
430+
431+
# Find matching sink
432+
matched_sink=$(LANG=en pactl list sinks | awk -v target="$normalized_target" '
433+
function normalize(str) {
434+
gsub(/\r/, "", str)
435+
gsub(/[[:space:]]+/, "", str)
436+
return tolower(str)
437+
}
438+
439+
/^[[:space:]]*Name:/ {
440+
current_name = $2
441+
}
442+
443+
/^[[:space:]]*Description:/ {
444+
raw_desc = substr($0, index($0, $2))
445+
norm_desc = normalize(raw_desc)
446+
447+
# Debug logs (stderr)
448+
#print "🔍 Comparing:" > "/dev/stderr"
449+
#print " Description : " raw_desc > "/dev/stderr"
450+
#print " normalized sink : [" norm_desc "]" > "/dev/stderr"
451+
#print " normalized target: [" target "]" > "/dev/stderr"
452+
453+
if (norm_desc == target) {
454+
#print "✅ Match found: sink name = " current_name > "/dev/stderr"
455+
print current_name # <-- THIS LINE GOES TO STDOUT
456+
exit
457+
}
458+
}
459+
')
460+
# Handle result
461+
if [[ -z "$matched_sink" ]]; then
462+
echo "❌ No matching sink found for: '$target_description'"
463+
#exit 1
464+
else
465+
# Set default sink
466+
# echo "🔧 Running: pactl set-default-sink $matched_sink"
467+
pactl set-default-sink "$matched_sink"
468+
fi
469+
470+
# Add the pulseaudio TCP socket to run args
471+
#add_run_arg --env="PULSE_SERVER=tcp:host.docker.internal"
472+
add_run_arg --env="PULSE_SERVER=docker.for.mac.localhost"
473+
474+
# Share your ~/.config/pulse directory with the container for authentication
475+
add_run_arg --volume="${HOME}/.config/pulse:/home/wineuser/.config/pulse"
476+
else
477+
echo "INFO: pulseaudio not installed so running without sound"
478+
fi
479+
}
480+
321481
run_container () {
322482
local mode
323483

0 commit comments

Comments
 (0)