Skip to content

Add optional octave-snap: fold isolated single-note octave spikes onto the melody - #129

Merged
MrDix merged 1 commit into
mainfrom
feat/octave-snap
Jul 7, 2026
Merged

Add optional octave-snap: fold isolated single-note octave spikes onto the melody#129
MrDix merged 1 commit into
mainfrom
feat/octave-snap

Conversation

@MrDix

@MrDix MrDix commented Jul 6, 2026

Copy link
Copy Markdown
Owner

What

A toggleable post-pass (--octave_snap, GUI "Octave Spike Snap", off by default) that folds isolated single-note octave jumps back onto the melody: the occasional lone note a pitch tracker lifts or drops by an octave while its neighbours stay put — jarring to sing and read.

Why conservative — validated, not guessed

I prototyped this against the 8 professional reference songs and measured note range, median octave offset vs the reference, and how many notes moved. Findings that shaped the design:

  • A naive per-note snap (fold anything far from a windowed median) tightens ranges but destroys legitimately wide-range songs (one reference spans 42 semitones; the naive version folded it to 23) and can worsen others.
  • The shipped version only folds a note that is (a) a local extremum above/below both immediate voiced neighbours, (b) in a stable context (the two neighbours agree within a few semitones), and (c) about an octave away, landing within a small residual of the local line. On the 8 songs this regressed zero while removing the isolated spikes on the songs that had them.

Because octave is scoring-irrelevant (the ptAKF scorer folds octaves), this never changes the game score — it is purely a display/singability polish.

Scope / honesty

This fixes isolated spikes. It does not fix a whole passage that is consistently an octave off (2 of the 8 songs have that) — that is a deeper pitch-tracking problem (octave continuity on the raw f0), out of scope here and called out in the docs.

Surfaces (kept in sync)

CLI --octave_snap, GUI toggle in Post-Processing, config default, runner arg, CLI help, README, and tooltip — all documenting that it is conservative, score-neutral, and does not fix whole-section shifts.

Tests

7 unit tests (isolated up/down spike folded; genuine leap, small interval, unstable context, wide-range all untouched; pitch class preserved; short-input no-op). Full suite 858 passed / 4 skipped.

Default: off (opt-in) for now — happy to revisit the default per your call.

@MrDix

MrDix commented Jul 6, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@MrDix, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 4 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2fb93b65-98a1-453a-b6f0-f6f53943517d

📥 Commits

Reviewing files that changed from the base of the PR and between fa97aec and 6a77b56.

📒 Files selected for processing (9)
  • README.md
  • pytest/modules/Midi/test_octave_snap.py
  • src/Settings.py
  • src/UltraSinger.py
  • src/gui/config.py
  • src/gui/settings_tab.py
  • src/gui/ultrasinger_runner.py
  • src/modules/Midi/midi_creator.py
  • src/modules/common_print.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/octave-snap

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.

@MrDix
MrDix force-pushed the feat/octave-snap branch from 54b3d4d to 6a77b56 Compare July 6, 2026 23:56
@MrDix
MrDix merged commit 6777c3c into main Jul 7, 2026
2 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.

1 participant