Improve sample rounding and clean up noise shaping leftovers (#771)

This commit is contained in:
Roderick van Domburg 2021-05-29 22:53:19 +02:00 committed by GitHub
parent a2fde0a1d6
commit 8062bd2518
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 89 additions and 24 deletions

85
Cargo.lock generated
View file

@ -435,6 +435,12 @@ dependencies = [
"percent-encoding",
]
[[package]]
name = "fuchsia-cprng"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba"
[[package]]
name = "futures"
version = "0.3.15"
@ -1073,6 +1079,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7d73b3f436185384286bd8098d17ec07c9a7d2388a6599f824d8502b529702a"
[[package]]
name = "libmath"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfd3416934a853ae80d5c3b006f632dfcbaf320300c5167e88a469e9ac214502"
dependencies = [
"rand 0.3.23",
]
[[package]]
name = "libmdns"
version = "0.6.1"
@ -1085,7 +1100,7 @@ dependencies = [
"if-addrs",
"log",
"multimap",
"rand",
"rand 0.8.3",
"socket2",
"thiserror",
"tokio",
@ -1190,7 +1205,7 @@ dependencies = [
"librespot-protocol",
"log",
"protobuf",
"rand",
"rand 0.8.3",
"serde",
"serde_json",
"tokio",
@ -1223,7 +1238,7 @@ dependencies = [
"pbkdf2",
"priority-queue",
"protobuf",
"rand",
"rand 0.8.3",
"serde",
"serde_json",
"sha-1",
@ -1254,7 +1269,7 @@ dependencies = [
"libmdns",
"librespot-core",
"log",
"rand",
"rand 0.8.3",
"serde_json",
"sha-1",
"simple_logger",
@ -1288,6 +1303,7 @@ dependencies = [
"gstreamer-app",
"jack 0.7.1",
"lewton",
"libmath",
"libpulse-binding",
"libpulse-simple-binding",
"librespot-audio",
@ -1296,7 +1312,7 @@ dependencies = [
"log",
"ogg",
"portaudio-rs",
"rand",
"rand 0.8.3",
"rand_distr",
"rodio",
"sdl2",
@ -1488,7 +1504,7 @@ dependencies = [
"autocfg",
"num-integer",
"num-traits",
"rand",
"rand 0.8.3",
]
[[package]]
@ -1840,6 +1856,29 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.3.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c"
dependencies = [
"libc",
"rand 0.4.6",
]
[[package]]
name = "rand"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293"
dependencies = [
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi",
]
[[package]]
name = "rand"
version = "0.8.3"
@ -1848,7 +1887,7 @@ checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
"rand_core 0.6.2",
"rand_hc",
]
@ -1859,9 +1898,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
dependencies = [
"ppv-lite86",
"rand_core",
"rand_core 0.6.2",
]
[[package]]
name = "rand_core"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b"
dependencies = [
"rand_core 0.4.2",
]
[[package]]
name = "rand_core"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc"
[[package]]
name = "rand_core"
version = "0.6.2"
@ -1878,7 +1932,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da9e8f32ad24fb80d07d2323a9a2ce8b30d68a62b8cb4df88119ff49a698f038"
dependencies = [
"num-traits",
"rand",
"rand 0.8.3",
]
[[package]]
@ -1887,7 +1941,16 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
dependencies = [
"rand_core",
"rand_core 0.6.2",
]
[[package]]
name = "rdrand"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2"
dependencies = [
"rand_core 0.3.1",
]
[[package]]
@ -2212,7 +2275,7 @@ checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
dependencies = [
"cfg-if 1.0.0",
"libc",
"rand",
"rand 0.8.3",
"redox_syscall",
"remove_dir_all",
"winapi",

View file

@ -20,6 +20,7 @@ version = "0.2.0"
[dependencies]
futures-executor = "0.3"
futures-util = { version = "0.3", default_features = false, features = ["alloc"] }
libmath = "0.2"
log = "0.4"
byteorder = "1.4"
shell-words = "1.0.0"

View file

@ -32,18 +32,19 @@ impl Converter {
// Denormalize and dither
pub fn scale(&mut self, sample: f32, factor: i64) -> f32 {
let dither = match self.ditherer {
Some(ref mut d) => d.noise(),
None => 0.0,
};
// From the many float to int conversion methods available, match what
// the reference Vorbis implementation uses: sample * 32768 (for 16 bit)
let int_value = sample * factor as f32;
let int_value = sample * factor as f32 + dither;
// https://doc.rust-lang.org/nomicon/casts.html: casting float to integer
// rounds towards zero, then saturates. Ideally halves should round to even to
// prevent any bias, but since it is extremely unlikely that a float has
// *exactly* .5 as fraction, this should be more than precise enough.
match self.ditherer {
Some(ref mut d) => int_value + d.noise(int_value),
None => int_value,
}
// Casting float to integer rounds towards zero by default, i.e. it
// truncates, and that generates larger error than rounding to nearest.
// Absolute lowest error is gained from rounding ties to even.
math::round::half_to_even(int_value.into(), 0) as f32
}
// Special case for samples packed in a word of greater bit depth (e.g.

View file

@ -32,7 +32,7 @@ pub trait Ditherer {
where
Self: Sized;
fn name(&self) -> &'static str;
fn noise(&mut self, sample: f32) -> f32;
fn noise(&mut self) -> f32;
}
impl fmt::Display for dyn Ditherer {
@ -64,7 +64,7 @@ impl Ditherer for TriangularDitherer {
"Triangular"
}
fn noise(&mut self, _sample: f32) -> f32 {
fn noise(&mut self) -> f32 {
self.distribution.sample(&mut self.cached_rng)
}
}
@ -87,7 +87,7 @@ impl Ditherer for GaussianDitherer {
"Gaussian"
}
fn noise(&mut self, _sample: f32) -> f32 {
fn noise(&mut self) -> f32 {
self.distribution.sample(&mut self.cached_rng)
}
}
@ -113,7 +113,7 @@ impl Ditherer for HighPassDitherer {
"Triangular, High Passed"
}
fn noise(&mut self, _sample: f32) -> f32 {
fn noise(&mut self) -> f32 {
let new_noise = self.distribution.sample(&mut self.cached_rng);
let high_passed_noise = new_noise - self.previous_noises[self.active_channel];
self.previous_noises[self.active_channel] = new_noise;