Skip to content

fix: mix L/R channels when converting system audio to mono#215

Open
werpoz wants to merge 1 commit intosohzm:masterfrom
werpoz:fix/mix-stereo-mono
Open

fix: mix L/R channels when converting system audio to mono#215
werpoz wants to merge 1 commit intosohzm:masterfrom
werpoz:fix/mix-stereo-mono

Conversation

@werpoz
Copy link
Copy Markdown

@werpoz werpoz commented Feb 20, 2026

What

This PR fixes mono conversion in the macOS system-audio path so both stereo channels are used.

Why

Root cause: convertStereoToMono() was reading only the left channel and discarding the right channel.
On some setups, useful signal is mostly on the right channel, which caused very low/empty audio input.

Change

Updated convertStereoToMono() in src/utils/gemini.js to downmix stereo frames as:
mono = round((left + right) / 2)

Scope

  • File: src/utils/gemini.js
  • Function: convertStereoToMono
  • Commit: 97f23eb

Validation

  • Verified code path in macOS SystemAudioDump flow.
  • Confirmed only this change is included (isolated fix).

Risk

Low. Change is localized to stereo->mono conversion and does not alter IPC/session flow.

Summary by CodeRabbit

  • Bug Fixes
    • Enhanced stereo to mono audio conversion to properly blend both channels for improved audio quality, replacing the previous method that used only a single channel.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 20, 2026

📝 Walkthrough

Walkthrough

Modified the convertStereoToMono function in src/utils/gemini.js to average both left and right audio channels instead of using only the left channel, improving mono audio conversion quality.

Changes

Cohort / File(s) Summary
Audio Processing Utility
src/utils/gemini.js
Updated convertStereoToMono to read both left and right channels, average them, and write the resulting mixed sample to the mono buffer instead of using only the left channel.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

Poem

🐰 A stereo song now sings in mono grace,
Left and right channels embrace,
Averaged together with care,
Audio balanced, perfectly fair!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: mixing left and right channels when converting stereo to mono audio, which is the core fix in the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/utils/gemini.js (1)

743-745: Optional: guard against non-multiple-of-4 buffer lengths.

convertStereoToMono is exported and called by external paths. If stereoBuffer.length is not a multiple of 4, samples is fractional, the loop runs one extra iteration, and both readInt16LE and writeInt16LE will throw a RangeError at the boundary. Current callers always pass CHUNK_SIZE = 9600 (a multiple of 4), so this is safe today, but a small guard would make the function robust against future callers.

🛡️ Proposed defensive guard
 function convertStereoToMono(stereoBuffer) {
-    const samples = stereoBuffer.length / 4;
+    const samples = Math.floor(stereoBuffer.length / 4);
     const monoBuffer = Buffer.alloc(samples * 2);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/utils/gemini.js` around lines 743 - 745, convertStereoToMono currently
assumes stereoBuffer.length is a multiple of 4 which can cause RangeError when
callers pass other sizes; update the convertStereoToMono function to guard
against non-multiple-of-4 lengths by computing samples =
Math.floor(stereoBuffer.length / 4) (or truncating/slicing the input to samples
* 4) before allocating monoBuffer and entering the readInt16LE/writeInt16LE
loop, and ensure any extra trailing bytes are ignored or handled safely so
readInt16LE/writeInt16LE never read past the buffer end.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/utils/gemini.js`:
- Around line 743-745: convertStereoToMono currently assumes stereoBuffer.length
is a multiple of 4 which can cause RangeError when callers pass other sizes;
update the convertStereoToMono function to guard against non-multiple-of-4
lengths by computing samples = Math.floor(stereoBuffer.length / 4) (or
truncating/slicing the input to samples * 4) before allocating monoBuffer and
entering the readInt16LE/writeInt16LE loop, and ensure any extra trailing bytes
are ignored or handled safely so readInt16LE/writeInt16LE never read past the
buffer end.

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.

1 participant