···11+# Changelog
22+33+All notable changes to this project will be documented in this file.
44+55+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66+77+## [2026.04.28-1]
88+99+### Added
1010+- Real-time PCM loudness normalizer (`normalize_volume = true` in `settings.toml`) — RMS-based AGC with asymmetric attack/release, similar to Spotify's "Normalize Volume"; applied across all PCM sinks (SDL, FIFO, AirPlay, Squeezelite, UPnP, Chromecast, Snapcast TCP)
1111+- `GET /player/volume` REST endpoint returning `{ volume, min, max }`
1212+- `volume` GraphQL query returning live current volume with min/max range
1313+- `useGetVolumeQuery` GraphQL hook in the web UI
1414+- `get_current_volume()` gRPC client helper in the GPUI app
1515+1616+### Fixed
1717+- Volume slider in GPUI mini-player now responds to mouse clicks (replaced plain `div` with `SeekBar` component)
1818+- Volume slider in web UI now uses correct 0–100 range with explicit `min`/`max` on the MUI Slider
1919+- `globalSettings.volume` in GraphQL now returns the live current volume via `rb::sound::current(0)` instead of a hardcoded `0`
2020+- `VOLUME_MIN_DB` constant in GPUI corrected from `-74` to `-80` (SDL target range)
2121+- Volume in GPUI loads the live value at startup via `SoundCurrent` gRPC instead of the stale saved setting
2222+- `adjust_volume` now has audible effect on all non-SDL PCM sinks (FIFO, AirPlay, Squeezelite, UPnP, Chromecast, Snapcast TCP) — SW volume scaling (`pcm_copy_buffer`) was not being applied in any of these sinks
2323+2424+## [2026.04.28]
2525+2626+### Added
2727+- Snapcast TCP PCM sink (`audio_output = "snapcast_tcp"`) — streams S16LE PCM directly to a Snapcast `tcp://` source; compatible with snapserver v0.35+
2828+- Stream metadata forwarding for Snapcast TCP sink
2929+3030+### Fixed
3131+- MPD `getvol` / `setvol` handlers now correctly map the Rockbox dB range (−80..0) to the MPD 0–100 scale
3232+3333+## [2026.04.27]
3434+3535+### Added
3636+- TypeScript SDK (`@rockbox/sdk`) for controlling rockboxd from Node.js / browser applications
3737+- Playlists UI in the web interface — create, edit, and manage saved and smart playlists
3838+- Album art footer overlay shown on album cover hover
3939+4040+### Changed
4141+- Web UI data layer migrated from Apollo Client to TanStack React Query
4242+- Playlist modals rendered into document body via React portal (fixes z-index stacking issues)
4343+4444+## [2026.04.26]
4545+4646+### Added
4747+- Chromecast PCM sink (`audio_output = "chromecast"`) — streams WAV over HTTP and controls playback via the Cast Media protocol
4848+- UPnP/DLNA support: ContentDirectory media server (`upnp_server_enabled`), MediaRenderer:1 (`upnp_renderer_enabled`), and UPnP PCM sink (`audio_output = "upnp"`) with auto-renderer discovery
4949+- Device picker UI in the GPUI and web mini-player — switch audio output (Rockbox built-in, AirPlay, Squeezelite, Chromecast) without restarting
5050+- Multi-room AirPlay: `airplay_receivers` list in `settings.toml` supports sending to multiple RAOP receivers simultaneously
5151+- Squeezelite multi-room PCM sink (`audio_output = "squeezelite"`) — Slim Protocol TCP server + HTTP PCM broadcast; supports unlimited concurrent squeezelite clients with independent reader cursors
5252+5353+### Fixed
5454+- Duplicate Chromecast devices skipped during discovery
5555+- Typesense search index initialised before the HTTP server accepts requests (avoids empty results on cold start)
5656+5757+## [2026.04.25]
5858+5959+### Added
6060+- Saved playlists: create, rename, delete, and reorder tracks via gRPC, GraphQL, and REST APIs
6161+- Smart playlists: rule-based auto-generated playlists with play-count and skip-count tracking
6262+- Playlist search integration with Typesense
6363+- `StreamLibrary` gRPC server-streaming RPC — pushes library updates to clients when a scan completes
6464+- GPUI file browser: navigate the local filesystem and enqueue directories directly
6565+6666+### Fixed
6767+- Now Playing widget in GPUI shows correctly when the app opens with a paused track (initial status fetched once at startup)
6868+- Rocksky registration failures logged at `debug` level instead of `warn` to reduce noise
6969+- Global Play/Pause keybind no longer fires when a text input field has focus