lewton/
header_cached.rs

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/*!
10Cached header info
11
12This mod contains logic to generate and deal with
13data derived from header information
14that's used later in the decode process.
15
16The caching is done to speed up decoding.
17*/
18
19pub struct TwiddleFactors {
20	pub a :Vec<f32>,
21	pub b :Vec<f32>,
22	pub c :Vec<f32>,
23}
24
25pub struct CachedBlocksizeDerived {
26	pub twiddle_factors : TwiddleFactors,
27	pub window_slope : Vec<f32>,
28	pub bitrev : Vec<u32>,
29}
30
31impl CachedBlocksizeDerived {
32	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
41fn 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
54fn 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
62fn 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
99fn 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]
111fn 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]
128fn 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".
140pub 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}