---
title: "Hearing the Whole Meeting: How an Empty AirPods Transcript Rewrote Two Releases"
description: "How Foxl fixed empty AirPods meeting transcripts: default system-audio capture on macOS, an inaudible tone probe that detects silently-dead captures, a native AVAudioEngine recorder for background recording on iOS, and ten native-app capabilities shipped in one release."
author: "Foxl Team"
date_published: "2026-07-21"
date_modified: "2026-07-21"
canonical_url: "https://foxl.ai/blog/hearing-the-whole-meeting"
markdown_url: "https://foxl.ai/blog/hearing-the-whole-meeting/index.md"
image: "https://foxl.ai/blog/one-session-per-device.png"
social_image: "https://foxl.ai/blog/social/hearing-the-whole-meeting.png"
content_type: "Deep dive"
topics: ["Audio capture","macOS permissions","iOS","Mobile UX","Reliability"]
products: ["Foxl Notes","Foxl Agent"]
---

<!-- Generated by scripts/blog-publishing.mjs; do not edit. -->

# Hearing the Whole Meeting: How an Empty AirPods Transcript Rewrote Two Releases

> One bug report - "I recorded a meeting with AirPods and the transcript came back empty" - led through a macOS permission that fails silently, an inaudible test tone, a GPL driver we chose not to bundle, a WebKit limitation with no escape hatch, a native Swift recorder, and a ten-item audit of what makes a phone app worth being an app. The story of v0.4.13 and v0.4.14.

- Author: Foxl Team
- Published: 2026-07-21
- Last reviewed: 2026-07-21
- Reading time: 11 minutes
- Canonical HTML: [https://foxl.ai/blog/hearing-the-whole-meeting](<https://foxl.ai/blog/hearing-the-whole-meeting>)

![Foxl v0.4.13 and v0.4.14: system-audio capture with an active self-test on macOS, a native background recorder on iOS, and ten native-app capabilities from share sheets to icon badges.](<https://foxl.ai/blog/one-session-per-device.png>)

Foxl v0.4.13 and v0.4.14 shipped a day apart, and most of what is in them traces back to one bug report: *"I recorded a meeting wearing AirPods, and the transcript came back empty."* Pulling on that thread took us through macOS permission archaeology, an inaudible test tone, a GPL license we decided not to touch, a WebKit behavior with no public escape hatch, and finally a native Swift recorder. Along the way we audited what actually makes a phone app feel like a phone app - and closed ten gaps at once. This post is the story of both releases, including the dead ends.

## The bug that speakers were hiding

Foxl Notes transcribes meetings. You hit record, it streams audio to a speech model, and a diarized transcript builds up live. It had worked for months - as long as your meeting audio came out of your laptop speakers.

The report said: with AirPods in and the mic muted, the recording captured nothing. Reading the recorder code made the reason plain. The pipeline captured **system audio** - the sound your Mac is playing, which is where the other participants' voices live - only when *live translation* was switched on. A plain recording captured the microphone alone. On speakers nobody noticed, because the other side's voices came out of the speakers, crossed the room, and re-entered through the mic. The feature looked like it worked because of an acoustic accident.

AirPods removed the accident. The remote voices played inside your ears, never touched the air near a microphone, and the "works on my machine" illusion collapsed. Worse: with the mic muted, the recorder kept streaming perfect digital silence to the transcription service, metering credits for audio that contained nothing at all.

## The fix that was already in the codebase

The satisfying part: the capture machinery already existed and already worked - live translation had used it for months. A single WebAudio graph mixes the microphone and the system audio into one stream, so the speech model hears each utterance exactly once and speaker diarization stays coherent. The bug was a gate, not a gap: the mixing pipeline only armed for translation.

So the fix was to make system-audio capture the default for every desktop recording, with a switch in Settings for anyone who really does want mic-only. One decision worth recording: we reuse the *single-pipeline* mix rather than transcribing mic and system audio separately. An earlier version of Foxl ran two parallel pipelines and, on speaker setups, the mic re-heard the remote party - every sentence transcribed twice. The one-graph design makes the double-hearing structurally impossible, which is why we didn't revisit it.

## macOS fails silent - literally

Then we hit the part that justified the rest of the release. Capturing system audio on macOS requires a permission called **System Audio Recording Only** - not Screen Recording, which is a different grant in the same settings pane. And macOS never prompts for it on its own. If the permission is missing, the capture API does not throw an error. It hands you a live, healthy-looking audio track that carries pure silence, forever.

Think about what that means for a meeting recorder: everything looks right - the timer runs, the level meters exist, the file saves - and forty-five minutes later you have a transcript of your own half of the conversation. The failure announces itself only after it has already cost you the meeting.

Our first instinct was a watchdog: listen to the captured track for twelve seconds, and if it never rises above the noise floor, warn. We built it, looked at it, and threw it away - a quiet meeting would false-alarm, and twelve seconds of "maybe" is not a diagnosis. What shipped instead is an **active self-test**: when a recording starts, Foxl plays a tone through your speakers at -60 dBFS - about a thousand times quieter than conversation, inaudible on any output at any volume - and checks whether that exact frequency comes back through the capture. The loopback path is purely digital, so a working capture must echo the tone within a couple of seconds, and a dead track never can. No guessing, no waiting. The tone sits at 1.5 kHz for an unglamorous reason: Bluetooth headsets drop to a low-bandwidth profile while their mic is engaged, and an ultrasonic probe would vanish in that mode and accuse a healthy capture.

When the probe hears nothing back, Foxl now says so - in onboarding, which gained a "Hear the whole meeting" step with a test button; in Settings, which shows a standing warning until the permission is granted; and at recording start, with a button that deep-links into the exact System Settings pane. The permission only needs granting once. The point is that you find out *before* the meeting, not after.

## The road not taken: a virtual audio driver

We seriously considered the approach many Mac audio tools use: bundle a virtual audio driver like BlackHole and route system audio through it. We decided against it, twice over. Legally, BlackHole is GPL-3.0 - shipping it renamed inside a proprietary app is a derivative work, which means either open-sourcing or negotiating a commercial license. Practically, drivers are heavy: an admin-password installer, an audio subsystem restart, a support surface that outlives the feature. The industry has been moving off this model - the current macOS APIs can tap system audio natively, which is exactly what Foxl's capture path rides. The tone probe was the missing piece, not the driver.

## The iPhone, where the rules are different

The same report had an iPhone-shaped shadow: recordings on iOS died the moment you switched apps or locked the screen. This one took us into WebKit internals. Foxl's iOS app renders the same React bundle as the web app inside a system web view - and iOS suspends a web view's microphone capture when the app leaves the foreground. Declaring the audio background mode, the documented way for an audio app to keep working, does not help: the suspension happens inside WebKit's media session handling, and there is no public API to opt out. The web layer cannot fix this from inside the web layer.

So the recorder left the web layer. v0.4.14 adds a native Swift recorder to the iOS app: an audio engine taps the microphone, resamples to exactly the format the transcription service wants, and streams the chunks across the bridge to the same JavaScript pipeline that handles sockets, transcripts, and billing on every other platform. Only the capture moved; everything downstream is shared code. With the audio background mode declared and a native engine running, iOS keeps the recording alive through backgrounding and screen lock, with the system's orange recording indicator showing - phone calls interrupt and resume cleanly, and swapping AirPods in or out mid-recording re-anchors the capture instead of going quiet.

What about capturing the *other side* of a call on iPhone - the thing the Mac now does? We researched it and are saying the honest thing: iOS has no equivalent. Capturing another app's audio requires a screen-broadcast extension that the user must start from Control Center every session, with a persistent recording banner. That is the platform working as designed - apps cannot silently listen to other apps - and a workflow that heavy would sell a promise the phone can't keep. On iPhone, Foxl records your side beautifully; for the whole meeting, the Mac is the right tool.

## Ten things a phone app should do

Fixing the recorder forced us to live in the iOS app for a week, and it sharpened a question: if the phone app renders the same bundle as the web app, what makes it *worth being an app*? We wrote down the ten capabilities a native app has that a browser tab doesn't, audited Foxl against the list, and found we had four. The rest of v0.4.14 is the other six.

**Share sheets.** Every chat reply now has a share button that opens the real iOS share sheet. Notes exports go through it too - which quietly fixes exporting on iOS, because the browser-style download the web build used simply discards the file inside an iOS web view. Now a transcript AirDrops to your Mac or lands in Files.

**An icon that tells the truth.** Replies that finish while you're away now mark their conversation unread, the app icon carries the count, push notifications set it, and opening the app clears both the badge and the pile of delivered notifications. The badge had never worked on mobile: the unread tracking that fed it lived only in the desktop code path.

**Quick actions.** Long-press the Foxl icon for New Chat or New Recording - the latter jumps into Notes and starts recording in one gesture.

**Links that go somewhere.** Foxl's sidebar has always offered "Copy link" for a conversation, producing a `foxl://` URL. On the phone, those links used to launch the app and then... nothing: the only deep link the app understood was the sign-in callback. Now conversation links, new-chat links, and record-now links all navigate properly, including from a cold start.

**A status bar that matches.** Switching Foxl between light and dark now flips the clock and battery icons to stay legible. It previously followed the system setting regardless of the app's theme - dark icons on a dark header if the two disagreed.

**Sheets you can flick away.** The model picker and Settings follow your finger on a downward drag and dismiss past a threshold - the gesture their grab handles always implied. This one shipped twice, because the first version had two honest bugs: the gesture listener attached to an element that a UI portal hadn't mounted yet, so the model picker never received a single touch; and on dismiss, clearing our drag styles mid-slide let the exit animation replay from the top, so the sheet visibly bounced back up before leaving. Both are the kind of bug you only find on a device, which is why we stage these builds before releasing them.

Rounding out the list: haptic feedback on the taps that matter (opening the sidebar, starting a chat, sending, picking a model), and the pieces that were already in place - push notifications with per-category settings, the native recorder above, and the keyboard docking work from v0.4.10. The phone app now clears the ten-item bar we set for it.

## Desktop kept pace

Two smaller desktop changes came out of the same week. Cmd+1, Cmd+2, and Cmd+3 now switch between Foxl Agent, Notes, and Code, listed in the View menu like any native app. And the menu-bar tray gained "New Recording", which brings up only the floating meeting panel - your main window stays wherever you left it. That floating panel also stopped taking seconds to appear: it was building its window from scratch on every open and tearing it down on every close. It now boots invisibly at startup and hides instead of closing, so opening it is instant, every time.

## What we'd tell you to take away

Three lessons from this pair of releases, none of them new but all of them re-earned. First: a feature that works by accident - speaker bleed standing in for system-audio capture - is a feature that fails silently the moment hardware changes. Second: when a platform fails without an error, *prove the path end-to-end* instead of trusting it; the inaudible tone probe is twenty lines of code that converts a forty-five-minute silent failure into a two-second diagnosis. Third: honesty beats parity. The iPhone will not capture the other side of a call, and saying so in the product is better than a Control Center ritual pretending otherwise.

## Upgrading

The web app at [app.foxl.ai](<https://app.foxl.ai>) updates automatically. The desktop app updates itself, or grab the latest from the [Foxl homepage](<https://foxl.ai/#download>) - the first recording after updating will ask once for the System Audio Recording permission. The iOS build is rolling out through TestFlight. The full list of changes is in the [changelog](<https://foxl.ai/changelog>).

## References and further reading

1. [Foxl v0.4.14 changelog](<https://foxl.ai/changelog>) - Release
2. [Download Foxl](<https://foxl.ai/#download>) - Reference