[PR #660] [MERGED] High-resolution volume control and normalisation #1002

Closed
opened 2026-02-27 20:00:47 +03:00 by kerem · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/librespot-org/librespot/pull/660
Author: @roderickvd
Created: 3/1/2021
Status: Merged
Merged: 4/10/2021
Merged by: @sashahilton00

Base: devHead: hi-res-volume-control


📝 Commits (10+)

  • f29e521 High-resolution volume control and normalisation
  • 1672eb8 Fix build on Rust < 1.50.0
  • 5257be7 Add command-line option to set F32 or S16 bit output
  • 6379926 Fix example
  • a4ef174 Fix Alsa backend for 64-bit systems
  • 5f26a74 Add support for S32 output format
  • 309e264 Rename steepness to knee
  • 9dcaeee Default to S16 output
  • 770ea15 Add support for S24 and S24_3 output formats
  • b94879d Fix GStreamer buffer pool size [ref #660 review]

📊 Changes

23 files changed (+1023 additions, -909 deletions)

View changed files

📝 Cargo.lock (+64 -545)
📝 audio/Cargo.toml (+3 -2)
audio/src/convert.rs (+59 -0)
📝 audio/src/lewton_decoder.rs (+5 -2)
📝 audio/src/lib.rs (+4 -2)
📝 audio/src/libvorbis_decoder.rs (+12 -1)
📝 examples/play.rs (+3 -2)
📝 playback/Cargo.toml (+6 -6)
📝 playback/src/audio_backend/alsa.rs (+51 -34)
📝 playback/src/audio_backend/gstreamer.rs (+43 -27)
📝 playback/src/audio_backend/jackaudio.rs (+17 -20)
📝 playback/src/audio_backend/mod.rs (+56 -5)
📝 playback/src/audio_backend/pipe.rs (+24 -31)
📝 playback/src/audio_backend/portaudio.rs (+101 -33)
📝 playback/src/audio_backend/pulseaudio.rs (+27 -20)
📝 playback/src/audio_backend/rodio.rs (+96 -113)
📝 playback/src/audio_backend/sdl.rs (+79 -24)
📝 playback/src/audio_backend/subprocess.rs (+13 -11)
📝 playback/src/config.rs (+86 -9)
📝 playback/src/mixer/mod.rs (+1 -1)

...and 3 more files

📄 Description

Enhancements:

  • Store and handle samples as 32-bit floats instead of 16-bit integers. This provides 24-25 bits of transparency, allowing for 48-54 dB of headroom to do volume control and normalisation without throwing away bits or dropping dynamic range below 96 dB CD quality.

  • Perform volume control and normalisation in 64-bit arithmetic for minimum quantisation noise.

  • Output to 32-bit float, 32-bit integer, 24-bit integer (both in padded 32-bit words and as three-byte arrays) or 16-bit integer (default), as specified on the command line.

  • Add a dynamic limiter with configurable threshold, attack time, release or decay time, and steepness for the sigmoid transfer function. This mimics the native Spotify limiter, offering greater dynamic range than the old limiter, that just reduced overall gain to prevent clipping.

  • Make the configurable threshold also apply to the old limiter, which is still available.

  • DRY-ups of a lot of audio backend code, at the same time enabling OggData passthrough on the subprocess backend.

Resolves: #608

Notes:

  • New command line options:
        --format FORMAT Output format (F32, S32, S24, S24_3 or S16). Defaults to S16
        --normalisation-method NORMALISATION_METHOD
                        Specify the normalisation method to use - [basic,
                        dynamic]. Default is dynamic.
        --normalisation-threshold THRESHOLD
                        Threshold (dBFS) to prevent clipping. Default is -1.0.
        --normalisation-attack ATTACK
                        Attack time (ms) in which the dynamic limiter is
                        reducing gain. Default is 5.
        --normalisation-release RELEASE
                        Release or decay time (ms) in which the dynamic
                        limiter is restoring gain. Default is 100.
        --normalisation-knee KNEE
                        Knee steepness of the dynamic limiter. Default is 1.0.
  • For the dynamic limiter, steepness between 0.5 and 2.0 work well. The default of 1.0 yields a linear function; > 1.0 rolls off softly, is steeper midway and < 1.0 has a sharp initial response, then gentler midway. Feedback on optimisation of these parameters is welcome.

  • Compiling with-vorbis works, but panics. This was already the case and is not new to this PR. See: https://github.com/tomaka/vorbis-rs/issues/19

To do:

  • Add a command-line option to output either 16 or 32 bit depth
  • Add support for four-byte S24 output format
  • Add support for three-byte S24_3 output format
  • Rename normalisation-steepness to normalisation-knee
  • Revert default format to S16 for a seamless out-of-the-box experience
  • Test remaining backends (help needed!)
  • Optimise requantizer to work in f32 then round

Pending refactoring:

  • DRY up sample conversion in PortAudio and SDL backends
    Edit: Not much to be gained, dropped idea
  • Use TryFrom idiom instead of AudioPacket::f32_to_<type>() helper functions
    Edit: Moved sample conversion into separate struct
  • Use Self instead of full struct and enum names on config.rs

All done!

Test status:

All backends compile successfully, but require testing.

Backend Status First verified by Remarks
Alsa @roderickvd -
GStreamer @JasonLG1979
JACK audio @sashahilton00
pipe @roderickvd -
PortAudio ☑️ @roderickvd [2]
PulseAudio @JasonLG1979 -
Rodio ☑️ @roderickvd [1]
SDL [3]
subprocess @roderickvd -
  1. Rodio on Raspian 10 (Raspberry Pi 3 Model B+) does not open the output correctly. See issue at: RustAudio/cpal#564. This seems to be on Alsa only, no issues on macOS Big Sur, and not strictly related to this PR.

  2. Panics on Alsa and macOS but that's already the case in dev.

  3. Free pass granted by @sashahilton00.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/librespot-org/librespot/pull/660 **Author:** [@roderickvd](https://github.com/roderickvd) **Created:** 3/1/2021 **Status:** ✅ Merged **Merged:** 4/10/2021 **Merged by:** [@sashahilton00](https://github.com/sashahilton00) **Base:** `dev` ← **Head:** `hi-res-volume-control` --- ### 📝 Commits (10+) - [`f29e521`](https://github.com/librespot-org/librespot/commit/f29e5212c402074c1a12eb493af7e5d4c966dcdf) High-resolution volume control and normalisation - [`1672eb8`](https://github.com/librespot-org/librespot/commit/1672eb87abd22f9c45e3086b8a2eb93c8c8fe6ed) Fix build on Rust < 1.50.0 - [`5257be7`](https://github.com/librespot-org/librespot/commit/5257be7824e0fd2c76cf889f88f82047080fed90) Add command-line option to set F32 or S16 bit output - [`6379926`](https://github.com/librespot-org/librespot/commit/6379926eb4c2dd786ccde075c6f1779aae3238ca) Fix example - [`a4ef174`](https://github.com/librespot-org/librespot/commit/a4ef174fd00bac3d2be779653ccf35617aa0f379) Fix Alsa backend for 64-bit systems - [`5f26a74`](https://github.com/librespot-org/librespot/commit/5f26a745d7dbe085cb52f4e433ae3438f54c5f95) Add support for S32 output format - [`309e264`](https://github.com/librespot-org/librespot/commit/309e26456ef2d381cf0a1338ec45fac2f3b25665) Rename steepness to knee - [`9dcaeee`](https://github.com/librespot-org/librespot/commit/9dcaeee6d445c0942eac6dd8abc74a2f53486c17) Default to S16 output - [`770ea15`](https://github.com/librespot-org/librespot/commit/770ea15498a0f1cfc7b9986f0954f4150258c29f) Add support for S24 and S24_3 output formats - [`b94879d`](https://github.com/librespot-org/librespot/commit/b94879de62f3fbf11a38aa2fa5df11b24e9998b8) Fix GStreamer buffer pool size [ref #660 review] ### 📊 Changes **23 files changed** (+1023 additions, -909 deletions) <details> <summary>View changed files</summary> 📝 `Cargo.lock` (+64 -545) 📝 `audio/Cargo.toml` (+3 -2) ➕ `audio/src/convert.rs` (+59 -0) 📝 `audio/src/lewton_decoder.rs` (+5 -2) 📝 `audio/src/lib.rs` (+4 -2) 📝 `audio/src/libvorbis_decoder.rs` (+12 -1) 📝 `examples/play.rs` (+3 -2) 📝 `playback/Cargo.toml` (+6 -6) 📝 `playback/src/audio_backend/alsa.rs` (+51 -34) 📝 `playback/src/audio_backend/gstreamer.rs` (+43 -27) 📝 `playback/src/audio_backend/jackaudio.rs` (+17 -20) 📝 `playback/src/audio_backend/mod.rs` (+56 -5) 📝 `playback/src/audio_backend/pipe.rs` (+24 -31) 📝 `playback/src/audio_backend/portaudio.rs` (+101 -33) 📝 `playback/src/audio_backend/pulseaudio.rs` (+27 -20) 📝 `playback/src/audio_backend/rodio.rs` (+96 -113) 📝 `playback/src/audio_backend/sdl.rs` (+79 -24) 📝 `playback/src/audio_backend/subprocess.rs` (+13 -11) 📝 `playback/src/config.rs` (+86 -9) 📝 `playback/src/mixer/mod.rs` (+1 -1) _...and 3 more files_ </details> ### 📄 Description ### Enhancements: - Store and handle samples as 32-bit floats instead of 16-bit integers. This provides 24-25 bits of transparency, allowing for 48-54 dB of headroom to do volume control and normalisation without throwing away bits or dropping dynamic range below 96 dB CD quality. - Perform volume control and normalisation in 64-bit arithmetic for minimum quantisation noise. - Output to 32-bit float, 32-bit integer, 24-bit integer (both in padded 32-bit words and as three-byte arrays) or 16-bit integer (default), as specified on the command line. - Add a dynamic limiter with configurable threshold, attack time, release or decay time, and steepness for the sigmoid transfer function. This mimics the native Spotify limiter, offering greater dynamic range than the old limiter, that just reduced overall gain to prevent clipping. - Make the configurable threshold also apply to the old limiter, which is still available. - DRY-ups of a lot of audio backend code, at the same time enabling `OggData` passthrough on the subprocess backend. Resolves: #608 ### Notes: - New command line options: ``` --format FORMAT Output format (F32, S32, S24, S24_3 or S16). Defaults to S16 --normalisation-method NORMALISATION_METHOD Specify the normalisation method to use - [basic, dynamic]. Default is dynamic. --normalisation-threshold THRESHOLD Threshold (dBFS) to prevent clipping. Default is -1.0. --normalisation-attack ATTACK Attack time (ms) in which the dynamic limiter is reducing gain. Default is 5. --normalisation-release RELEASE Release or decay time (ms) in which the dynamic limiter is restoring gain. Default is 100. --normalisation-knee KNEE Knee steepness of the dynamic limiter. Default is 1.0. ``` - For the dynamic limiter, steepness between 0.5 and 2.0 work well. The default of 1.0 yields a linear function; > 1.0 rolls off softly, is steeper midway and < 1.0 has a sharp initial response, then gentler midway. Feedback on optimisation of these parameters is welcome. - Compiling `with-vorbis` works, but panics. This was already the case and is not new to this PR. See: https://github.com/tomaka/vorbis-rs/issues/19 ### To do: - [x] Add a command-line option to output either 16 or 32 bit depth - [x] Add support for four-byte S24 output format - [x] Add support for three-byte S24_3 output format - [x] Rename `normalisation-steepness` to `normalisation-knee` - [x] Revert default format to S16 for a seamless out-of-the-box experience - [x] Test remaining backends (help needed!) - [x] Optimise requantizer to work in `f32` then round Pending refactoring: - [x] ~~DRY up sample conversion in PortAudio and SDL backends~~ _Edit: Not much to be gained, dropped idea_ - [x] ~~Use `TryFrom` idiom instead of `AudioPacket::f32_to_<type>()` helper functions~~ _Edit: Moved sample conversion into separate struct_ - [x] Use `Self` instead of full struct and enum names on `config.rs` All done! ### Test status: All backends compile successfully, but require testing. Backend | Status | First verified by | Remarks ------- | ------ | ----------- | ------ Alsa | :white_check_mark: | @roderickvd | - GStreamer | :white_check_mark: | @JasonLG1979 | JACK audio | ✅ | @sashahilton00 | pipe | :white_check_mark: | @roderickvd | - PortAudio | :ballot_box_with_check: | @roderickvd | [2] PulseAudio | :white_check_mark: | @JasonLG1979 | - Rodio | :ballot_box_with_check: | @roderickvd | [1] SDL | | | [3] subprocess | :white_check_mark: | @roderickvd | - 1. Rodio on Raspian 10 (Raspberry Pi 3 Model B+) does not open the output correctly. See issue at: RustAudio/cpal#564. This seems to be on Alsa only, no issues on macOS Big Sur, and not strictly related to this PR. 2. Panics on Alsa and macOS but that's already the case in `dev`. 3. Free pass granted by @sashahilton00. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
kerem 2026-02-27 20:00:47 +03:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/librespot#1002
No description provided.