Hack On Self: Headphone Friend

In the last two articles, I talked about two systems relying on audio notifications. The first one is the Alt-Tab annihilator system – a system making use of my window monitoring code to angrily beep at me when I’m getting distracted. The other is the crash prevention system – a small script that helps me avoid an annoying failure mode where I run out of energy before getting myself comfortable for it.

I’ve been appreciating these two systems quite a bit – not only are they at my fingertips, they’re also pretty effective. To this day, I currently use these two systems to help me stay focused as I hack on my own projects or write articles, and they are definitely a mainstay in my self-hacking arsenal.

There is a particular thing I’ve noticed – audio notifications help a fair bit in a way that phone or desktop notifications never would, and, now I have a framework to produce them – in a way that calls for a purpose-tailored device. It’s just wireless headphones, Pi-powered, connected through WiFi, and a library to produce sounds on my computer, but it turns out I can squeeze out a lot out of this simple combination.

Here’s a pocketable device I’ve developed, using off-the-shelf hardware – an audio receiver/transmitter with extra IO, paired to my laptop. And, here’s how I make use of this device’s capabilities to the fullest.

Audio Output

In the “producing sound out of a Pi” article, I’ve mentioned USB-C 3.5mm soundcards. You can use them with a USB-C host port, and you don’t even need any sort of resistors for that – the soundcard doesn’t try and detect state of the CC pin, and why would it, anyway? Get VBUS, GND, D+, and D-, and you got yourself an audio card with high quality output.

I’ve also talked about the Roc toolkit – it’s a system for transferring audio over a network connection, whether LAN or WAN. It requires Linux/Mac/Android, so it fits wonderfully in my Windows-less ecosystem. I’ve been using it for years, on this kind of devices, and my friends use it with Android phones. Whenever I meet up IRL with some of my friends, at times, we might use a Roc sender on someone’s laptop to stream music or some YouTube video into everyone’s headphones at the same time.

This $10 Apple USB soundcard has been instrumental in many of my Pi audio projects – it’s just that good

A Roc receiver works wonderfully on a Pi Zero. It only made sense to marry Roc, a USB-C soundcard, and a Pi – they work wonders together. Here’s a script you can run on the Pi, coupled with an audio service, and the repo contains all the laptop-side commands you could need. You don’t need to install Pulseaudio for it or anything of the sort – it uses an Alsa card number, so as long as that remains static (very likely on a pocket system), you got it.

On the laptop side, I use pavucontrol to switch audio outputs – if your OS uses Pipewire, you can still use pavucontrol, and, you can also use qpwgraph if you ever want to route audio in a very specific way. It’s like Bluetooth headphones, except they work over WiFi, which avoids Bluetooth software nuances, antenna sharing issues, annoying pairing and battery level noises, audio quality limitations, and relatively short range, not to mention all the features I can add myself. And, the battery also works throughout the entire day – no need to take the headphones off to top them up every now and then, charging the device overnight is sufficient.

Bring The Sound Everywhere

What does this device let me do? First off, I can listen to music or videos even if I get up from my computer and go to a different room. This alone frees up a hefty amount of executive function – it’s way easier to get up from the desk and go cook some food while I am watching a video or a livestream, it just keeps playing in my ears all throughout, so I don’t have to feel like I’m missing out on something!

With Tailscale, or any other personal VPN accessible from the outside, I could also take this pocket device outside on a walk or cycling trip, connected to my phone in mobile hotspot mode, and listen through a queue of videos I was long planning to watch. Roc also let me pass the headset microphone back to my computer – which, I often use to have Discord calls with friends while going around the house and doing cleaning or other chores.

Another worthwhile addition is audio notifications, and the alt-tab annihilator audio library helps a fair bit. I already get audio notifications from some browser tabs, so I can get little beeps when someone from a select group of friends of mine messages me, and you could easily make a utility like beep to let yourself know when long-running shell scripts finish. Not to mention that you could definitely port a “beep every X minutes” script to it!

Now, you might notice – this device is output-only, and most of the tasks above could use some input capabilities. For instance, remote audio streaming could use volume control and media seek/switching – all the more so when I’m listening to my laptop’s audio while being ten kilometers away. I’m not a fan of voice commands, though you could definitely use those – for me, the headset’s single button was more than enough.

One Button For All Seasons

The USB-C soundcard has a USB HID endpoint, and it produces keypress events (for the PLAYPAUSE keycode) when I press the button – what’s more, it even keeps track of when the button is pressed and when it’s released! I’ve been working with HID devices a fair bit now – perhaps, I could extract multiple features out of that single button.

With just one button, you can make wireless multi-functional controls for anything you want

The main problem was, while the headset is connected to a Pi Zero in my pocket, the HID device is completely unused – it’s a CLI distribution of Raspbian, after all, no software would care for those keypresses. This wasn’t hard to fix – there’s two crucial elements to a HID device, first one is the descriptor, and another one is the report. Forward these aspects over the network using rawhid and uhid respectively , and you can have your device work natively over the network. I wrote a client-server application, ran the server on the pocket Pi, client on my laptop, and now I could use the headset button seamlessly as if the USB-C soundcard were connected to my laptop directly.

Now, I could pause or resume music of videos wherever I needed. I also hardcoded a feature into the server – restart the Roc streaming service once the HID device is connected, and restart it again once the device is unplugged. The Roc commandline receiver doesn’t exit on its own when an audio device disappears, instead, consuming 100% of the CPU, and it doesn’t restart by itself when the audio device disappears, either. Both of these problems were easy to solve as an aside of the HID forwarding server.

The single purpose of this button was a problem, though – not even volume controls would work. What can you do with a single button? A lot – if you can distinguish button press from a button release, and this soundcard sure can. If you’ve ever controlled your phone using headset button double presses or long presses, rejoice – reimplementing that is trivial; not that a three-button headset isn’t a more comfortable option still. I wrote a script distinguishing press length, and assigned different callbacks to different sequences.

From here, I can do a double click of the button, or a long press, or a long press followed by a double click, or long-short-long press – map different actions to different combinations and off we go. So far, I use it for volume control, seeking and pausing/resuming media, and poking some of my other self-hacking scripts remotely – but you can add features seamlessly, like running scripts of your choosing, reading out your desktop notifications through text-to-speech, setting timers, making notes of specific events in your life, or even combining it with the on-headset mic to record audio notes as you go.

Headphone Friend

All you need – a Pi Zero W, a power management board, and a USB-C host socket (no CC resistors, even!)

At this point, I have a pocket audio receiver device tied into my laptop over the local network, and, just as easily, Internet. While using it, it’s as inobtrusive as a pair of headphones paired to a wireless receiver, except that every single feature is used to the max. From a constant stream of audio, be it videos and music to notifications, to controlling my laptop remotely, this device is an augment like no other, codename’d “headphone friend” among my friends.

There’s another fun futuristic aspect to this build. With minimal modifications, it’s the kind of device I can take out of my pocket, connect to a USB-serial or USB-Ethernet adapter, wire it into a network switch in a rack, and then sit ten meters away from it on a comfy couch, reconfiguring the switch through its serial port. Or, I can hook this device onto a robot riding around, and collect telemetry through its debug port. Pair your Pi with a battery and a USB-C soundcard, and, you too can benefit from such a device – or accidentally build an even cooler platform while at it, after all, that’s how it worked out for me.

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.