Skip to content

Commit 68f409c

Browse files
committed
Match reference Vorbis sample conversion technique
See: #694 (comment)
1 parent f7bcb49 commit 68f409c

File tree

2 files changed

+15
-22
lines changed

2 files changed

+15
-22
lines changed

playback/src/convert.rs

+14-18
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@ use zerocopy::AsBytes;
77
#[repr(transparent)]
88
pub struct i24([u8; 3]);
99
impl i24 {
10-
pub const MIN: i32 = -8388608;
11-
pub const MAX: i32 = 8388607;
12-
1310
fn from_s24(sample: i32) -> Self {
1411
// trim the padding in the most significant byte
1512
let [a, b, c, _d] = sample.to_le_bytes();
@@ -35,13 +32,10 @@ impl Converter {
3532
}
3633

3734
// Denormalize, dither and shape noise
38-
pub fn scale(&mut self, sample: f32, max: i32) -> f32 {
39-
// Losslessly represent [-1.0..1.0] as some signed integer value while
40-
// maintaining DC linearity. There is nothing to be gained by doing
41-
// this in f64, as the significand of a f32 is 24 bits, just like the
42-
// maximum bit depth we are converting to. Taken from:
43-
// http://blog.bjornroche.com/2009/12/int-float-int-its-jungle-out-there.html
44-
let int_value = sample * (max as f32 + 0.5) - 0.5;
35+
pub fn scale(&mut self, sample: f32, factor: i64) -> f32 {
36+
// From the many float to int conversion methods available, match what
37+
// the reference Vorbis implementation uses: sample * 32768 (for 16 bit)
38+
let int_value = sample * factor as f32;
4539
self.shaped_dither(int_value)
4640
}
4741

@@ -50,11 +44,13 @@ impl Converter {
5044
// byte is zero. Otherwise, dithering may cause an overflow. This is not
5145
// necessary for other formats, because casting to integer will saturate
5246
// to the bounds of the primitive.
53-
pub fn clamping_scale(&mut self, sample: f32, min: i32, max: i32) -> f32 {
54-
let int_value = self.scale(sample, max);
47+
pub fn clamping_scale(&mut self, sample: f32, factor: i64) -> f32 {
48+
let int_value = self.scale(sample, factor);
49+
50+
// In two's complement, there are more negative than positive values.
51+
let min = -factor as f32;
52+
let max = (factor - 1) as f32;
5553

56-
let min = min as f32;
57-
let max = max as f32;
5854
if int_value < min {
5955
return min;
6056
} else if int_value > max {
@@ -75,15 +71,15 @@ impl Converter {
7571
pub fn f32_to_s32(&mut self, samples: &[f32]) -> Vec<i32> {
7672
samples
7773
.iter()
78-
.map(|sample| self.scale(*sample, std::i32::MAX) as i32)
74+
.map(|sample| self.scale(*sample, 0x80000000) as i32)
7975
.collect()
8076
}
8177

8278
// S24 is 24-bit PCM packed in an upper 32-bit word
8379
pub fn f32_to_s24(&mut self, samples: &[f32]) -> Vec<i32> {
8480
samples
8581
.iter()
86-
.map(|sample| self.clamping_scale(*sample, i24::MIN, i24::MAX) as i32)
82+
.map(|sample| self.clamping_scale(*sample, 0x800000) as i32)
8783
.collect()
8884
}
8985

@@ -94,7 +90,7 @@ impl Converter {
9490
.map(|sample| {
9591
// Not as DRY as calling f32_to_s24 first, but this saves iterating
9692
// over all samples twice.
97-
let int_value = self.clamping_scale(*sample, i24::MIN, i24::MAX) as i32;
93+
let int_value = self.clamping_scale(*sample, 0x800000) as i32;
9894
i24::from_s24(int_value)
9995
})
10096
.collect()
@@ -103,7 +99,7 @@ impl Converter {
10399
pub fn f32_to_s16(&mut self, samples: &[f32]) -> Vec<i16> {
104100
samples
105101
.iter()
106-
.map(|sample| self.scale(*sample, std::i16::MAX as i32) as i16)
102+
.map(|sample| self.scale(*sample, 0x8000) as i16)
107103
.collect()
108104
}
109105
}

playback/src/decoder/libvorbis_decoder.rs

+1-4
Original file line numberDiff line numberDiff line change
@@ -38,14 +38,11 @@ where
3838
loop {
3939
match self.0.packets().next() {
4040
Some(Ok(packet)) => {
41-
// Losslessly represent [-32768, 32767] to [-1.0, 1.0] while maintaining DC linearity.
4241
return Ok(Some(AudioPacket::Samples(
4342
packet
4443
.data
4544
.iter()
46-
.map(|sample| {
47-
((*sample as f64 + 0.5) / (std::i16::MAX as f64 + 0.5)) as f32
48-
})
45+
.map(|sample| (*sample as f64 / 0x8000 as f64) as f32)
4946
.collect(),
5047
)));
5148
}

0 commit comments

Comments
 (0)