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

View file

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

View file

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

View file

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