Skip to content

fix: capture audio from apps that use nested helpers or render into one channel - #479

Open
itsmeyaw wants to merge 2 commits into
ronitsingh10:mainfrom
itsmeyaw:fix/application-audio-capture
Open

itsmeyaw wants to merge 2 commits into
ronitsingh10:mainfrom
itsmeyaw:fix/application-audio-capture

Conversation

@itsmeyaw

@itsmeyaw itsmeyaw commented Oct 27, 2026 •

Copy link
Copy Markdown

Summary

Two separate bugs together made some apps impossible to control: FineTune muted their original output and played back silence. Discord shows both bugs at once.

1. Nested helper apps showed up as their own app row

Chromium and Electron apps often play audio from a helper bundle inside the main app, for example Discord.app/Contents/Frameworks/Discord Helper (Renderer).app. NSWorkspace lists these helpers as running .app bundles, so FineTune treated them as top-level apps. The audio appeared under a separate "Discord Helper (Renderer)" row instead of "Discord".

 resolve app for audio process
   responsible PID → running app
-  accept if bundleURL ends in .app
+  accept if bundleURL ends in .app AND is not nested inside another .app
   else walk up the process tree (same check)

The new AudioProcessMonitor.isTopLevelAppBundle(_:) check is used in both lookups.

2. The input peak scan read only the first channel

The audio callback measures the peak of the tapped input. That peak drives the VU meter and the output gate, which stays closed until it detects sound. The scan stepped through interleaved samples by the channel count, so it read only channel 0. An app that renders into only one side of a stereo stream reads as silence, so the gate never opens. Because the tap is .mutedWhenTapped, the app's original output is muted too, and the user hears nothing.

 inputPeak(buffers)
   for each buffer
-    for i in stride(0, sampleCount, by: channels)   // channel 0 only
+    for i in 0..<sampleCount                        // every channel
       peak = max(peak, |sample[i]|)
   return min(peak, 1), frames

The scan moved into ProcessTapController.inputPeak(_:) (static, @inline(__always)) so it can be tested. It stays RT-safe: no allocations, locks or logging.

Evidence

Example case: Discord on macOS 27.0.1, playing sound to the built-in speakers.

  • Before: Discord appeared as "Discord Helper (Renderer)". After merging the rows, the Discord meter stayed flat, the slider had no effect, and Discord went silent while FineTune ran. Discord was audible again as soon as FineTune quit.
    A separate raw tap on the same process showed real audio on the right channel only:
    L=0.000000 R=0.542171 ch=2, while FineTune's own level for Discord stayed at 0.000000.
  • After: there is a single "Discord" row. Discord is audible, the meter moves, and the slider changes the volume (verified on hardware).

Regression tests (Swift Testing):

AudioProcessMonitorBundleTests
  topLevelAppIsReal            /Applications/Discord.app                         → true
  nestedHelperAppIsNotReal     …/Discord.app/Contents/Frameworks/…Helper….app    → false
  xpcServiceAndNilAreNotReal   *.xpc, nil                                        → false

InputPeakTests
  rightOnlyStereo   L=0, R=0.5 interleaved   → peak 0.5 (was 0.0), frames 256
  clamped           -3.0                     → peak 1.0

The full unit test suite passes (-skip-testing:FineTuneUITests).

Electron/Chromium helpers such as Discord.app/Contents/Frameworks/Discord Helper (Renderer).app
are registered with NSWorkspace as running .app bundles, so they were treated as top-level apps.
Discord's audio showed up under a separate "Discord Helper" row instead of "Discord".
Only bundles not nested inside another .app now count as real apps; helpers resolve to their
responsible parent.
The VU/output-gate peak only scanned the first channel of interleaved buffers. Apps that
render into one side of a stereo stream (Discord renders right-only) read as silence, so the
output gate never opened: the tap muted the app and FineTune played nothing.
Extract the scan into ProcessTapController.inputPeak and read every sample.
@sheeesh3

Copy link
Copy Markdown

hey, tried this locally on macos 27.0.1 and it fixed the issue for me.

some clips with audio only on the right channel were silent at my usual volume. turning it up made them audible but it kept cutting in and out.

same clips play fine now at the volume where they had no sound before.

been dealing with this daily so thanks for the fix! would be nice to see this merged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants