SuvMusic v2.4.1.0
Release Overview
- Version: 2.4.1.0
- Build Date: May 09, 2026
- Target SDK: 36 (Android 15+)
- Status: Stable Release
Implementation Details
- Spotify-Style Volume Normalization: Replaced the old constant-gain shim with per-track perceptual loudness analysis. Each song's RMS is measured the first time it plays, cached locally, and used to apply a per-track gain offset (target ≈ -15 dBFS) so back-to-back tracks land at the same level — no more sudden jumps between a quiet ballad and a loud chart hit.
- Robust Volume Boost: Capped the boost slider at +12 dB (was +15) with combined-gain clamp [-8, +14] dB, plus softer limiter params (threshold -0.3, ratio 12, attack 1ms, release 80ms) so the limiter colours the signal less while still preventing clipping.
- Spatial Audio Intensity: New 'Spatial Intensity' slider (0–100%) drives both the static azimuth sweep and crossfeed strength independently of head-tracking sensitivity, so users on speakers or wired headphones now have a knob too.
- Audio Offload Now Honoured: The 'Audio Offload' toggle in Playback settings is finally respected — previously the service would auto-enable offload regardless of the user's preference whenever no software effect was active.
- Music Haptics from Real Audio: Replaced the synthetic 500ms beat with real-time RMS sampling from the native audio engine, plus an adaptive threshold (max of absolute floor and 60% of running peak) so haptics fire on quiet tracks and don't spam on loud ones. Toggling Music Haptics or changing mode/intensity now takes effect live without an app restart.
- Music Languages Dialog Wired: Clicking 'Music Languages' in Playback Settings now actually shows the selection dialog (it was orphaned before). Expanded to 23 languages with proper YouTube hl-code mapping for richer localized recommendations.
- Queue Reorder Polish: Bumped drag threshold so the row that snaps under the finger lines up cleanly after each swap (was visibly jumping ~8dp). Replaced the single-step
ifwith awhileloop tracking the working index locally so fast flicks walk through multiple rows instead of stalling on a stale captured index. Same fast-flick fix applied to the Playlist screen for consistency. - Swipe-Down-To-Dismiss Responsiveness: Lowered dismiss threshold from -10% to -5% of drag range so a confident flick reliably triggers it. Mini player now tracks the finger 1:1 with a fade as you pull down, and animates offscreen before stopping playback so the dismiss reads as motion rather than a hard cut. Toggle now takes effect live without restart.
- High-Resolution Artwork on Home: Threaded covers, song rows, and playlist headers across HomeSections, PlaylistCard, AlbumScreen, and PlaylistScreen through ImageUtils.getHighResThumbnailUrl with explicit Coil request sizes. Previously several AsyncImage calls passed raw w120/w160 URLs so YouTube returned tiny stamps that pixellated on mid/large surfaces.
- Instant Settings Account Header: Mirrored userName + userAvatar into encrypted prefs for synchronous read on entry. The Settings header now renders the current account immediately instead of flashing blank while DataStore reads complete.
- Robust Announce Songs: Rewrote TTSManager around UtteranceProgressListener with AudioAttributes(USAGE_MEDIA, CONTENT_TYPE_SPEECH), STREAM_MUSIC, and per-utterance audio focus so announcements actually route through Bluetooth/BLE headsets correctly. Added three settings — TTS volume %, music duck volume %, and Bluetooth-only switch — with sliders. Player volume now reliably restores in onDone/onError, and a try/finally fallback ensures the player isn't stranded ducked when cancellation happens (e.g., next track starting before announcement finishes).
- Bluetooth Autoplay Reliability: Debounce duplicate connect events between AudioDeviceCallback and ACL_CONNECTED, then verify play() actually started — some BT stacks miss the first call while the route is still settling, so retry up to three times with backoff.
- AI Equalizer Critical Fixes:
· OpenAI client was building a JSONObject and discarding it before sending a hand-templated string. Now uses proper JSONObject/JSONArray construction.
· Restored AI state on app restart was set on _lastResult but never pushed to the audio engine. Now applies on init AND snapshots the pre-AI engine state so A/B Compare works after restart.
· checkAndAutoApplyAIForSong took the 'before AI' snapshot AFTER applySettings — so Original and AI were identical. Snapshot is now captured first.
· Auto mode was hardcoded to ChatProxy with empty key, ignoring the user's configured provider/model. Now resolves provider+key+model from settings with a graceful fallback to ChatProxy when no key is configured.
· System prompt mislabelled limiterThresholdDb as 'Peak Level' and let the model wrap output in markdown. Reworded plus added an extractJsonObject() helper that strips ``` json fences across all four AI clients.
Contribution & Support
The development of SuvMusic is maintained through community support. If you find value in this project, consider supporting through the following channels:
- Support via Coindrop: https://coindrop.to/suvojeet_sengupta
- Direct Support (UPI):
suvojitsengupta21-3@okicici
Maintainer
Suvojeet Sengupta
Lead Android Developer
GitHub Profile
Email
This release was automatically generated as part of the SuvMusic continuous integration pipeline.