1 // Vorbis decoder written in Rust
2 //
3 // Copyright (c) 2016 est31 <MTest31@outlook.com>
4 // and contributors. All rights reserved.
5 // Licensed under MIT license, or Apache 2 license,
6 // at your option. Please see the LICENSE file
7 // attached to this source distribution for details.
8 
9 /*!
10 Cached header info
11 
12 This mod contains logic to generate and deal with
13 data derived from header information
14 that's used later in the decode process.
15 
16 The caching is done to speed up decoding.
17 */
18 
19 pub struct TwiddleFactors {
20 	pub a :Vec<f32>,
21 	pub b :Vec<f32>,
22 	pub c :Vec<f32>,
23 }
24 
25 pub struct CachedBlocksizeDerived {
26 	pub twiddle_factors : TwiddleFactors,
27 	pub window_slope : Vec<f32>,
28 	pub bitrev : Vec<u32>,
29 }
30 
31 impl CachedBlocksizeDerived {
from_blocksize(bs :u8) -> Self32 	pub fn from_blocksize(bs :u8) -> Self {
33 		CachedBlocksizeDerived {
34 			window_slope : generate_window((1 << (bs as u16)) >> 1),
35 			twiddle_factors : compute_twiddle_factors(bs),
36 			bitrev : compute_bitreverse(bs),
37 		}
38 	}
39 }
40 
win_slope(x :u16, n :u16) -> f3241 fn win_slope(x :u16, n :u16) -> f32 {
42 	// please note that there might be a MISTAKE
43 	// in how the spec specifies the right window slope
44 	// function. See "4.3.1. packet type, mode and window decode"
45 	// step 7 where it adds an "extra" pi/2.
46 	// The left slope doesn't have it, only the right one.
47 	// as stb_vorbis shares the window slope generation function,
48 	// The *other* possible reason is that we don't need the right
49 	// window for anything. TODO investigate this more.
50 	let v = (0.5 * std::f32::consts::PI * (x as f32 + 0.5) / n as f32).sin();
51 	return (0.5 * std::f32::consts::PI * v * v ).sin();
52 }
53 
generate_window(n :u16) -> Vec<f32>54 fn generate_window(n :u16) -> Vec<f32> {
55 	let mut window = Vec::with_capacity(n as usize);
56 	for i in 0 .. n {
57 		window.push(win_slope(i, n));
58 	}
59 	return window;
60 }
61 
compute_twiddle_factors(blocksize :u8) -> TwiddleFactors62 fn compute_twiddle_factors(blocksize :u8) -> TwiddleFactors {
63 	let n = 1 << (blocksize as u16);
64 
65 	let n2 = n >> 1;
66 	let n4 = n >> 2;
67 	let n8 = n >> 3;
68 
69 	let mut a = Vec::with_capacity(n2);
70 	let mut b = Vec::with_capacity(n2);
71 	let mut c = Vec::with_capacity(n4);
72 
73 	let mut k2 = 0;
74 
75 	let pi_4_n = 4.0 * std::f32::consts::PI / (n as f32);
76 	let pi_05_n = 0.5 * std::f32::consts::PI / (n as f32);
77 	let pi_2_n = 2.0 * std::f32::consts::PI / (n as f32);
78 
79 	for k in 0..n4 {
80 		a.push( f32::cos((k as f32)      * pi_4_n));
81 		a.push(-f32::sin((k as f32)      * pi_4_n));
82 		b.push( f32::cos(((k2+1) as f32) * pi_05_n) * 0.5);
83 		b.push( f32::sin(((k2+1) as f32) * pi_05_n) * 0.5);
84 		k2 += 2;
85 	}
86 	k2 = 0;
87 	for _ in 0..n8 {
88 		c.push( f32::cos(((k2 + 1) as f32) * pi_2_n));
89 		c.push(-f32::sin(((k2 + 1) as f32) * pi_2_n));
90 		k2 += 2;
91 	}
92 	return TwiddleFactors {
93 		a,
94 		b,
95 		c,
96 	};
97 }
98 
compute_bitreverse(blocksize :u8) -> Vec<u32>99 fn compute_bitreverse(blocksize :u8) -> Vec<u32> {
100 	let ld = blocksize as u16;
101 	let n = 1 << blocksize;
102 	let n8 = n >> 3;
103 	let mut rev = Vec::with_capacity(n8);
104 	for i in 0 .. n8 {
105 		rev.push((::bit_reverse(i as u32) as u32 >> (32 - ld + 3)) << 2);
106 	}
107 	return rev;
108 }
109 
110 #[test]
test_compute_bitreverse()111 fn test_compute_bitreverse() {
112 	let br = compute_bitreverse(8);
113 	// The output was generated from the output of the
114 	// original stb_vorbis function.
115 	let cmp_arr = &[
116 		0,   64,  32,  96,
117 		16,  80,  48, 112,
118 		8,   72,  40, 104,
119 		24,  88,  56, 120,
120 		4,   68,  36, 100,
121 		20,  84,  52, 116,
122 		12,  76,  44, 108,
123 		28,  92,  60, 124];
124 	assert_eq!(br, cmp_arr);
125 }
126 
127 #[inline]
bark(x :f32) -> f32128 fn bark(x :f32) -> f32 {
129 	13.1 * (0.00074 * x).atan() + 2.24 * (0.0000000185*x*x).atan() + 0.0001 * x
130 }
131 
132 /// Precomputes bark map values used by floor type 0 packets
133 ///
134 /// Precomputes the cos(omega) values for use by floor type 0 computation.
135 ///
136 /// Note that there is one small difference to the spec: the output
137 /// vec is n elements long, not n+1. The last element (at index n)
138 /// is -1 in the spec, we lack it. Users of the result of this function
139 /// implementation should use it "virtually".
compute_bark_map_cos_omega(n :u16, floor0_rate :u16, floor0_bark_map_size :u16) -> Vec<f32>140 pub fn compute_bark_map_cos_omega(n :u16, floor0_rate :u16,
141 		floor0_bark_map_size :u16) -> Vec<f32> {
142 	let mut res = Vec::with_capacity(n as usize);
143 	let hfl = floor0_rate as f32 / 2.0;
144 	let hfl_dn = hfl / n as f32;
145 	let foobar_const_part = floor0_bark_map_size as f32 / bark(hfl);
146 	// Bark map size minus 1:
147 	let bms_m1 = floor0_bark_map_size as f32 - 1.0;
148 	let omega_factor = std::f32::consts::PI / floor0_bark_map_size as f32;
149 	for i in 0 .. n {
150 		let foobar = (bark(i as f32 * hfl_dn) * foobar_const_part).floor();
151 		let map_elem = foobar.min(bms_m1);
152 		let cos_omega = (map_elem * omega_factor).cos();
153 		res.push(cos_omega);
154 	}
155 	return res;
156 }
157