Fix WSL Audio Output on Windows 10

Version, Version and Version

This is the updated version, I use Windows 10 21H2 when writing the draft.

Windows: Version 22H2 (OS Build 25182.1000)
WSL: 2 (WSLg)
Distro: Devuan Testing
Windows PulseAudio: 1.1 (2011-10-25), installed via Chocolatey
Libpulse0: 15.0+dfsg1-4+b1
Cmus: 2.10.0-2
Windows Terminal: 1.14.2282.0

Intro

Today I wanted to enjoy my Memories Off OST collection on cmus on Debian on WSL 2 on Windows 10. As I did not upgrade to Windows 11 due to privacy and stability concerns, I could not use built-in GUI app feature for WSL.

I started the journey to fix broken audio output in WSL.

Get the Malody Working

Before I start, please be aware that each step is on Windows/WSL or you’ll get confused.

1. Download PulseAudio for Windows

On Windows:

The guide I followed used a third party built for X2Go (Remote Desktop client). I chose to use the good old Chocolatey anyway. The package uses the preview binary provided by freedesktop.org. Just run choco install pulseaudio -y in an elevated PowerShell window.

2. Configure PulseAudio for Windows

On Windows:

First we need to find the installation directory of PulseAudio for Windows.

$ Get-Command pulseaudio.exe
CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Application     pulseaudio.exe                                     1.0.0.0    C:\ProgramData\chocolatey\bin\pulseaudio.exe

Create a file called config.pa in the folder we got above. In this case, C: \ProgramData\chocolatey\bin.

Open the file and add following lines and save the file:

load-module module-native-protocol-tcp auth-ip-acl=127.0.0.1;172.16.0.0/12
load-module module-esound-protocol-tcp auth-ip-acl=127.0.0.1;172.16.0.0/12
load-module module-waveout sink_name=output source_name=input record=0

Explanation:

  1. We allow connections from 127.0.0.1 (local IP address) and 172.16.0.0/12 (default IP range, i.e. 172.16.0.0 - 172.31.255.255) for WSL2.
  2. To stream the audio to external devices, change the first line to load-module module-native-protocol-tcp auth-anonymous=1 to allow anonymous connections. It’s better not to change it for better security.
  3. record=0 disable the microphone. Change it to record=1 if you need that.

3. Run PulseAudio (Freely)

The guide I followed uses NSSM to install PulseAudio as a Windows service, but I did not like that.

Head to the installation folder and create a shortcut, copy it to a folder in $PATH and rename it to pa.

Under the “Shortcut” section of Properties:

  • Target: C:\ProgramData\chocolatey\bin\pulseaudio.exe -F C:\ProgramData\chocolatey\bin\config.pa --exit-idle-time=-1
  • Start in: C:\ProgramData\chocolatey\bin
  • Run: Minimized
  • In the “Advanced” tab, enable the “Run as administrator” option.

Explanation:

  1. -F tells PulseAudio to run the specified script on program startup.
  2. --exit-idle-time=-1 disables the option to terminate the daemon after a number of seconds of inactivity.

Now when we want to start PulseAudio server on Windows, just press Win + R, type pa, hit enter and go.

4. Configure PulseAudio in WSL

On WSL:

libpulse0 should be installed by default but just make sure by running sudo apt install libpulse0.

Edit shell config file using the favored editor. In this case, vim ~/.zshrc, paste the following to somewhere:

# Enable audio output (PulseAudio) on WSL 2
# Get the IP Address of the Windows 10 Host and use it in Environment.
# Internal WSL IP
export HOST_IP="$(ip route |awk '/^default/{print $3}')"
export PULSE_SERVER="tcp:$HOST_IP"
# Enable this only if I wanna use GUI
#export DISPLAY="$HOST_IP:0.0"

Note:

  1. Uncomment the last line to enable audio support for graphical applications.
  2. If you are on WSL 2.0.0 and above, and using mirrored networking mode, change export PULSE_SERVER="tcp:$HOST_IP" to export PULSE_SERVER="tcp:127.0.0.1".

Restart your default shell. In this case, exec zsh.

5. Configure Cmus in WSL

Edit ~/.config/cmus/rc(create it if it doesn’t exist) and add set output_plugin=pulse to get its sound output to work on Windows.

It can be done within cmus by running :set output_plugin=pulse and :save if you wish to do so.

Cmus may arise an error when starting:

cmus: Error: an error occured while initializing MPRIS: No medium found. MPRIS will be disabled.

This can be ignored by running :set mpris=false in cmus from this issue .

6. Enjoy

  • On Windows: Win + R ->pa
  • On WSL: cmus

Don’t forget to allow the PulseAudio server to bypass the firewall. It should only need local network access in most cases.

I don’t know why, but certain songs can cause cmus to jam, and WSL just halt with CPU burning and fun taking off. tmux kill-server did not solve the issue. I had to use wsl --shutdown in PowerShell to stop that.

I did not experience it for a long time so chances are WSL team fixed it already.

Reference

  1. PulseAudio config and method to find internal WSL IP
  2. Did not use but great to know new methods to find Windows host IP

Vinfall's Geekademy

Sine īrā et studiō