This is for you if:
- You want a phone's sound — a game, a video, a music app — to come out of your PC's speakers or headset, and you already listen to everything else there.
- Bluetooth to the PC adds latency and re-encodes the audio with a lossy codec, and the PC may not even have Bluetooth.
- A cable from the headphone jack into the PC's line-in hums, needs a free input, and most phones no longer have the jack.
- You already run Scream for a virtual machine or another computer and would like the phone to be one more Scream source.
- You have a NanoKVM sitting idle.
What it does
phone ──USB-C (UAC1 speaker)──▶ NanoKVM ──Scream UDP, wired LAN──▶ PC receiver ──▶ speakers
The NanoKVM's USB-C port is normally a USB device — keyboard, mouse and virtual disk for the computer it controls. The kernel can also present that port as a USB Audio Class 1 speaker. Android accepts it as wired headphones and routes all audio there.
On the NanoKVM, arecord reads what the phone plays (48 kHz, 16-bit, stereo) and a 40-line Python script wraps each 288 frames in a Scream header and sends it as one UDP packet. Any Scream receiver plays it: the Unix receiver from duncanthrax/scream, or pipewire-scream.
Nothing is resampled or re-encoded on the way. A 50-second capture produced 166.7 packets per second — exactly 48 kHz — and the sample values reach the PC as the phone produced them.
Setup
You need a NanoKVM reachable as root@<host> over ssh with a key, a Scream receiver on the PC listening on a UDP port (4015 below) that the firewall lets in from the LAN, and a USB-C cable from the phone to the NanoKVM.
git clone https://github.com/zirize/nanokvm-scream-bridge
cd nanokvm-scream-bridge
tools/install-nanokvm.sh <host> # copy two files to the device, once
KVM_HOST=<host> tools/kvm-mode audio <pc-ip>:4015 # switch to audio mode
KVM_HOST=<host> tools/kvm-mode status
KVM_HOST=<host> tools/kvm-mode kvm # back to the normal KVM
The mode switch takes effect immediately and survives a reboot: the destination is stored in /boot/usb.audio. To recover without the network, put the SD card in a computer and delete that file.
How it stays up
An init script, S04usbaudio, runs after the firmware's own S03usbdev (which it never modifies). It rebuilds the gadget as a single UAC1 function and starts a supervisor that
- restarts the capture pipe whenever it ends — phone unplugged, card gone after a rebind;
- checks every 5 seconds whether the NanoKVM web app has rebuilt the stock gadget (it does when you toggle virtual disk or network settings) and switches it back to audio.
Re-applying audio mode while it is already active does not re-enumerate the phone, so playback is not interrupted.
Things worth knowing
- Keep the phone at full volume. The phone scales the samples digitally; at low volume the stream peaked at −50 dBFS. Set loudness on the PC.
- When the gadget re-enumerates (reboot, mode switch) Android treats it as headphones being unplugged and pauses playback. Press play again.
- UAC1 on this kernel has no feedback endpoint: the phone's clock drives the stream and the receiver absorbs any drift.
- A phone on the USB-C port keeps the NanoKVM powered. To power-cycle it, remove both the phone and the power supply.
- The stock shutdown can hang in the NanoKVM's own service stop. Remount
/and/bootread-only over ssh before pulling power.
Other boards: Pi 4, Pi Zero 2 W, Pico W
Nothing here depends on the NanoKVM except the gadget setup. Any board with a USB device port can be the speaker. These are worked out from the hardware, not built yet:
| board | to the PC | phone-side wiring | software |
|---|---|---|---|
| NanoKVM (tested) | Ethernet | plain USB-C cable | this repository |
| Raspberry Pi 4 | Ethernet | USB-C power/data splitter | configfs gadget + the same uac2scream.py |
| Pi Zero 2 W | Wi-Fi | OTG cable to the data port | same as the Pi 4 |
| Pico W / Pico 2 W | Wi-Fi | OTG cable; the phone powers it | new firmware: TinyUSB UAC2 + UDP |
Pi 4: the USB-C port is the only one that can be a gadget, and it is also the power input. A phone cannot power a Pi 4, and two 5 V sources on one rail is asking for trouble, so a USB-C power/data splitter sits in between: the Pi's supply goes to one side, the phone's data to the other. If the splitter's data port has no CC line, an OTG 2-in-1 adapter on the phone provides it and lets a charger keep the phone topped up at the same time.
Pico W: no Linux, so it needs firmware of its own, but the parts exist: TinyUSB has UAC2 speaker examples (one with an asynchronous feedback endpoint), and the Scream stream is only about 1.5 Mbit/s of UDP over Wi-Fi. It draws so little that the phone can power it directly. Full details are in the hardware notes.
Why not a USB-to-USB box
The first idea was a box that is a USB speaker to the phone and a USB microphone to the PC. That needs two USB device controllers, and the NanoKVM and the Raspberry Pi 4 have one each. Two RP2040 boards linked by I2S, or an STM32 with two OTG cores, could do it bit-perfectly. Sending Scream to the PC needs only one controller and reuses receivers that already exist. The hardware notes cover that choice.
Tested on
A full NanoKVM (hardware beta, SG2002 RISC-V, firmware kernel 5.10.4) with one Android phone, sending to the Unix Scream receiver on a Linux PC. MIT licensed.