Skip to content

feat(desktop-gpui): Clean Up tab, silence skipping, and preview speed - #2774

Open
flyingwebie wants to merge 5 commits into
CapSoftware:mainfrom
flyingwebie:feat/gpui-clean-up-and-preview-speed
Open

flyingwebie wants to merge 5 commits into
CapSoftware:mainfrom
flyingwebie:feat/gpui-clean-up-and-preview-speed

Conversation

@flyingwebie

@flyingwebie flyingwebie commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Clean Up tab, silence skipping, and a pitch-preserving preview speed for the GPUI editor. Rebuilt on the current main.

The two small fixes that used to be in this PR are now separate, as asked in review:

The playback-rate engine in crates/editor overlaps with #2780 (J/K/L shuttle). This PR is on hold for that decision; I can rebase onto whichever engine you pick.

Commits

  1. feat(editor): add a preview playback rate with pitch-preserving audio (audio.rs, audio_output.rs, playback.rs, editor_instance.rs).
  2. feat(desktop-gpui): add Clean Up tab, silence skipping, and preview speed menu. The Clean Up tab detects silences (kernels/src/silence.rs) and cuts them from the timeline. Playback can skip silences, with silence marks on the timeline, and a speed menu sets the preview rate.
  3. fix(desktop-gpui): bound the Clean Up envelope against out-of-range clip offsets.
  4. refactor(editor): return to 1x passthrough and hold the clock at 1x while preparing (review feedback):
    • When the rate goes back to exactly 1.0, TimeStretcher::resume_passthrough moves the source back to the last emitted sample and flushes. 1x playback is bit-identical again after any speed change. New test: returning_to_unit_rate_resumes_bit_identical_passthrough.
    • The playback clock stays at 1x while adopted preparing audio owns playback, and reads the rate at the handoff.
    • Trimmed doc comments that restated the code.
  5. perf(desktop-gpui): show waveforms before Clean Up analysis and lock speed while preparing (review feedback):
    • load_editor_waveforms calls set_waveforms as soon as the peaks are ready. The Clean Up loudness envelopes are computed afterwards. The tab shows "Analyzing audio…" until they arrive.
    • While the editor is preparing, the speed menu is disabled and the playhead runs at 1x.

Benchmark at 1x

editor-playback-benchmark on cap-performance-fixtures/reference-recording.cap, --fps 60 --frames 300 --resolution half --headless, release build, Apple M4. Before = main at d16fd19, after = this branch. 11 alternating runs each, medians (p95 in brackets):

before after
Frames rendered / dropped / skipped (all runs) 3300 / 0 / 0 3300 / 0 / 0
Effective FPS 59.1 59.1
Schedule overshoot 2.47 ms (3.93) 2.62 ms (3.92)
Callback gap 16.69 ms (20.32) 16.70 ms (20.20)
Render steady 3.97 ms (6.31) 4.02 ms (6.27)
Press-to-clock 86.2 ms 87.5 ms

Press-to-clock moves in ~11 ms prefetch steps and has the same spread on both sides (75–150 ms before, 76–140 ms after).

Testing

  • cargo test -p cap-editor --lib: 209 passed, 0 failed
  • cargo clippy -p cap-editor --all-targets: clean
  • cargo test --bin cap-gpui in apps/desktop-gpui: 1264 passed, 0 failed
  • cargo fmt --all clean for the touched files

🤖 Generated with Claude Code

@vercel

vercel Bot commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

@flyingwebie is attempting to deploy a commit to the Cap Software Inc Team on Vercel.

A member of the Team first needs to authorize it.

@flyingwebie

Copy link
Copy Markdown
Contributor Author

@greptileai review

@richiemcilroy

Copy link
Copy Markdown
Member

@flyingwebie thanks for rebuilding this on main and dropping the fork workflow, the Clean Up flow looks really well thought through. A few things before we can take it: 1) Please split it into separate PRs: the multi-display placement fix (global_macos_work_area in app_windows.rs), the Studio Sound export fallback (export_audio.rs), and the playback rate plus Clean Up feature. The two fixes are small and can land on their own quickly. 2) #2780 (J/K/L shuttle) adds a competing EditorInstance::set_playback_rate and WSOLA stretcher in crates/editor, and the two conflict in playback.rs and audio_output.rs. We can only take one engine implementation, so we will come back to you on which one to build on before you spend more time on that commit. 3) load_editor_waveforms (app_windows.rs around line 5498) now computes the Clean Up loudness envelopes before set_waveforms, so every editor open waits for that extra pass before waveforms appear, even if Clean Up is never used. Please set the waveforms first and compute the envelopes afterwards, or lazily when the Clean Up tab opens. 4) install_progressive_source pins the preparing audio path to 1.0 while the video clock in playback.rs already follows the rate, so changing speed during that phase drifts audio and video apart. Either keep video at 1x until the ordinary source takes over or disable the speed menu while preparing. 5) Once a non-1x rate is used, TimeStretcher stays on the WSOLA path at 1x for the rest of the session. Could it go back to passthrough after a flush() when the rate returns to exactly 1.0, so 1x is always bit-identical? 6) Per the comments policy in AGENTS.md, please trim doc comments that restate the code, e.g. on MIN_PLAYBACK_RATE/MAX_PLAYBACK_RATE, set_playback_rate/playback_rate(), PlaySpec::playback_rate, Playback::playback_rate, rate_adjusted_frame_deadline, frames_elapsed_at_rate and the long TimeStretcher header. Keep only the non-obvious why, like the Studio Sound preroll note. 7) Since the rate work touches the playback clock and audio callback, please include editor-playback-benchmark results at 1x before and after showing no change.

flyingwebie and others added 5 commits October 21, 2026 10:21
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… speed menu

Clean Up detects silences in the recording and cuts them from the timeline
(detection lives in kernels/src/silence.rs). Playback can skip silences, with
silence marks shown on the timeline, and a preview speed menu sets the
pitch-preserving preview rate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e clip offsets

Mic and system audio offsets come straight from project-config.json. A large
negative offset grew the combined silence envelope without limit, allocating
and scanning a huge buffer as soon as the editor opened. Offsets are now
clamped and the envelope never grows past the longest track.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…x while preparing

- TimeStretcher goes back to passthrough when the rate returns to exactly 1.0:
  the source is moved back to the last emitted sample and the stretcher is
  flushed, so 1x playback is bit-identical again after a speed change.
- The playback clock stays at 1x while adopted preparing audio (which has no
  stretcher) owns playback, and picks the rate up at the handoff.
- Trim doc comments that restated the code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ck speed while preparing

- Editor waveforms are set as soon as their peaks are ready; the Clean Up
  loudness envelopes are computed afterwards, so opening the editor no longer
  waits for them.
- The preview speed menu is disabled, and the playhead uses 1x, while the
  editor is still preparing, since that audio path only plays at 1x.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@flyingwebie
flyingwebie force-pushed the feat/gpui-clean-up-and-preview-speed branch from 42da475 to 4d8565a Compare October 21, 2026 09:34
@flyingwebie flyingwebie changed the title feat(desktop-gpui): Clean Up tab, silence skipping, preview speed, and two fixes feat(desktop-gpui): Clean Up tab, silence skipping, and preview speed Oct 21, 2026
@flyingwebie

Copy link
Copy Markdown
Contributor Author

@richiemcilroy thanks for the detailed review. All points are addressed and the branch is rebased on the current main.

  1. Split. The two fixes are now their own PRs: fix(desktop-gpui): place windows correctly on displays above or below the primary #2811 (multi-display placement, global_macos_work_area) and fix(editor): fall back to in-memory audio for short Studio Sound cuts #2812 (Studio Sound export fallback, export_audio.rs). This PR now only has the playback rate and Clean Up.
  2. feat(gpui): J/K/L playback speed shuttle #2780. Understood. I'll wait for your call on which engine to build on and won't do more on the rate commit until then. If you go with feat(gpui): J/K/L playback speed shuttle #2780's set_playback_rate and stretcher, I'll rebase the Clean Up and speed menu onto it.
  3. Waveforms first. load_editor_waveforms now calls set_waveforms as soon as the peaks are ready. The Clean Up envelopes are computed in a second background pass afterwards. The Clean Up tab shows "Analyzing audio…" until they arrive.
  4. Preparing phase. Both sides are covered. In playback.rs the clock stays at 1x while adopted preparing audio owns playback, and it reads the rate at the handoff. In the GPUI editor the speed menu is disabled, and the playhead extrapolates at 1x, while the editor is preparing.
  5. Passthrough at 1x. When the rate returns to exactly 1.0, TimeStretcher::resume_passthrough moves the source back to the last emitted sample and calls flush(), which now also clears active. After that, fill is a direct passthrough again, so 1x is bit-identical. New test: returning_to_unit_rate_resumes_bit_identical_passthrough.
  6. Comments. I removed the doc comments on MIN_PLAYBACK_RATE/MAX_PLAYBACK_RATE, set_playback_rate/playback_rate(), PlaySpec::playback_rate, ActiveSource::playback_rate, Playback::playback_rate, rate_adjusted_frame_deadline, frames_elapsed_at_rate and the WSOLA constants. The TimeStretcher header is now four lines. I kept the Studio Sound preroll note and the short "why" notes on latency scaling and the >1x prefetch boost.
  7. Benchmark at 1x. editor-playback-benchmark on reference-recording.cap (--fps 60 --frames 300 --resolution half --headless, release build, Apple M4). Before = main at d16fd19, after = this branch. 11 alternating runs each, medians (p95 in brackets):
before after
Frames rendered / dropped / skipped (all runs) 3300 / 0 / 0 3300 / 0 / 0
Effective FPS 59.1 59.1
Schedule overshoot 2.47 ms (3.93) 2.62 ms (3.92)
Callback gap 16.69 ms (20.32) 16.70 ms (20.20)
Render steady 3.97 ms (6.31) 4.02 ms (6.27)
Press-to-clock 86.2 ms 87.5 ms

Press-to-clock moves in ~11 ms prefetch steps and has the same spread on both sides. I used --headless so the runs don't play through the speakers; it drives the same render_source_block path at realtime pace.

Tests: cargo test -p cap-editor --lib 209 passed, cargo clippy -p cap-editor --all-targets clean, cargo test --bin cap-gpui 1264 passed.

flyingwebie added a commit to flyingwebie/Cap that referenced this pull request Oct 21, 2026
🔀 chore: sync upstream main and the PR CapSoftware#2774 review changes

This branch has not been deployed

No deployments
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