Repository navigation
Conversation
UltraStar Play only supports mp3, ogg, and wav audio formats, but YouTube downloads typically produce AAC audio. This adds automatic conversion to OGG Vorbis (high quality VBR) when the extracted audio is in an incompatible format. Changes: - Add convert_to_ultrastar_format() in ffmpeg_helper.py - Integrate conversion into separate_audio_video() for YouTube downloads - Add conversion for local audio/video file inputs in UltraSinger.py Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixes case-sensitivity issue where uppercase extensions (.MP3, .WAV, .OGG) would bypass the compatibility check, causing unnecessary conversion and deletion of the original file. Reported by CodeRabbit in PR #1. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The FFmpeg afftdn noise reduction was hard-coded to 70dB (nr=70), far above the default of 12dB. This strips consonants, sibilants and transients from vocals, degrading both speech recognition accuracy and pitch detection quality. Changes: - Reduce default noise reduction from 70dB to 20dB - Make denoise parameters configurable via Settings and CLI args (--denoise_nr, --denoise_nf, --disable_denoise_track_noise) - Include denoise config in cache filename for proper cache invalidation - Add help text for new denoise options Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Normalize denoise_config cache key with consistent float formatting (:.1f) so that int defaults (20) and CLI floats (20.0) produce the same filename - Add range validation for --denoise_nr (0.01-97) and --denoise_nf (-80 to -20) with clear error messages Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ormat feat: auto-convert incompatible audio formats to OGG for UltraStar Play
Fix aggressive denoising (nr=70 -> nr=20)
The mute_no_singing_parts() function zeroes silent audio sections, which helps pitch detection (SwiftF0) focus on singing parts. However, these hard zero→audio transitions cause WhisperX's wav2vec2 forced alignment to lose sync, resulting in sudden timestamp jumps after non-singing sections. Split CreateProcessAudio() into two separate paths: - whisper_audio_path (_mono.wav): denoised mono without muting, used for Whisper transcription, BPM detection, key detection, and silence-based transcription cleanup - processing_audio_path (_mute.wav): muted audio, used only for pitch detection (SwiftF0) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove dead code: processing_audio_path assignment in CreateProcessAudio() was immediately overwritten in run() - Rename misleading parameter processing_audio_path → audio_path in transcribe_audio() to match new design - Add '_unmuted' suffix to transcription cache key so old caches (from muted audio) are automatically invalidated Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Separate audio paths for Whisper and pitch detection
* Fix beat rounding cascade and get_multiplier() off-by-one Three fixes to reduce timing drift in generated UltraStar files: 1. get_multiplier() off-by-one: The loop returned a multiplier that gave bpm*mult < 400 instead of >= 400 (e.g. 37.5*10=375 instead of 37.5*11=412.5). This reduced beat resolution and amplified rounding errors. 2. Beat rounding: Changed round() to floor() for start beats and ceil() for durations. round() caused ±0.5 beat errors that accumulated because the overlap guard (max) only shifts notes later, never earlier. floor() for starts prevents notes from being placed too late, ceil() ensures minimum 1-beat duration. 3. Redundant get_multiplier() call: multiplication and ultrastar_bpm were computed with two separate get_multiplier() calls instead of reusing the result. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix negative BPM guard and linebreak rounding inconsistency - get_multiplier(): reject negative BPM (would cause infinite loop) - Linebreak position: use math.floor() consistent with note starts Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Read fresh session cookies directly from a browser (chrome, firefox, edge, etc.) instead of requiring a manually exported cookies.txt file. This avoids the common issue of cookie files going stale after one or two YouTube downloads. - Add _cookie_opts() helper in youtube.py to centralise cookie option building; cookies_from_browser takes precedence over cookiefile. - Thread the new parameter through all yt-dlp call sites. - Add --cookies_from_browser CLI argument, help text, and interactive-mode prompt. - Update README with detailed authentication documentation. Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Audio chunks exported via --create_audio_chunks were extracted from the muted audio (_mute.wav), which contains hard zero transitions where silence was removed. This made the chunks useless for verifying what was actually sung. Switch to whisper_audio_path (_mono.wav) so chunks contain the original vocal audio. Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Add --bpm CLI option to override auto-detected BPM librosa.feature.tempo() can misdetect BPM (e.g. 378 instead of 340 for "Bring Me To Life"), shifting all beat positions in the output. This adds a --bpm flag that lets users provide the correct BPM manually, skipping auto-detection entirely. Changes: - Settings.py: add bpm_override field - UltraSinger.py: parse --bpm arg, skip get_bpm_from_file() when set - common_print.py: add --bpm to help text - init_interactive_mode.py: add BPM input to interactive mode Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix CodeRabbit findings for --bpm override - Wrap float(arg) in try/except for non-numeric --bpm values - Apply BPM override unconditionally (works with .txt input too) - Preserve CLI --bpm in interactive mode (skip prompt if already set) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Replace mode-based pitch selection with confidence-weighted median The previous approach converted frequencies to note names first, then picked the most common note (mode via Counter). This caused wrong pitch selection when pitch jitter spread frames across note boundaries — the mode could pick a rare outlier, and notes ended up 3-4 octaves off. Changes: - pitched_data_helper: return confidence weights alongside filtered frequencies; fall back to top 25% by confidence instead of ALL frequencies when none pass the threshold (reduces noise notes) - midi_creator: replace most_frequent()/convert_frequencies_to_notes() with confidence_weighted_median_note() that computes a weighted median in continuous Hz space before converting to a note name Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add empty-input guards for confidence_weighted_median_note Address CodeRabbit findings: validate that frequencies/weights are non-empty and length-matched before computing the weighted median. Guard the call site in create_midi_note_from_pitched_data to fall back to C4 when no valid frequencies are available. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Pitch detectors occasionally jump notes into wrong octaves (e.g. C2 instead of C4). This adds a post-processing step that compares each note's MIDI value to the median of its neighbours (window=5) and shifts it by octaves until it is within 11 semitones of the local median. The correction runs after MIDI segment creation and before syllable merging, so both the transcription and re-pitch pipelines benefit. Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Improve BPM detection with half/double-tempo correction librosa.feature.tempo() sometimes reports double or half the actual tempo. This change fetches multiple tempo candidates (aggregate=None) and applies half/double-tempo correction to prefer a value in the typical song range (60-200 BPM). For example, if librosa reports 378 BPM, the corrector will also consider 189 BPM (half) which falls in the normal range and is more likely correct. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Fix incorrect librosa tempo API usage per CodeRabbit review aggregate=None returns per-frame estimates, not ranked candidates. Simplified to use default aggregation (single primary BPM) and apply half/double correction on that single value. Also simplified _pick_best_tempo to accept a single float instead of an array. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Replace mean-based linebreak detection with percentile threshold The old deviation() function used a mean of silence durations (excluding the longest gap) to decide where to insert linebreaks. This produced poor results: evenly distributed pauses created no linebreaks at all, while a single long pause could skew the average and suppress linebreaks elsewhere. Replace with silence_threshold() using numpy's 75th percentile: only silences in the top 25% of duration trigger a linebreak. This is more robust against both uniform distributions and single-pause outliers. Also refactored calculate_silent_beat_length() into a thin wrapper around the new silence_threshold() function for better testability. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Address CodeRabbit review: None sentinel, clamp negative gaps, add tests - Change silence_threshold() sentinel from 0 to None for "too few gaps" to prevent false linebreaks when any positive intra-word pause exists - Guard the linebreak condition with `is not None` check so the entire condition is skipped when there are fewer than 5 gaps - Clamp overlapping segment gaps to 0 with max() before percentile calculation to avoid negative silences lowering the threshold - Add tests for short-input branch, few-segments wrapper, and negative gap clamping Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Add global octave correction for sub-harmonic pitch detection Pitch detectors (e.g. SwiftF0) sometimes lock onto a sub-harmonic, pushing all notes into an implausibly low register (e.g. MIDI 14-24 instead of 62-74). The existing correct_octave_outliers() only compares each note to its local neighbours and cannot detect such a global shift because all notes are consistently wrong. This adds correct_global_octave() which computes the median MIDI value of the entire song and shifts all notes by whole octaves if the median falls outside the expected vocal range (C3-C6 / MIDI 48-84). It runs before the local outlier correction in the pipeline. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Address CodeRabbit review: narrow exceptions, clean up imports - Narrow bare `except Exception` to `except (ValueError, TypeError)` in correct_global_octave() for safer error handling - Remove unused `correct_octave_outliers` import from tests - Move `numpy` import to file-level in test module - Add `strict=True` to zip() for defensive length checking - Remove copyrighted song name references from test docstrings Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Replace Unicode EN DASH with ASCII hyphen in docstrings Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Expand BPM detection with additional tempo-ratio candidates The previous half/double correction missed songs in triple or compound metre where librosa reports 3x or 4x the actual tempo. Expand _pick_best_tempo() to consider 10 common musical ratios (identity, half, double, third, triple, quarter, quadruple, two-thirds, dotted-half, three-quarters) with tie-breaking that favours simpler ratios. The function signature now accepts custom range and target parameters for flexibility. Add 21 unit tests covering edge cases, backward compatibility with the old half/double behaviour, and the new ratio candidates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Guard in-range tempos from ratio rescaling (CodeRabbit review) When the detected BPM already lies inside the acceptable range (default 60-200), return it unchanged. Ratio correction now only applies to out-of-range values. This prevents legitimate fast songs (e.g. 180 BPM) from being silently rescaled, which would break all downstream beat-length calculations. Also import _TEMPO_RATIOS in tests instead of hard-coding the list, and add explicit tests for in-range preservation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
The 75th-percentile default produced ~46% more linebreaks than manually-authored UltraStar files. Raising the threshold to the 85th percentile keeps only the longest 15% of inter-note gaps as linebreaks, which better matches human editing patterns. Existing regression tests now pass an explicit percentile=75 so they remain unaffected; two new tests verify the stricter default. Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add --bpm override option to README Document the --bpm CLI parameter in both the command-line reference block and as a dedicated section with usage example. This parameter was added in a previous PR but not yet documented. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: clarify --bpm accepts positive numbers and decimals Address CodeRabbit review finding: document that --bpm must be a positive number and that decimal values are supported. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Previously, CUDA PyTorch was replaced by CPU-only on every `uv sync` because no package index was configured. Install scripts now maintain a named PyTorch index in pyproject.toml (CPU default) and modify the URL in-place when switching between CPU and CUDA variants. Key changes: - pyproject.toml: Add [[tool.uv.index]] + [tool.uv.sources] for torch - Install scripts: sed/powershell to toggle CPU/CUDA index URL, then regenerate lockfile with `uv lock` before `uv sync` - Install scripts: Always remove old .venv for clean CPU↔CUDA switch - .gitignore: Add uv.toml (reserved for future local overrides) Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Widen BPM range to [50, 500] and remove target bias UltraStar uses BPM as a timing-resolution parameter, not as musical tempo -- higher values give a finer beat grid and more precise note placement. The previous range [60, 200] with target=120 aggressively downscaled detected tempos (e.g. 409 BPM -> 136 BPM), breaking timing alignment. Changes: - Widen acceptable range from [60, 200] to [50, 500] - Default target=None (use detected BPM as reference instead of 120) - Fallback for invalid input (<=0) always returns 120.0 - Values inside range pass through unchanged (no correction) - Out-of-range correction picks candidate closest to detected value Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Remove duplicate test for out-of-range fallback test_nothing_in_range_returns_primary duplicated test_very_low_bpm_prefers_closest_to_original (both tested _pick_best_tempo(10.0) == 10.0). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Exercise actual tie-break in test_simpler_ratio_wins_tie Use primary=600 with target=350 so half (300) and two-thirds (400) are equidistant from target — verifying that the simpler ratio (earlier in _TEMPO_RATIOS) wins the tie. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Add multi-pass octave outlier correction with global consensus Enhances correct_octave_outliers() with two improvements: Phase 1 - Multi-pass local correction: - Runs multiple passes (default 2) instead of one - Each pass rebuilds MIDI values from corrected notes - When multiple octave shifts are valid, prefers the one closest to the global median (tie-breaker) - Stops early when a pass makes no corrections Phase 2 - Global consensus correction: - After local passes, fixes clusters of wrong-octave notes that local correction missed due to contaminated local medians - Only activates when >50% of notes agree on an octave region - Shifts minority notes by whole octaves toward global median - Handles sub-harmonic detection clusters where 6+ consecutive notes are all one octave too low Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Address CodeRabbit review feedback for octave outlier correction Phase 1: Buffer corrections and apply after the scan instead of mutating mid-pass, which made results order-dependent. Phase 2: Raise the consensus skip threshold from 6 to 12 semitones so legitimate melody intervals (e.g. G4, a perfect fifth above C4) are never shifted. Only notes a full octave or more from the global median are candidates for consensus correction. Add two regression tests: one verifying G4 is preserved among C4 neighbours, and one exercising Phase 1 + Phase 2 cooperation on a mixed outlier/cluster scenario. Fix Ruff B007 (unused loop variable) and BLE001 (blind Exception). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Fix two issues that caused CUDA users to silently revert to CPU: 1. Windows PowerShell BOM bug: Set-Content -Encoding UTF8 writes a UTF-8 BOM (EF BB BF) that breaks TOML parsing. Switch to .NET File.WriteAllText which writes BOM-free UTF-8 by default. 2. Git operations reset: Branch switches, pulls, and checkouts would overwrite the local pyproject.toml CUDA index URL with the committed CPU default. CUDA install scripts now set git skip-worktree on pyproject.toml and uv.lock to protect local config. CPU install scripts clear the flag since CPU matches the committed default. Also fix: sed temp-file pattern replaced with in-place -i flag, and incorrect 'py' command replaced with 'python' on Linux/macOS. Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* Add format-aware UltraStar comparison tool Developer tool for evaluating generated UltraStar files against reference files. Key capabilities: - Auto-detects file format version (#VERSION header) and normalizes pitch values to MIDI (v1.2.0: pitch+48, legacy: raw MIDI) - Fuzzy word alignment using SequenceMatcher with tilde note grouping - Comprehensive metrics: pitch (median/exact/within-2ST), intervals, timing (excluding first word for GAP offset), lyrics precision/recall - CLI with --json and --detailed output modes 43 unit tests covering format detection, pitch conversion, beat-to-ms conversion, word alignment, tilde grouping, timing exclusion, interval format independence, JSON output structure, and edge cases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add docstrings to reach 80%+ coverage threshold Add missing docstrings to all public functions, classes, and test methods in compare_ultrastar.py and test_compare_ultrastar.py to satisfy the CodeRabbit docstring coverage requirement (was 58%, now above 80%). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address CodeRabbit nitpicks for robustness - Wrap int() conversions in note-line parsing with try/except to gracefully skip malformed lines instead of aborting - Use NFKD Unicode normalisation in word matching so accented characters (é→e, ü→u) are handled correctly for i18n songs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Address CodeRabbit feedback from PR #22: - Remove f-prefix from constant strings without placeholders (F541) - Harden _version_ge() for shorthand ("1.2"), suffixed ("1.2.0-beta"), and single-component ("2") version strings with zero-padding - Merge tilde continuation text into parent word so split words like "Hel" + "~lo" produce the group word "Hello" - Use same non-empty word filter for lyrics precision/recall denominators as align_words() uses for matching Tests: 48 passed (5 new, 2 updated) Co-authored-by: Anthony <anthony@baseattack.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Standalone tool that scans song directories for UltraStar TXT files and: - Converts incompatible audio formats (AAC, FLAC, OPUS, M4A, WMA, etc.) to OGG Vorbis via FFmpeg for UltraStar Play compatibility - Normalizes text encoding from legacy codepages (CP1252, Latin-1) to UTF-8 for universal compatibility across all UltraStar derivatives - Removes deprecated #ENCODING: tags during normalization - Supports --dry-run for safe preview of changes - Handles multi-encoding detection (UTF-8-sig, CP1252, Latin-1 fallback) - Updates all audio tags (#MP3:, #AUDIO:, #VOCALS:, #INSTRUMENTAL:) - Preserves original audio files (never deleted) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The tool now covers both audio format conversion and encoding normalization — the old name only reflected the audio aspect. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
Opened against wrong repo by accident. Moving to fork. |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (26)
📝 WalkthroughWalkthroughThis PR introduces BPM override functionality and denoising configuration options, refactors audio processing into separate whisper and pitch detection paths, replaces mode-based MIDI note detection with confidence-weighted median calculation, adds octave correction algorithms for MIDI consistency, and includes new UltraStar compatibility tools and comprehensive test coverage. Changes
Sequence Diagram(s)sequenceDiagram
actor User
participant UltraSinger as UltraSinger.py
participant AudioProc as Audio Processing
participant MidiDetect as MIDI Detection
participant OctaveCorr as Octave Correction
participant USWriter as Ultrastar Writer
User->>UltraSinger: Run with --bpm override & denoise opts
UltraSinger->>AudioProc: CreateProcessAudio()<br/>(returns whisper_audio_path<br/>+ pitch_audio_path)
AudioProc-->>UltraSinger: Two audio paths
UltraSinger->>AudioProc: TranscribeAudio(whisper_audio_path)
AudioProc-->>UltraSinger: Transcription + silence removal
UltraSinger->>AudioProc: get_bpm_from_file(whisper_audio_path)
Note over AudioProc: Apply _pick_best_tempo<br/>correction with ratios
AudioProc-->>UltraSinger: Corrected BPM
UltraSinger->>MidiDetect: create_midi_from_pitch(pitch_audio_path)
Note over MidiDetect: Use confidence_weighted_median_note<br/>instead of mode
MidiDetect-->>UltraSinger: midi_segments
UltraSinger->>OctaveCorr: correct_global_octave(midi_segments)
Note over OctaveCorr: Shift median into vocal range
OctaveCorr->>OctaveCorr: correct_octave_outliers()<br/>(two-phase: local + global)
OctaveCorr-->>UltraSinger: Corrected midi_segments
UltraSinger->>USWriter: create_ultrastar_txt()<br/>(with silence_threshold)
Note over USWriter: Calculate linebreaks<br/>using percentile-based threshold
USWriter-->>UltraSinger: UltraStar file
UltraSinger-->>User: Karaoke file generated
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
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. Comment |
Summary
Standalone tool (
tools/fix_ultrastar_compatibility.py) that scans UltraStar song directories and fixes two categories of compatibility issues:#MP3:,#AUDIO:,#VOCALS:,#INSTRUMENTAL:). Original audio files are preserved.--normalize-encoding): Converts legacy encodings (CP1252, Latin-1) to UTF-8 for universal compatibility across USDX, UltraStar Play, Performous, and Vocaluxe. Removes deprecated#ENCODING:tags.Features
--dry-runmode for safe preview of all changesUsage
Test plan
--dry-runon 18,287 songs → 60 audio conversions, 5,397 encoding fixes detected--normalize-encodingno encoding info shown0xFC→ UTF-80xC3 0xBC)🤖 Generated with Claude Code
Summary by CodeRabbit
Release Notes
New Features
Bug Fixes
Documentation
Tests
Chores