Skip to content

Type your own sound card buffer size, used by the mic too - #35

Merged
epell-development merged 1 commit into
mainfrom
claude/project-thread-1z389x
Nov 1, 2026
Merged

epell-development merged 1 commit into
mainfrom
claude/project-thread-1z389x

Conversation

@epell-development

Copy link
Copy Markdown
Owner

Requested by epell · project thread

Closes #34

What changed

  • Settings → Audio → Sound card buffer keeps its presets (Low delay 40 ms, Normal 80 ms, Slow 150 ms, Very slow 300 ms) and adds a box where you can type or drag any value from 1 to 1000 ms. The sound cards reopen once the typing or dragging ends, never every frame.
  • The size is turned into frames at the card's own rate and clamped to the range the driver reports. If the driver refuses it, its own default is used, as before.
  • The device report under the outputs now shows what each output really got, for example playing (primary, 144 frames, about 3.0 ms), or that the driver refused the size. Drivers often round or have a floor (WASAPI shared mode is usually about 10 ms), so this shows what you actually got.
  • Mic input asks for the same buffer when a custom or preset value is set. On Normal it keeps the driver's default, as before. The change applies the next time the mic is turned on.
  • Mic delay can no longer creep up: the mic queue used to be allowed to grow to 1 s when the input and output clocks drift. It is now capped at about 4 buffers (60 ms to 1 s, never less than 3 of the input's own chunks), with one small skip when it overflows.

ASIO (not in this PR)

cpal has an asio feature, but turning it on is a bigger job than a setting:

  • The build needs Steinberg's ASIO SDK (CPAL_ASIO_DIR) plus LLVM/clang for bindgen on the Windows CI runner and on any Windows dev machine. The SDK has its own license (Steinberg proprietary or GPLv3), which needs checking against RadioMaster's MIT license before we ship binaries built with it.
  • ASIO is a separate cpal host. Device listing, the output/input pickers, the watchdog and the preview player would need a host choice (WASAPI or ASIO). ASIO also usually allows one open device per driver, and it has a different idea of sample rate and buffer control (it is set in the driver's control panel), so the multi-output and monitor model needs a rule for that.
  • It can't be tested here (Linux cloud session, no ASIO hardware), so it needs someone with an interface to try it.

Remaining limit for very low settings

The mixer renders in fixed 1024-frame blocks (about 23 ms). With a 3 ms buffer the card's own delay goes down, but a sound through the software (the mic, or pressing Play) can still wait up to one block. Smaller mix blocks would need the stream/server taps and monitor channels re-sized (their queues are counted in blocks) and the meter decay rescaled. That is a sensible follow-up, but it was kept out of this change.

Checks

  • cargo fmt, cargo +1.99.0 clippy --all-targets -- -D warnings, cargo test (new tests for the buffer clamp and frame maths) all pass
  • tools/extract_strings.py regenerated lang/template.json; tools/check_glyphs.py passes
  • Not tested on real audio hardware

Generated by Claude Code

The Sound card buffer setting keeps its presets and gets a box to type any
value from 1 to 1000 ms. The devices reopen once the typing or dragging is
done, never every frame. The mic input now asks for the same buffer, and the
mic queue is capped to a few buffers so its delay can't creep up to a second
when the input and output clocks drift. Settings shows the buffer each
output actually got (frames and ms), or that the driver refused it.

Closes #34

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016m2QHYB4iztqZ2rQPnXw76
@epell-development epell-development self-assigned this Nov 1, 2026

@mergeguards mergeguards Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

MergeGuard review
Risk: 🟠 35/100 — moderate
Findings: 🟠 1 medium
Open Files changed for 1 inline review thread on the diff.

💡 Use the Fix All MergeGuard Findings checkbox comment on this PR to apply auto-fixes in batch, or reply @mergeguards fix on any inline finding.


Reviewed by MergeGuard AI

Comment thread src/engine.rs
while s.mic_buf.len() > SR as usize * 2 {
s.mic_buf.pop_front();
// never less than a few of the input's own chunks, whatever size its driver settled on
let max = s.mic_max.max(samples.len() * 3).min(SR as usize * 2);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🛡️ MergeGuard · 🟠 medium · bug

Bitwise AND operation on potentially odd-sized sample buffer. Line 740 uses samples.len() * 3 which may be odd, then .min(SR as usize * 2) which is even (SR=48000, so 96000 samples). The subsequent line 743 applies & !1 to ensure even count, but the intermediate max value could be odd, leading to inconsistent mic_buf length tracking.

💡 Ensure max is always even by applying & !1 after computing it: let max = (s.mic_max.max(samples.len() * 3).min(SR as usize * 2)) & !1;

Reply with @mergeguards fix to apply an auto-fix on this line.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

I traced this and the queue length stays even, so I'm leaving the line as it is. Here's why:

  • samples always holds interleaved stereo pairs: mic::build pushes two values for every frame, so samples.len() is even and samples.len() * 3 is even too.
  • SR is 44 100 here, so SR * 2 is even.
  • mic_max is already rounded down to even in set_mic_latency_ms.

So max is always even. Even if it weren't, drop = (len - max / 2) & !1 is what decides how much gets drained, and it is even whenever len is. mic_buf therefore always gets drained by an even count, and left and right never swap.


Generated by Claude Code

@mergeguards

mergeguards Bot commented Nov 1, 2026

Copy link
Copy Markdown

MergeGuard agent actions

Fix all findings

All issues (1)
  • medium · src/engine.rs:740 — Bitwise AND operation on potentially odd-sized sample buffer. Line 740 uses samples.len() * 3 which may be odd, then .min(SR as usize * 2) which is even (SR=48000, so 96000 samples). The subseq…

Apply auto-fixes for the findings above on this branch:

  • Fix All MergeGuard Findings

Or reply with @mergeguards fix-all on this pull request.

@epell-development
epell-development merged commit ae331e8 into main Nov 1, 2026
6 checks passed
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.

Latency Settings

2 participants