This project provides a guide to creating a basic USB audio interface out of a Raspberry Pi Zero 2 W. It uses Linux's built-in UAC2 gadget g_audio driver to stream audio between a host computer and the Pi without having to install any special drivers on the host. The audio can then be routed to an output device on the Pi or input can be captured on the Pi and streamed to the host computer.
Any number of sample rates, bit depths, formats, and channels can be configured (within reason of course). The g_audio driver also allows configuring various device description strings.
While this has only been tested on the Pi Zero 2 W, it should also work on any of the Pi models that can be used as device mode USB devices. Other Linux-capable boards that can function in USB device mode can also be used, however some of the steps presented in this guide may need to be modified for that particular board.
I will probably update this guide periodically as new features get added to the g_audio driver. Kernel 5.18 added a lot of great new features related to this, and it also fixed a lot of bugs from the pre-5.18 verions.
Step 1: Flash Raspberry Pi OS LiteRaspberry Pi OS Lite does not come with a desktop environment. This offers some performance improvement especially on lower power devices like the Pi Zero 2 W. If you are not comfortable working in the Linux command line, or you need a desktop for some other reason, choose the normal Raspberry Pi OS or the Full version. Either should work, though I have only tested the Lite version. Depending on your raspberry pi model, you may want to use the 64-bit version of Raspberry Pi OS.
First, insert the micro SD card into your computer.
Using Raspberry Pi Imager, select Choose OS -> Raspberry Pi OS (Other) -> Raspberry Pi OS Lite.
Next, Choose Storage then select the micro SD card that was inserted.
Next, choose the gear icon on the bottom right (if you do not see this icon, make sure your Raspberry Pi Imager software is up to date). This will allow you to configure some things like SSH and WiFi.
Make sure to enable SSH and WiFi so that you can access your Pi later.
Finally, save then choose Write to flash the OS to the micro SD card.
Safely eject the micro SD card from your host machine, and insert it into the Pi. Plug the 5V power supply into the Pi Zero 2 W's PWR IN micro USB port.
Step 2: First Boot and SetupAssuming the network settings were correctly configured and SSH was enabled, you should now be able to SSH into the Pi. On Windows, you can use PuTTY, and on Linux you can use:
ssh -l pi (ip address)
There are many ways to figure out the IP address of your Pi. I would recommend using a network scanner app to find the IP address of the Pi on your local network.
Log in with the username and password you set up in step 1.
Once connected to the Pi via SSH, update the OS:
sudo apt update && sudo apt upgrade -y
Next, update your hostname and password:
sudo raspi-config
Navigate to SystemOptions and change Password and Hostname. You can also reconfigure any other settings you need to in raspi-config.
Enable DWC2 USB OTG driver:
echo "dtoverlay=dwc2" | sudo tee -a /boot/config.txt
Enable DWC2 driver module at boot:
echo "dwc2" | sudo tee -a /etc/modules
Enable g_audio module at boot:
echo "g_audio" | sudo tee -a /etc/modules
Now is probably a good time to reboot:
sudo reboot
When you log back in over SSH, don't forget that you changed your password in Step 3!
Now, check to make sure the kernel modules were loaded:
dmesg | grep -iE "g_audio|dwc2"
You should also check that the UAC2 soundcard is available to ALSA using:
aplay -l && arecord -l
UAC2Gadget should show up. This is the USB interface. You can optionally use the -L
flag instead of -l
to list all PCM interfaces.
When I tested the default 5.10 kernel that Raspberry Pi OS ships with, the g_audio driver wouldn't show up correctly as an audio device with Windows as the host. There are a bunch of fixes to the USB gadget drivers in newer kernel versions, so we will update Raspberry Pi OS to the latest Linux kernel. This process was tested on kernel 5.16.0-rc7 and 5.16.0-rc8 and most of the early 5.18.0 kernels. These versions have the audio device correctly show up in Windows, though there are still limitations and bugs. If you only plan on using Linux as a host, you can probably skip building a new kernel.
NOTE: local building is the simplest method, however it is slow. For a faster compile, please see Step 4b.
Install kernel build tools and screen:
sudo apt install screen git bc bison flex libssl-dev make libncurses5-dev
Start a new screen session. This will be helpful since the build process can take a few hours on a Pi, and this will allow you to build the kernel while not being connected via SSH the entire time:
screen
You can use ctrl+a+d
to detach from the screen session. To reattach to the session, use:
screen -x
Download the latest kernel from the Raspberry Pi Linux GitHub. Feel free to change which branch is cloned if a newer branch exists. This will probably take awhile. Make sure to include --depth=1
otherwise it will try to clone the repository's entire commit history:
git clone --depth=1 --branch rpi-5.18.y https://github.com/raspberrypi/linux
Now enter the directory and apply the default configuration for the RPi Zero 2 W. If you're using a different Pi with OTG, see this guide for other default configurations:
For 32-bit:
cd linux
KERNEL=kernel7
make bcm2709_defconfig
For 64-bit:
cd linux
KERNEL=kernel8
make bcm2711_defconfig
Next, it's time to configure the kernel. Most likely, you'll just want to change the kernel local version name. There are many other options available as well. For example, you can enable debug messages for USB gadgets. It's most intuitive to use menuconfig:
make menuconfig
For local version naming, go to General setup -> Local Version
USB gadget debug options are in Device Drivers -> USB Support -> USB Gadget Support if you want to enable them. You can also check to make sure that the Audio Class 2.0 is included in the kernel from this submenu.
After you are done configuring, use the left/right arrow keys to navigate to the Save button. Save the configuration and use the left/right arrow keys to navigate to the Exit button. Exit menuconfig.
Now it's time to build the kernel. Make sure you are in a screen session for this as it can take a few hours to build on a Pi:
For 32-bit:
make -j4 zImage modules dtbs
sudo make modules_install
sudo cp arch/arm/boot/dts/*.dtb /boot/
sudo cp arch/arm/boot/dts/overlays/*.dtb* /boot/overlays/
sudo cp arch/arm/boot/dts/overlays/README /boot/overlays/
sudo cp arch/arm/boot/zImage /boot/$KERNEL.img
For 64-bit:
make -j4 Image modules dtbs
sudo make modules_install
sudo cp arch/arm64/boot/dts/broadcom/*.dtb /boot/
sudo cp arch/arm64/boot/dts/overlays/*.dtb* /boot/overlays/
sudo cp arch/arm64/boot/dts/overlays/README /boot/overlays/
sudo cp arch/arm64/boot/Image /boot/$KERNEL.img
Reboot your Pi:
sudo reboot
Check to make sure the kernel is the new version:
uname -r
Everything should be ready now! Plug in your Pi to the host computer using the micro USB port labeled USB on the Raspberry Pi Zero 2 W. The Pi should show up as an available audio interface on the host computer.
Step 4b: Update to the Newest Linux Kernel Release (Cross Compile)This method for compiling the Linux kernel is significantly faster than building the kernel locally on the raspberry pi. It requires a separate Linux computer, a VM with a Linux distro, or Windows with Windows Subsystem for Linux (WSL). I generally use WSL.
First, install the necessary build tools on the host:
sudo apt install git bc bison flex libssl-dev make libc6-dev libncurses5-dev crossbuild-essential-armhf crossbuild-essential-arm64
Download the latest kernel from the Raspberry Pi Linux GitHub. Feel free to change which branch is cloned if a newer branch exists. Make sure to include --depth=1
otherwise it will try to clone the repository's entire commit history:
git clone --depth=1 --branch rpi-5.18.y https://github.com/raspberrypi/linux
Now enter the directory and apply the default configuration for the RPi Zero 2 W. If you're using a different Pi with OTG, see this guide for other default configurations. Most likely, you'll just want to change the kernel local version name. There are many other options available as well. For example, you can enable debug messages for USB gadgets. It's most intuitive to use menuconfig:
For local version naming, go to General setup -> Local Version
For 32-bit:
cd linux
KERNEL=kernel7
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- bcm2709_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
For 64-bit:
cd linux
KERNEL=kernel8
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- bcm2711_defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig
Now it's time to build the kernel and modules. Change the -j 8
flag to change how many threads the compiler will use:
For 32-bit:
mkdir ../modules
make -j 8 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- zImage modules dtbs
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=../modules modules_install
For 64-bit:
mkdir ../modules
make -j 8 ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- Image modules dtbs
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- INSTALL_MOD_PATH=../modules modules_install
Next, we will copy the files to the RPi over WiFi. First, find your RPi's IP address and username and put them in a variable:
RPI_LOGIN=pi@192.168.0.172
Now copy the files from your host to your RPi using rsync. Copy/paste these lines one at a time:
For 32-bit:
rsync -v -e "ssh" --rsync-path="sudo rsync" arch/arm/boot/dts/*.dtb $RPI_LOGIN:/boot/
rsync -v -e "ssh" --rsync-path="sudo rsync" arch/arm/boot/dts/overlays/*.dtb* $RPI_LOGIN:/boot/overlays/
rsync -v -e "ssh" --rsync-path="sudo rsync" arch/arm/boot/dts/overlays/README $RPI_LOGIN:/boot/overlays/
rsync -v -e "ssh" --rsync-path="sudo rsync" arch/arm/boot/zImage $RPI_LOGIN:/boot/$KERNEL.img
For 64-bit:
rsync -v -e "ssh" --rsync-path="sudo rsync" arch/arm64/boot/dts/broadcom/*.dtb $RPI_LOGIN:/boot/
rsync -v -e "ssh" --rsync-path="sudo rsync" arch/arm64/boot/dts/overlays/*.dtb* $RPI_LOGIN:/boot/overlays/
rsync -v -e "ssh" --rsync-path="sudo rsync" arch/arm64/boot/dts/overlays/README $RPI_LOGIN:/boot/overlays/
rsync -v -e "ssh" --rsync-path="sudo rsync" arch/arm64/boot/Image $RPI_LOGIN:/boot/$KERNEL.img
Remove extra build files and copy modules to RPi:
rm -rf ../modules/lib/modules/*/build ../modules/lib/modules/*/source
rsync -rv -e "ssh" --rsync-path="sudo rsync" ../modules/lib/modules/* $RPI_LOGIN:/lib/modules/
Now, ssh into your RPi and reboot it:
sudo reboot
Check to make sure the kernel on the RPi is the new version:
uname -r
Step 5: Reconfiguring g_audioThere are a few commands that will be useful when using the g_audio driver if you want to reconfigure the USB interface's sample rate, bit depth, etc.
Here are the possible input parameters used to configure the device:
modinfo g_audio
You only need to specify parameters when starting the module if you need the specific parameter changed from the default value. For example, start g_audio with bidirectional, stereo audio at 96kHz / 32-bit:
First, unload the current g_audio instance:
sudo modprobe -r g_audio
Now start g_audio with parameters:
sudo modprobe g_audio c_srate=96000 c_ssize=4 p_srate=96000 p_ssize=4 iProduct="Testing Device"
You can read the current values by using:
grep -H '' /sys/module/g_audio/parameters/*
Remember to unload the g_audio module before making any changes to the parameters.
These parameters will not save on reboot. If you want to load specific settings at boot, you can add a configuration file to /etc/modprobe.d/
(the file can have any name, but must have the .conf
extension):
sudo nano /etc/modprobe.d/g_audio.conf
Now add your parameters to that file, save, and exit.
options g_audio (parameters)
For the above example, the config file would be:
options g_audio c_srate=96000 c_ssize=4 p_srate=96000 p_ssize=4 iProduct="Testing Device"
Testing with Windows HostsUsing a Windows host can be somewhat buggy depending on which kernel revision that was used for the RPi. Some older kernels (pre 5.18) have problems changing the g_audio settings on the fly such as sample rates, formats, etc. sometimes results in Windows recognizing the device, but not being able to send audio to the Pi. In order to change these settings, you must first configure the g_audio settings. Then you need to uninstall the device in Windows. This is detailed at the end of this section.
Also, some configurations don't work at all. A safe check to see if the configuration will work is to see if both the input and output show a sample rate and bit depth in Windows. Usually, if it shows this information, the input or output will work correctly. Linux hosts do not seem to have these problems.
This was tested on Windows 11 (Build 22000). Your mileage may vary.
Plug your Pi into your Windows host computer. On the host machine, new audio input and output devices should show up called InternalAUXJack(Source/Sink). You will probably have to enable the device in SoundSettings in the Windows Settings.
Next, test the gadget by playing some audio through the USB interface and looping it back to the host.
First go to Control Panel -> Sound and set the InternalAUXJack as the default input and output devices.
Next, in the Recording tab, select InternalAUXJack and Properties. Go to the Listen tab at the top. Check Listento this device and select a device to use to listen through. The audio will be sent to the Pi through the USB interface then sent back and played through the selected device.
Now on the Pi, record audio to test.wav
at stereo 96kHz / 32-bit (signed little endian):
arecord --rate 96000 -c 2 -f S32_LE test.wav
Play some music on your computer. This should route to the Pi through USB and save in the file test.wav
. You can listen to the results with:
aplay test.wav
You can also loop the audio from your computer to the Pi then straight back to your computer. Following the above example:
alsaloop -C hw:CARD=UAC2Gadget,DEV=0 --rate=96000 -f S32_LE -P hw:CARD=UAC2Gadget,DEV=0 ---rate=96000 -f S32_LE
Sometimes, in order to change audio settings on the Pi and have them reflected in Windows, you will have to uninstall the device and let Windows reinstall the drivers each time you change settings. It is easiest to create a set of default parameters to be loaded into g_audio at boot as described at the end of the "Reconfiguring g_audio" section of this project.
In order to uninstall the audio device in Windows, first, unplug your Pi and open DeviceManager. Next, go to the Sound, video and game controllers section. Click on the View tab at the top and make sure ShowHidden Devices is enabled.
Now, right click on Source/Sink, and select UninstallDevice
Plug your Pi back in and wait for the driver to reinstall. You will probably have to reenable the sound device. Then check to make sure both the input and output device (if g_audio is configured for both) show a bit depth and sample rate.
This usually means Windows will actually accept audio from the device. If you can't find the Advanced tab, there is probably something wrong with the device. You can try to reconfigure g_audio or reinstall the driver in Windows.
Testing With Linux HostsUsing Linux hosts is your best bet. As far as I can tell, changing sample rates, audio formats, etc. on the fly does not cause any problems. Audio software can vary somewhat per distro, so I'll be sticking to only testing with ALSA.
The easiest and quickest way to test that should work on all distros is to start a loopback on the Pi:
alsaloop -C hw:CARD=UAC2Gadget,DEV=0 --rate=96000 -f S32_LE -P hw:CARD=UAC2Gadget,DEV=0 ---rate=96000 -f S32_LE
Next, on the host, select the USB Device as the default input and output device from whatever sound control panel your distro provides. Now, play some audio on the host machine, and record it on the host with:
arecord --rate 96000 -c 2 -f S32_LE test.wav
Then, after you've finished recording, switch your default output to whatever output device you normally test audio through. Now, play it back with:
aplay test.wav
With any luck, you should be able to hear what you just recorded.
Testing With macOS HostsI have no way to test this so if anyone has any luck, please let me know in the comments.
ConclusionNow you can use your Pi as a USB Audio interface. The next step would be to connect one of the many RPi GPIO header audio DAC cards and pipe audio from a host computer to your Pi and the DAC. You could then listen on a pair of speakers or headphones. You could even add some effects such as equalizers and room correction in between. That is outside the scope of this guide, but there are many resources online describing those processes.
Any suggestions and comments are greatly appreciated, especially if it would help fix some of the issues found with using Windows hosts!
Comments