In 2024, Apple shipped something genuinely great: AirPods Pro can run a clinical-style hearing test and then act as hearing assistance, tuned to your ears. People love it. There's just one catch — it needs an iPhone to set up, recent AirPods to run, and if you're on Android you get nothing.
Meanwhile, the average pair of prescription hearing aids costs about $4,700, and surveys show a $1,500 device is simply out of reach for more than half the people who need one. There are a billion-plus Android phones out there, most of them sitting next to a pair of ordinary earbuds that already contain everything you physically need: a microphone, a DAC, and speakers.
The gap seemed absurd. So I've spent the past weeks building OpenHearing — a free, GPLv3 Android app that does the whole pipeline:
- Hearing check — a pure-tone test using the modified Hughson–Westlake staircase (the same adaptive up-down procedure audiologists use), per ear, per frequency. Or skip it and type in the numbers from a real audiogram.
- Sound profile — the results are fitted into a per-ear gain curve (half-gain rule for v1; NAL-NL2 is a pluggable strategy for later).
- Real-time assist — mic in, per-ear DSP, earbuds out. Quiet speech gets louder. Works with whatever earbuds you already own. No root, no special hardware.
It is very deliberately not a medical device — no diagnosis, no treatment claims, big disclaimer before anything plays a tone. Think of it as the open, inspectable "gateway" tier below real hearing care.
This post is about the three engineering decisions that turned out to matter most.
1. The safety-critical DSP is pure Kotlin — and that's the whole point
An app that amplifies sound directly into human ears has exactly one unforgivable failure mode: being loud when it shouldn't be.
So the entire signal chain is plain Kotlin with zero Android dependencies, hidden behind a tiny I/O interface. AudioRecord/AudioTrack is a dumb shell; everything that can hurt someone is JVM-testable:
input → EQ (your profile) → WDRC compression → feedback guard → master gain → LIMITER → output
The lookahead limiter is always the final stage, and its test suite is the release gate. My favorite test names in the repo:
output never exceeds the ceiling even for loud input-
master gain is hard-capped by safety limits(requesting +999 dB gets you the cap, not a lawsuit) live master gain change never exceeds cap or ceiling- NaN and Infinity in → silence out, never noise
When I later added live gain adjustment (changing volume while the audio thread is running), the change was a single @Volatile field read once per block — because the limiter downstream makes it impossible for a mid-stream change to produce anything dangerous. Safety as architecture, not as code review vigilance.
Every limit lives in one file, SafetyConstants, and nothing else in the codebase is allowed to define its own. 88 JVM tests run on every push without an emulator in sight.
2. Per-ear stereo from a mono microphone
Most hearing loss is asymmetric — your left and right ears usually differ. Phones have one usable mic. The fix is embarrassingly simple and I haven't seen other amplifier apps do it: duplicate the mono capture into interleaved stereo and run a completely independent DSP chain per channel, each with its own fitted gain curve and its own limiter.
class StereoAssistChain(
private val left: HearingAssistChain, // fitted to your left ear
private val right: HearingAssistChain, // fitted to your right ear
framesPerBlock: Int,
) : AudioProcessor { /* deinterleave → process → reinterleave, allocation-free */ }
Costs one extra chain of CPU (still trivial at 48 kHz mono-per-ear), buys correct per-ear correction.
3. Privacy you don't have to trust — you can check it
A hearing app is an always-listening microphone app, which is exactly the category people should be suspicious of. Promises are cheap, so OpenHearing makes the OS enforce the promise instead: the manifest declares no INTERNET permission. The app cannot phone home. Not "we don't collect data" — the platform will not let it transmit anything, ever.
aapt dump permissions OpenHearing-0.2.0-alpha01.apk
# RECORD_AUDIO, FOREGROUND_SERVICE(+MICROPHONE), POST_NOTIFICATIONS, MODIFY_AUDIO_SETTINGS
# ...and that's it. No INTERNET.
Any journalist, packager, or paranoid user can verify this in ten seconds. For health-adjacent software I think this should be table stakes.
The weirdest test harness I've written this year
The results screen draws an audiogram-style chart. To screenshot it honestly, I needed a completed hearing test — but the test is interactive and the emulator's mic is silent. Solution: since the Hughson–Westlake staircase is deterministic (start at 40 dB, drop 10 on "heard", rise 5 on "missed"), a shell script can mirror the staircase's internal state and simulate a listener with any hearing profile you want:
if [ $lvl -ge $target ]; then tap "Yes, I heard it"; lvl=$((lvl-10))
else tap "No, I didn't"; lvl=$((lvl+5)); fi
Ninety-seven taps later, the app believes it just tested someone with mild high-frequency loss, and the chart in the README shows realistic sloping curves instead of a flat line. Simulated patients as a UI test fixture.
An uncomfortable thing I learned about regulation
While writing the store copy I went through the FDA's hearing-device guidance, and one line stopped me cold: using "a hearing aid fitting formula to program the product output to match the user's hearing profile" is itself evidence that your product is a medical device — by design, regardless of your disclaimers. That's literally the pipeline described above.
The practical consequences for an open-source project are nuanced (enforcement has targeted commercial efficacy claims, and Apple went and got actual FDA clearance for exactly this software category), but it shaped every word of the app's copy: it's a "hearing check," not a diagnosis; a "sound profile," not a prescription; and it never, ever compares itself to hearing aids. If you're building anything health-adjacent, read the regulations before writing your marketing.
Honest status, and what I could use
This is an alpha. The full pipeline is built, 88 unit tests are green, every flow is verified on an emulator — and it has not yet been validated on real hardware, which for a real-time mic-to-earbud path means the two questions that decide everything (Bluetooth latency and feedback behavior) are still open.
If any of this interests you:
- 📱 Try it on real hardware — signed APK on GitHub, and there's a testers issue for reports. Start with low amplification.
- 🌍 Translate it — all strings just moved to
strings.xml; one file per language. - ⭐ Star it if you think open hearing assistance should exist: github.com/HMAKT99/OpenHearing
And if you know the Android audio stack better than I do — the issues tab is open, and I'd genuinely love to be told what I got wrong.
OpenHearing is a sound-amplification and hearing-assistance tool. It is not a medical device and not a substitute for a professional hearing exam. If you have concerns about your hearing, please see an audiologist.
Top comments (0)