using a Raspberry Pi 4 as a USB DSP-DAC

Status
This old topic is closed. If you want to reopen this topic, contact a moderator using the "Report Post" button.
Below is the output from sudo modinfo g_audio:

ON HOST:
Code:
charlie@ZBOX-CI329-1:~$ sudo modinfo g_audio
filename:       /lib/modules/4.15.0-58-generic/kernel/drivers/usb/gadget/legacy/g_audio.ko
license:        GPL
author:         Bryan Wu <cooloney@kernel.org>
description:    Linux USB Audio Gadget
srcversion:     CAC36978C11B925AFA752EF
depends:        libcomposite
retpoline:      Y
intree:         Y
name:           g_audio
vermagic:       4.15.0-58-generic SMP mod_unload 
signat:         PKCS#7
signer:         
sig_key:        
sig_hashalgo:   md4
parm:           idVendor:USB Vendor ID (ushort)
parm:           idProduct:USB Product ID (ushort)
parm:           bcdDevice:USB Device version (BCD) (ushort)
parm:           iSerialNumber:SerialNumber string (charp)
parm:           iManufacturer:USB Manufacturer string (charp)
parm:           iProduct:USB Product string (charp)
parm:           p_chmask:Playback Channel Mask (uint)
parm:           p_srate:Playback Sampling Rate (uint)
parm:           p_ssize:Playback Sample Size(bytes) (uint)
parm:           c_chmask:Capture Channel Mask (uint)
parm:           c_srate:Capture Sampling Rate (uint)
parm:           c_ssize:Capture Sample Size(bytes) (uint)

ON PI:
Code:
pi@Pi4gadget:~ $ sudo modinfo g_audio
filename:       /lib/modules/4.19.67-v7l+/kernel/drivers/usb/gadget/legacy/g_audio.ko
license:        GPL
author:         Bryan Wu <cooloney@kernel.org>
description:    Linux USB Audio Gadget
srcversion:     CAC36978C11B925AFA752EF
depends:        libcomposite
intree:         Y
name:           g_audio
vermagic:       4.19.67-v7l+ SMP mod_unload modversions ARMv7 p2v8 
parm:           idVendor:USB Vendor ID (ushort)
parm:           idProduct:USB Product ID (ushort)
parm:           bcdDevice:USB Device version (BCD) (ushort)
parm:           iSerialNumber:SerialNumber string (charp)
parm:           iManufacturer:USB Manufacturer string (charp)
parm:           iProduct:USB Product string (charp)
parm:           p_chmask:Playback Channel Mask (uint)
parm:           p_srate:Playback Sampling Rate (uint)
parm:           p_ssize:Playback Sample Size(bytes) (uint)
parm:           c_chmask:Capture Channel Mask (uint)
parm:           c_srate:Capture Sampling Rate (uint)
parm:           c_ssize:Capture Sample Size(bytes) (uint)
 
nice aproach, lots of work put into it.
here is an alternative aproach:

USB/IP/

it generates a kernel sided VHCI (virtual host controler interface) and forwards it over ethernet . the code is upstream, userspace tools are in most of the repos. it would be nice to hear your opinions about it.

i wanted to do a setup later because i wanted to wait for pipewire to implement A/V delay function and use it this way. it might work with audio.
the homepage is pretty much outdated but its being actively developed: last update is a month ago. cheers.
 
nice aproach, lots of work put into it.
here is an alternative aproach:

USB/IP/

it generates a kernel sided VHCI (virtual host controler interface) and forwards it over ethernet . the code is upstream, userspace tools are in most of the repos. it would be nice to hear your opinions about it.

i wanted to do a setup later because i wanted to wait for pipewire to implement A/V delay function and use it this way. it might work with audio.
the homepage is pretty much outdated but its being actively developed: last update is a month ago. cheers.

That project seems to have a different goal. According to the text in the About section:
The USB/IP Project aims to develop a general USB device sharing system over IP network.
Here we are doing the reverse: using the USB bus to send IP packets over a local connection, so no "sharing". We happen to be using that system to send audio over the IP link (USB ethernet gadget) to another computer that is directly connected to the host. There is no need for a true, routable ethernet connection. Instead we set up the USB gadget to act as a private, non-routable local network and use that for audio. The motivation for that is to use the connected computer (e.g. a Raspberyr Pi 4) to do DSP on the incoming (stereo) audio and then output it over a multichannel DAC. We can create an audio peripheral that makes use of the audio processing capabilities of Linux and offload it from the host (e.g. Windows) platform.
 
Last edited:
While I work with phofman to try to get the USB audio gadget mode working correctly under Windows, I am working on the batch/script files that are needed to send audio over the USB ethernet gadget mode. The latter works under Windows, and has the added benefit that the same scripts can be used to send PCM (uncompressed) audio over your LAN from a Windows machine to a Pi or other linux machine where your endpoint (DAC) is located and where DSP can be performed.
 
I read your posts on the alsa-devel mailing list. IMO all the stuff you describe is pretty complicated. My opinion:
a) adaptive in this case is perfectly fine if you have proper adaptive resampling, i.e. a pll implemented digitally with smooth, long term averaged adjustement of the resampling factor.
b) if you want to go async and at the same time avoid all the userspace feedback hassle and the daemon keeping an eye on this, why not use an existing alsa device as clocking master and slave the gadget to it. And then base the clock/timing feedback channel off of the master alsa device's clock/timing? The "master" device could be specified as module parameter.

Sorry for being lazy and not signing up to the alsa devel mailing list :)
 
At this point phofman has split off the effort on the USB audio gadget, while I have been continuing to work on sending audio using the USB ethernet gadget.

I used the RTP protocol to send over UDP for streaming audio over IP. To do this I have been using the Gstreamer platform, since it has good clock management under this scheme. Previously I was sending audio data over my LAN from where my audio player was located (computer #1) to where my DACs, amps, and speakers were located (computer #2, e.g. a Raspberry Pi). Since our "local" connection between the host computer and the Raspberry Pi 4 is ethernet over USB, the same kind of audio streaming is taking place, just over a local and non-routable connection.

The following approach is used:
On the host (e.g. Windows) computer, we can tap into the audio stream for the currently active audio device using WASAPI as input to Gstreamer. The audio will be in whatever playback format the user has selected in the sound device on the Windows machine. Gstreamer then converts this to 16 or 24 bit RTP frames and sends it to an IP address (the IP address of local USB ethernet gadget). On the Pi 4, another running instance of Gstreamer receives the RTP stream, puts the packets in the correct order, calculates the clock from the RTP timestamps, buffers the audio, and then sends it to the Pi's DAC.

There is some initial one-time setup that includes configuring the Pi 4 to present the USB-C port as an ethernet gadget, editing a couple of config files on the Pi, and installing Gstreamer on both the Pi and the Windows machine (a prebuilt executable is available for Windows). Please keep in mind this can only work on the Pi 4 or Pi zero (not tested on the zero), and on the Pi 4 you MUST supply power to the Pi via the GPIO pins and not via the USB-C (power) connector.

Once the initial setup is complete, the user should install two scripts, one for Windows and the other for the Pi. These automate all the audio streaming tasks for the user. On the Pi, the script should be running all the time - it could be called at startup. On Windows, a batch script can be placed on the Desktop where it appears as an icon. Clicking on the icon starts the audio data flowing to the Pi. I am doing the final polishing of these scripts now, and I should be able to post them here in the next couple of days.

The scripts check whether the USB ethernet connection has been established, and then run the relevant Gstreamer pipeline. On the Pi, the script checks to see if any audio data is flowing as well, because after the first gadget connection is made it always appears to be "up" even when the cable has been disconnected. Checking the RX buffer to see if data is arriving is a better way to trigger the launch of the Gstreamer pipeline. At boot up of the Pi, the IP address is not yet assigned, so that still has to be checked as well.

The scripts are configured to stream stereo audio. The user does not need to do anything except make sure the IP address, bit depth, and sample rate for the stream are correctly configured in the script files by editing a couple of lines at the top of each one. It's then set and forget, as long as the user maintains that audio format in the Windows audio device that is being used as the source for WASAPI.

This application requires resampling except when the source audio is already at the same rate as the stream. Windows audio, or the application playing the source, will perform the resampling.

This setup is also capable of performing multichannel DSP within Gstreamer on the Pi using LADSPA plugins. To do that requires editing of the Gstreamer pipeline in the shell script on the Pi, installing LADSPA on the Pi, and choosing/building one or more plugins. I will cover how to do that at a later date. If anyone wants more info they are welcome to post in the thread to encourage that discussion.

For now, the scripts will demonstrate what can be expected when sending audio over the USB ethernet gadget, and that is a good first step.
 
Here is an amazon seller link to the 8-channel HDMI audio extractor HAT that I use in this project and that supplies power to the PI via the GPIO pins so that you can use the USB-C port as a OTG ethernet gadget:
https://www.amazon.com/WINGONEER-WX6000-Multifunction-Expansion-Raspberry/dp/B06XCFBTVW
The 5V 4A external power supply you will need is also available on that page under the heading "Frequently bought together".

51itP4HqyFL.jpg


Since the Pi 4 has micro HDMI ports and the HDMI input to the X6000 board is full size you will need an adapter cable or a standard HDMI cable plus an M-F adapter like this one:
https://www.amazon.com/GearIt-Micro-HDMI-Adapter-Type/dp/B00V5KRF66/ref=sr_1_11

Is there a DSP-Chip on the wx6000-board, that you can control over the GPIO from the Pi? Would be nice: plug any hdmi-source (LPCM7.1) to it, make all dsping over pi and then to the hdmi-out to an reciever.
 
Is there a DSP-Chip on the wx6000-board, that you can control over the GPIO from the Pi? Would be nice: plug any hdmi-source (LPCM7.1) to it, make all dsping over pi and then to the hdmi-out to an reciever.

No. It's just an HDMI audio extractor. The DSP must be performed upstream from the WX6000 and sent to it over HDMI.

The WX6000 is just a convenient DAC and power via GPIO for the Pi-4. I need both in this application.
 
After some more experimenting, I was able to successfully initiate the USB ethernet gadget connection from a type A USB port (it often looks like this):

An externally hosted image should be here but it was not working when we last tested it.


...to the USB-C port on the Pi 4. To do this I had to buy an adapter: one was used on the end of a USB type A male to mini female cable and was mini A to USB-C. The other was a male type A USB to USB-C female adapter, with a USB-C cable.

I was only able to get the connection to work when I was using a USB 3.x port on the host (e.g. laptop #2). The connection could not be initialized properly when using a USB 2 port, instead it tried to become a USB serial gadget. Plugging the cable into the USB 3 port resulted in the RNDIS driver installing correctly.

Oddly enough, on the laptop that had been using up to this point (laptop #1) from its USB-C port, when I used the adapter dongle and connected the cable via USB 3.0 type A port, it would not work. It only seems to work when the connection is made to the USB-C port on this machine. It could be that the RNDIS driver is somehow bound to a particular port, which ever one you use first. Not sure. Maybe phofman has some insight into this behavior?
 
Last edited:
@Charlie, I've been following your various posts with interest.

My ultimate plan is to have the following sources:
- Sony Android TV - I'm hoping this can output digital audio somehow - ideally USB out
- Streaming service (radio / spotify etc...)
- Local SSD media - FLAC/MP3

All feeding into a Pi operating the following:
- Dynamically selected EQ or crossover when local SSD media is being played
- 8-channel DAC
- Digital volume control
optional:
- Volume level normalisation
- "Bit perfect / pure mode"

I can physically connect the Pi to the sources - so am not required to WiFi /bluetooth or otherwise stream wirelessly

the dynamic EQ / crossover is required currently as some music I like does not sound good on my speakers.

I currently have alsaeq (?) running on an Archlinux based distro called Archphile outputting to your conventional 2-channel HAT DAC feeding an analogue chain and passive speakers (and dynamically selected EQ works listening to track changes on MPD.

I've been reading your excellent ACD LADSPA plug in tutorials and playing around with the view for my next speaker being fully active.
 
I have posted my initial release of the scripts used to stream audio from the host PC to the Pi, and an installation/directions/HOW-TO. These documents are attached.

It would be great for someone to try this out and then post here about it. If any problems arise please let me know.


.
 

Attachments

  • Playback Windows Audio on a Pi4 over USB ver1.zip
    6.4 KB · Views: 578
Hi Charlie!

I've also been planning on almost exactly the same as you. I downloaded the zip, I may be able to give it a try on my RPI4 over the weekend.

It's a shame that g_audio doesn't work with Windows, it would be a necessity for me. UAC is a future proof way to deal audio streaming, since usb-audio has gained much popularity over the years. I wouldn't be surprised if usb replaced spdif as digital audio interconnect over short distances. That in mind RPI4 with working UAC would be pluggable to other such devices.

The world is full of streaming software for multiple platforms. If g_audio isn't working as expected, wouldn't it really be easier to use ethernet through LAN? Wouldn't that also make it easier to stream audio from multiple sources, like mobile devices?
 
Hi Jukka!

I've also been planning on almost exactly the same as you. I downloaded the zip, I may be able to give it a try on my RPI4 over the weekend.
Yes, please try it. Let me know how it works out for you, if you have any trouble, etc. I would like to know.

It's a shame that g_audio doesn't work with Windows, it would be a necessity for me. UAC is a future proof way to deal audio streaming, since usb-audio has gained much popularity over the years. I wouldn't be surprised if usb replaced spdif as digital audio interconnect over short distances. That in mind RPI4 with working UAC would be pluggable to other such devices.
I agree, if g_audio would work perfectly I would focus my effort on that. Phofman and I tried a few simple modifications to the code but there were still some problems. He is trying to see if anyone at alsa-devel can help him figure out how to implement some fixes in the UAC2 kernel module, etc. I am hopeful that sometime in the future, this will be the better way to go. For now, at least under Windows, it is not working and so my approach is the only viable one.

The world is full of streaming software for multiple platforms. If g_audio isn't working as expected, wouldn't it really be easier to use ethernet through LAN? Wouldn't that also make it easier to stream audio from multiple sources, like mobile devices?
Well, what I am doing is exactly that: streaming over ethernet. It's just that the particular ethernet connection I am using happens to be via the USB ethernet gadget.

Let me give you the background: I like to do DSP under Linux using LADSPA and Gstreamer. I wrote a complicated helper app for Gstreamer (it's a large script) that constructs pipelines based on used input files. It can stream to multiple locations and can implement DSP at the endpoints, e.g. on other small Linux boxes a la Raspberry Pi and that kind of thing. I have always wanted to do something similar under Windows, but my helper app is a bash script. Since I wanted to be able to playback audio locally, the WSL (Ubuntu under Windows) isn't helpful because you do not have any access to audio devices. Many people are comfortable with the concept of a DAC - an external USB device to which they send audio from their computer. When I learned that the Pi 4 could implement the USB ethernet gadget I thought "why not connect the Pi 4 like a DAC, stream audio to it, and do DSP on it before the D-to-A conversion?". That was the idea behind this project.

The nice thing is that what I have come up with can ALSO be used to stream over your LAN. It makes no difference to Gstreamer whether it is streaming over a local USB-ethernet connection, or to some other IP address of a machine on your LAN. As long as you can supply a fixed IP address, Gstreamer will send the audio there. On the RX machine, the exact same bash script can be used to receive the audio. Also, if you want to send from the Windows machine to multiple endpoints, and keep them in sync, it is a simple matter to add more endpoint IP addresses to the Gstreamer command. My Gstreamer helper app does all of this automatically, but it is not too difficult (once you understand Gstreamer) to hand write the pipeline to do what you want.

There are also other variation that I hope to explore in the near future. One of these is to set up Gstreamer's input to be a uPnP endpoint. You can then discover this on your LAN, e.g. from your phone or other device, and send audio to it. Gstreamer would turn around and stream it out over your LAN to the endpoint(s) that you give it. There are many interesting possibilities!
 
Does RPi HDMI really support 24-bit audio? Last time (Oct-2018) I asked the answer was no. Apparently the HDMI audio could on Pi could really support 24-bit PCM and some their developer wrote on github issue that they had added it but later removed because of some incompatibility issues with some device.

At least in 4.19 there isn't support for 24-bit format:
linux/bcm2835-pcm.c at rpi-4.19.y * raspberrypi/linux * GitHub
(they have oddly moved the driver under vc04_services).

This may be too early for 5.3:
linux/bcm2835-pcm.c at rpi-5.3.y * raspberrypi/linux * GitHub

No 24-bit support (yet).

There is this issue:
Alsa driver limited to 16-bit / 48kHz audio files * Issue #494 * raspberrypi/linux * GitHub
 
Last edited:
Status
This old topic is closed. If you want to reopen this topic, contact a moderator using the "Report Post" button.