Skip to main content

wavetable/
wt_reader.rs

1//! Reads wavetable files in WAV format.
2//!
3//! Currently WtReader only supports reading WAV files in float format,
4//! containing the samples as 32-bit float values. Additionally each waveform
5//! in the file must be 2048 samples long.
6//!
7//! For every 2048 samples, the reader will add an additional waveshape table
8//! to the result. As read from disk, these tables will not be bandlimited,
9//! and might therefore generate aliasing if used directly.
10
11use super::{Wavetable, WavetableRef};
12use super::Float;
13
14use std::fs::File;
15use std::io::{Read, BufReader};
16use std::mem;
17
18use log::{info, trace, warn};
19
20#[repr(C, packed)]
21#[derive(Debug, Copy, Clone)]
22struct ChunkHeader {
23    chunk_id: [u8; 4],
24    size: u32
25}
26
27pub struct WtReader {
28    pub base_path: String,
29}
30
31impl WtReader {
32    /// Creates a new WtReader instance.
33    ///
34    /// The argument is a path to a directory that files will be read from.
35    ///
36    /// ```
37    /// use wavetable::WtReader;
38    ///
39    /// let data_dir = "data".to_string();
40    ///
41    /// let reader = WtReader::new(&data_dir);
42    /// ```
43    pub fn new(path: &str) -> Self {
44        let mut base_path = path.to_string();
45        let path_bytes = base_path.as_bytes();
46        if path_bytes[path_bytes.len() - 1] != b'/' {
47            base_path.push('/');
48        }
49        WtReader{base_path: base_path}
50    }
51
52    /// Read a file with the given filename.
53    ///
54    /// The filename should not contain the full path of the file, but be
55    /// relative to the base path set in the constructor.
56    ///
57    /// ``` norun
58    /// use wavetable::WtReader;
59    ///
60    /// # fn main() -> Result<(), ()> {
61    ///
62    /// let reader = WtReader::new("data");
63    /// let wavetable = reader.read_file("test")?;
64    ///
65    /// # Ok(())
66    /// # }
67    /// ```
68    pub fn read_file(&self, filename: &str) -> Result<WavetableRef, ()> {
69        if filename == "" {
70            return Err(());
71        }
72        let filename = self.base_path.clone() + filename;
73        let result = File::open(filename);
74        if let Ok(file) = result {
75            let reader = BufReader::new(file);
76            WtReader::read_wavetable(reader, 2048)
77        } else {
78            Err(())
79        }
80    }
81
82    /// Read a wavetable from the provided input stream.
83    ///
84    /// Source is any stream object implementing the Read trait.
85    /// samples_per_table is the length of a single wave cycle, usually 2048.
86    ///
87    /// ``` norun
88    /// use wavetable::WtReader;
89    /// use std::io::BufReader;
90    ///
91    /// let reader = WtReader::new("data");
92    /// let data: &[u8] = &[0x00]; // Some buffer with wave data
93    /// let buffer = BufReader::new(data);
94    /// let wavetable = WtReader::read_wavetable(buffer, 2048)?;
95    /// ```
96    pub fn read_wavetable<R: Read>(mut source: R, samples_per_table: usize) -> Result<WavetableRef, ()> {
97        // Read RIFF header
98        let result = WtReader::read_header(&mut source);
99        match result {
100            Ok(_header) => { WtReader::skip_chunk(&mut source, 4); }
101            Err(()) => return Err(()),
102        }
103        // Read chunks
104        loop {
105            let result = WtReader::read_header(&mut source);
106            match result {
107                Ok(header) => {
108                    unsafe { info!("Chunk ID: {:?}\nSize; {}", header.chunk_id, header.size) };
109                    if header.chunk_id[0] == 'd' as u8
110                    && header.chunk_id[1] == 'a' as u8
111                    && header.chunk_id[2] == 't' as u8
112                    && header.chunk_id[3] == 'a' as u8 {
113                        // Found data section, create wavetable
114                        let result = WtReader::read_samples(&mut source, header.size, samples_per_table);
115                        let samples = if let Ok(s) = result { s } else { return Err(()) };
116                        let num_tables = samples.len();
117                        return Result::Ok(Wavetable::new_from_vector(num_tables, 1, samples_per_table, samples));
118                    } else {
119                        WtReader::skip_chunk(&mut source, header.size);
120                    }
121                }
122                Err(()) => return Err(()),
123            }
124        }
125    }
126
127    // Read the header of the next WAV chunk from the input stream.
128    fn read_header<R: Read>(source: &mut R) -> Result<ChunkHeader, ()> {
129        let mut header: ChunkHeader = unsafe { mem::zeroed() };
130        let header_size = mem::size_of::<ChunkHeader>();
131        unsafe {
132            let header_slize = std::slice::from_raw_parts_mut(&mut header as *mut _ as *mut u8, header_size);
133            source.read_exact(header_slize).unwrap();
134        }
135        info!("\nRead structure: {:#?}", header);
136        Ok(header)
137    }
138
139    // Skip over the rest of the current chunk to the next header.
140    fn skip_chunk<R: Read>(source: &mut R, num_bytes: u32) {
141        let mut buf: [u8; 1] = unsafe { mem::zeroed() };
142        for _i in 0..num_bytes {
143            source.read(&mut buf).unwrap();
144        }
145    }
146
147    // Read samples into multiple tables.
148    //
149    // A file is assumed to hold multiple waveshapes, each with
150    // <samples_per_table> values. One more value is added to the end of the
151    // table automatically.
152    //
153    fn read_samples<R: Read>(source: &mut R, num_bytes: u32, samples_per_table: usize) -> Result<Vec<Vec<Float>>, ()> {
154        let mut buf: f32 = unsafe { mem::zeroed() };
155        let mut samples: Vec<Vec<Float>> = vec!{};
156        let sample_size = mem::size_of::<f32>();
157        let num_samples = num_bytes as usize / sample_size;
158        let num_tables = num_samples / samples_per_table;
159        if num_samples < samples_per_table || num_samples % samples_per_table != 0 {
160            info!("Unexpected number of samples: {}", num_samples);
161            return Err(());
162        }
163        info!("{} samples total, {} tables with {} values each", num_samples, num_tables, samples_per_table);
164        for _i in 0..num_tables {
165            samples.push(vec!(0.0; samples_per_table + 1));
166        }
167        unsafe {
168            let sample = std::slice::from_raw_parts_mut(&mut buf as *mut _ as *mut u8, sample_size);
169            for i in 0..num_tables {
170                let table = &mut samples[i];
171                for j in 0..samples_per_table {
172                    source.read_exact(sample).unwrap();
173                    table[j] = buf as Float;
174                }
175                table[samples_per_table] = table[0]; // Duplicate first entry as last entry for easy interpolation
176                Wavetable::normalize(table);
177            }
178        }
179        Ok(samples)
180    }
181}
182
183
184// ----------------------------------------------
185//                  Unit tests
186// ----------------------------------------------
187
188struct TestContext {
189}
190
191impl TestContext {
192    pub fn new() -> Self {
193        TestContext{}
194    }
195
196    pub fn test(&mut self, ptr: &[u8]) -> bool {
197        let reader = BufReader::new(ptr);
198        let result = WtReader::read_wavetable(reader, 512);
199        match result {
200            Ok(_) => true,
201            Err(()) => false
202        }
203    }
204}
205
206#[test]
207fn single_wave_can_be_read() {
208    let mut context = TestContext::new();
209    assert!(context.test(SINGLE_WAVE));
210}
211
212#[test]
213fn partial_wave_is_rejected() {
214    let mut context = TestContext::new();
215    assert!(!context.test(PARTIAL_WAVE));
216}
217
218#[test]
219fn base_path_is_set_up_correctly() {
220    let wtr = WtReader::new("NoSlash");
221    assert!(wtr.base_path == "NoSlash/".to_string());
222
223    let wtr = WtReader::new("WithSlash/");
224    assert!(wtr.base_path == "WithSlash/".to_string());
225}
226
227const PARTIAL_WAVE: &[u8] = &[
228    // RIFF/ WAVE header
229    'R' as u8, 'I' as u8, 'F' as u8, 'F' as u8,
230    0x04, 0x00, 0x00, 0x00,
231    'W' as u8, 'A' as u8, 'V' as u8, 'E' as u8,
232    // Junk chunk
233    'j' as u8, 'u' as u8, 'n' as u8, 'k' as u8,
234    0x04, 0x00, 0x00, 0x00,
235    0x00, 0x00, 0x00, 0x00,
236    // TODO: fmt chunk
237    // data chunk
238    'd' as u8, 'a' as u8, 't' as u8, 'a' as u8,
239    0x10, 0x00, 0x00, 0x00,
240    0xa0, 0xc4, 0xb8, 0xba,
241    0xa0, 0x54, 0xfc, 0x3a,
242    0xf0, 0x0f, 0xaa, 0x3b,
243    0x54, 0x2f, 0x0a, 0x3c,
244];
245
246const SINGLE_WAVE: &[u8] = &[
247    'R' as u8, 'I' as u8, 'F' as u8, 'F' as u8,
248    0x04, 0x00, 0x00, 0x00,
249    'W' as u8, 'A' as u8, 'V' as u8, 'E' as u8,
250    // Junk chunk
251    'j' as u8, 'u' as u8, 'n' as u8, 'k' as u8,
252    0x04, 0x00, 0x00, 0x00,
253    0x00, 0x00, 0x00, 0x00,
254    // TODO: fmt chunk
255    // data chunk
256    'd' as u8, 'a' as u8, 't' as u8, 'a' as u8,
257    0x00, 0x08, 0x00, 0x00,
258    0xa0, 0xc4, 0xb8, 0xba, 0xa0, 0x54, 0xfc, 0x3a,
259    0xf0, 0x0f, 0xaa, 0x3b, 0x54, 0x2f, 0x0a, 0x3c, 0x9c, 0x35, 0x40, 0x3c, 0x48, 0x43, 0x76, 0x3c,
260    0x2a, 0x33, 0x96, 0x3c, 0x18, 0xb3, 0xb1, 0x3c, 0x1c, 0x50, 0xcd, 0x3c, 0xc5, 0x82, 0xe8, 0x3c,
261    0xa8, 0x39, 0x01, 0x3d, 0x33, 0xdc, 0x0d, 0x3d, 0x3e, 0x6d, 0x1a, 0x3d, 0x8e, 0x5e, 0x27, 0x3d,
262    0x44, 0x7a, 0x34, 0x3d, 0x8b, 0x5b, 0x41, 0x3d, 0xb6, 0x98, 0x4d, 0x3d, 0x25, 0x41, 0x59, 0x3d,
263    0x98, 0x12, 0x65, 0x3d, 0x3c, 0xb0, 0x71, 0x3d, 0x23, 0xf3, 0x7e, 0x3d, 0xef, 0xfa, 0x85, 0x3d,
264    0x19, 0xda, 0x8b, 0x3d, 0x7c, 0x23, 0x91, 0x3d, 0xf7, 0x76, 0x96, 0x3d, 0x46, 0x6f, 0x9c, 0x3d,
265    0xe8, 0xe9, 0xa2, 0x3d, 0x0e, 0x66, 0xa9, 0x3d, 0x0b, 0x3f, 0xaf, 0x3d, 0x3c, 0x83, 0xb4, 0x3d,
266    0x4a, 0xa0, 0xb9, 0x3d, 0xb2, 0xf8, 0xbe, 0x3d, 0x1e, 0x5b, 0xc4, 0x3d, 0x74, 0xcf, 0xc9, 0x3d,
267    0xfe, 0x6f, 0xcf, 0x3d, 0xfa, 0x29, 0xd5, 0x3d, 0x1c, 0xe7, 0xda, 0x3d, 0x8f, 0x6e, 0xe0, 0x3d,
268    0x4b, 0x9f, 0xe5, 0x3d, 0x8c, 0xb4, 0xea, 0x3d, 0x21, 0xcd, 0xef, 0x3d, 0x52, 0xd5, 0xf4, 0x3d,
269    0x38, 0xc8, 0xf9, 0x3d, 0xcd, 0xf7, 0xfe, 0x3d, 0x4e, 0x25, 0x02, 0x3e, 0xe2, 0xcc, 0x04, 0x3e,
270    0x96, 0x64, 0x07, 0x3e, 0x3a, 0xc8, 0x09, 0x3e, 0x08, 0x1c, 0x0c, 0x3e, 0xab, 0x7e, 0x0e, 0x3e,
271    0x7e, 0xf7, 0x10, 0x3e, 0xe8, 0x6f, 0x13, 0x3e, 0xde, 0xdb, 0x15, 0x3e, 0xf0, 0x3c, 0x18, 0x3e,
272    0xdb, 0x81, 0x1a, 0x3e, 0x30, 0xcf, 0x1c, 0x3e, 0x28, 0x0c, 0x1f, 0x3e, 0x19, 0x41, 0x21, 0x3e,
273    0x36, 0x81, 0x23, 0x3e, 0x5e, 0xd1, 0x25, 0x3e, 0xa8, 0x30, 0x28, 0x3e, 0xc1, 0x70, 0x2a, 0x3e,
274    0x78, 0x95, 0x2c, 0x3e, 0x5b, 0xa2, 0x2e, 0x3e, 0x59, 0xa7, 0x30, 0x3e, 0x84, 0xc0, 0x32, 0x3e,
275    0x8e, 0xd2, 0x34, 0x3e, 0x17, 0xf1, 0x36, 0x3e, 0x54, 0x0f, 0x39, 0x3e, 0xf8, 0x46, 0x3b, 0x3e,
276    0x2b, 0x67, 0x3d, 0x3e, 0xb2, 0x64, 0x3f, 0x3e, 0xc8, 0x65, 0x41, 0x3e, 0xcf, 0x43, 0x43, 0x3e,
277    0x78, 0x1c, 0x45, 0x3e, 0x48, 0xe7, 0x46, 0x3e, 0x08, 0xbb, 0x48, 0x3e, 0x09, 0xbf, 0x4a, 0x3e,
278    0xbd, 0xf1, 0x4c, 0x3e, 0xb0, 0x19, 0x4f, 0x3e, 0xc2, 0xd4, 0x50, 0x3e, 0xec, 0x6b, 0x52, 0x3e,
279    0x1a, 0xf4, 0x53, 0x3e, 0xde, 0xb0, 0x55, 0x3e, 0x16, 0xb0, 0x57, 0x3e, 0x88, 0xa3, 0x59, 0x3e,
280    0x82, 0x7f, 0x5b, 0x3e, 0xb9, 0x45, 0x5d, 0x3e, 0xde, 0x09, 0x5f, 0x3e, 0x42, 0xae, 0x60, 0x3e,
281    0x6f, 0x42, 0x62, 0x3e, 0xde, 0xdf, 0x63, 0x3e, 0xba, 0x78, 0x65, 0x3e, 0x81, 0x43, 0x67, 0x3e,
282    0xa2, 0xfb, 0x68, 0x3e, 0xc6, 0xa0, 0x6a, 0x3e, 0x61, 0x25, 0x6c, 0x3e, 0x4d, 0x9e, 0x6d, 0x3e,
283    0x64, 0x2c, 0x6f, 0x3e, 0xe9, 0xb6, 0x70, 0x3e, 0xae, 0x53, 0x72, 0x3e, 0x24, 0xe5, 0x73, 0x3e,
284    0x78, 0x85, 0x75, 0x3e, 0x0a, 0x1a, 0x77, 0x3e, 0x96, 0x96, 0x78, 0x3e, 0x63, 0xfa, 0x79, 0x3e,
285    0x88, 0x36, 0x7b, 0x3e, 0xf4, 0x96, 0x7c, 0x3e, 0xa9, 0xee, 0x7d, 0x3e, 0x3d, 0x48, 0x7f, 0x3e,
286    0x3b, 0x5a, 0x80, 0x3e, 0xa0, 0x16, 0x81, 0x3e, 0x6f, 0xd1, 0x81, 0x3e, 0x05, 0x86, 0x82, 0x3e,
287    0xa8, 0x33, 0x83, 0x3e, 0xa2, 0xcb, 0x83, 0x3e, 0x32, 0x6e, 0x84, 0x3e, 0xa1, 0x10, 0x85, 0x3e,
288    0x8f, 0xa5, 0x85, 0x3e, 0x05, 0x47, 0x86, 0x3e, 0x63, 0xe6, 0x86, 0x3e, 0x74, 0x81, 0x87, 0x3e,
289    0x91, 0x1c, 0x88, 0x3e, 0x80, 0xb9, 0x88, 0x3e, 0x18, 0x47, 0x89, 0x3e, 0xbc, 0xdd, 0x89, 0x3e,
290    0xbf, 0x7c, 0x8a, 0x3e, 0xce, 0x12, 0x8b, 0x3e, 0xcb, 0xaf, 0x8b, 0x3e, 0xea, 0x46, 0x8c, 0x3e,
291    0xec, 0xd7, 0x8c, 0x3e, 0x72, 0x63, 0x8d, 0x3e, 0xd1, 0xde, 0x8d, 0x3e, 0x10, 0x48, 0x8e, 0x3e,
292    0xcd, 0xbb, 0x8e, 0x3e, 0x02, 0x53, 0x8f, 0x3e, 0x50, 0xfc, 0x8f, 0x3e, 0xb5, 0xa4, 0x90, 0x3e,
293    0x10, 0x23, 0x91, 0x3e, 0xa9, 0x8a, 0x91, 0x3e, 0xb3, 0xf0, 0x91, 0x3e, 0x0a, 0x63, 0x92, 0x3e,
294    0xf9, 0xe3, 0x92, 0x3e, 0x3b, 0x65, 0x93, 0x3e, 0x99, 0xdf, 0x93, 0x3e, 0x7a, 0x5c, 0x94, 0x3e,
295    0x20, 0xd7, 0x94, 0x3e, 0xfa, 0x3b, 0x95, 0x3e, 0x42, 0x98, 0x95, 0x3e, 0x4c, 0xf7, 0x95, 0x3e,
296    0x8a, 0x5a, 0x96, 0x3e, 0xfc, 0xd8, 0x96, 0x3e, 0x26, 0x57, 0x97, 0x3e, 0xf6, 0xc6, 0x97, 0x3e,
297    0xc6, 0x37, 0x98, 0x3e, 0xc6, 0xa5, 0x98, 0x3e, 0x2c, 0x01, 0x99, 0x3e, 0x44, 0x59, 0x99, 0x3e,
298    0x72, 0xb0, 0x99, 0x3e, 0x00, 0x04, 0x9a, 0x3e, 0xe2, 0x6a, 0x9a, 0x3e, 0x5a, 0xd2, 0x9a, 0x3e,
299    0x90, 0x31, 0x9b, 0x3e, 0xe0, 0x97, 0x9b, 0x3e, 0x4a, 0xfb, 0x9b, 0x3e, 0xb3, 0x52, 0x9c, 0x3e,
300    0xd1, 0xa3, 0x9c, 0x3e, 0x2b, 0xf3, 0x9c, 0x3e, 0xf4, 0x3b, 0x9d, 0x3e, 0xa8, 0x90, 0x9d, 0x3e,
301    0x78, 0xe1, 0x9d, 0x3e, 0xab, 0x31, 0x9e, 0x3e, 0xd6, 0x86, 0x9e, 0x3e, 0x5a, 0xd8, 0x9e, 0x3e,
302    0x10, 0x26, 0x9f, 0x3e, 0x74, 0x67, 0x9f, 0x3e, 0x46, 0xa5, 0x9f, 0x3e, 0x78, 0xe9, 0x9f, 0x3e,
303    0xd8, 0x44, 0xa0, 0x3e, 0x6a, 0xa4, 0xa0, 0x3e, 0xbb, 0xf5, 0xa0, 0x3e, 0x08, 0x2a, 0xa1, 0x3e,
304    0x02, 0x45, 0xa1, 0x3e, 0x25, 0x75, 0xa1, 0x3e, 0x13, 0xbf, 0xa1, 0x3e, 0x90, 0x18, 0xa2, 0x3e,
305    0x39, 0x6f, 0xa2, 0x3e, 0x6c, 0xb5, 0xa2, 0x3e, 0xf6, 0xf1, 0xa2, 0x3e, 0xec, 0x26, 0xa3, 0x3e,
306    0x45, 0x50, 0xa3, 0x3e, 0x44, 0x64, 0xa3, 0x3e, 0xdb, 0x8b, 0xa3, 0x3e, 0xc1, 0xd8, 0xa3, 0x3e,
307    0x4c, 0x3d, 0xa4, 0x3e, 0x67, 0x99, 0xa4, 0x3e, 0x53, 0xbc, 0xa4, 0x3e, 0x1a, 0xbb, 0xa4, 0x3e,
308    0x73, 0xb8, 0xa4, 0x3e, 0xe2, 0xd9, 0xa4, 0x3e, 0xc0, 0x08, 0xa5, 0x3e, 0x91, 0x41, 0xa5, 0x3e,
309    0xe0, 0x81, 0xa5, 0x3e, 0x78, 0xc9, 0xa5, 0x3e, 0x7c, 0x0c, 0xa6, 0x3e, 0x78, 0x30, 0xa6, 0x3e,
310    0xf8, 0x3a, 0xa6, 0x3e, 0x5d, 0x3f, 0xa6, 0x3e, 0xb1, 0x56, 0xa6, 0x3e, 0x36, 0x7c, 0xa6, 0x3e,
311    0x86, 0xa1, 0xa6, 0x3e, 0x75, 0xcc, 0xa6, 0x3e, 0xf2, 0xf7, 0xa6, 0x3e, 0xeb, 0x22, 0xa7, 0x3e,
312    0x1d, 0x45, 0xa7, 0x3e, 0xea, 0x61, 0xa7, 0x3e, 0x86, 0x78, 0xa7, 0x3e, 0x48, 0x91, 0xa7, 0x3e,
313    0xac, 0xae, 0xa7, 0x3e, 0xde, 0xc6, 0xa7, 0x3e, 0xb3, 0xdf, 0xa7, 0x3e, 0x22, 0xf7, 0xa7, 0x3e,
314    0xf1, 0x0a, 0xa8, 0x3e, 0x84, 0x1c, 0xa8, 0x3e, 0x61, 0x29, 0xa8, 0x3e, 0x2c, 0x35, 0xa8, 0x3e,
315    0x92, 0x43, 0xa8, 0x3e, 0x42, 0x5c, 0xa8, 0x3e, 0x09, 0x74, 0xa8, 0x3e, 0x70, 0x8c, 0xa8, 0x3e,
316    0x00, 0xa5, 0xa8, 0x3e, 0x2c, 0xbc, 0xa8, 0x3e, 0x6c, 0xc9, 0xa8, 0x3e, 0xde, 0xc3, 0xa8, 0x3e,
317    0xed, 0xb8, 0xa8, 0x3e, 0x3d, 0xb9, 0xa8, 0x3e, 0x78, 0xd2, 0xa8, 0x3e, 0x54, 0xf6, 0xa8, 0x3e,
318    0xea, 0x10, 0xa9, 0x3e, 0x56, 0x19, 0xa9, 0x3e, 0x2e, 0x19, 0xa9, 0x3e, 0x75, 0x19, 0xa9, 0x3e,
319    0xb6, 0x17, 0xa9, 0x3e, 0x2a, 0x18, 0xa9, 0x3e, 0x86, 0x18, 0xa9, 0x3e, 0x3e, 0x1d, 0xa9, 0x3e,
320    0x9c, 0x24, 0xa9, 0x3e, 0x3e, 0x29, 0xa9, 0x3e, 0xf7, 0x23, 0xa9, 0x3e, 0x88, 0x1a, 0xa9, 0x3e,
321    0x43, 0x12, 0xa9, 0x3e, 0xfc, 0x0c, 0xa9, 0x3e, 0xfe, 0x0a, 0xa9, 0x3e, 0x86, 0x09, 0xa9, 0x3e,
322    0x86, 0x07, 0xa9, 0x3e, 0x48, 0x07, 0xa9, 0x3e, 0x60, 0x03, 0xa9, 0x3e, 0x22, 0xf6, 0xa8, 0x3e,
323    0x12, 0xe0, 0xa8, 0x3e, 0xc6, 0xcd, 0xa8, 0x3e, 0x02, 0xc4, 0xa8, 0x3e, 0x2a, 0xc5, 0xa8, 0x3e,
324    0x68, 0xc3, 0xa8, 0x3e, 0x48, 0xb8, 0xa8, 0x3e, 0xbe, 0xa3, 0xa8, 0x3e, 0x86, 0x8f, 0xa8, 0x3e,
325    0xe4, 0x82, 0xa8, 0x3e, 0x6e, 0x7b, 0xa8, 0x3e, 0x0e, 0x76, 0xa8, 0x3e, 0x48, 0x6a, 0xa8, 0x3e,
326    0xd0, 0x5c, 0xa8, 0x3e, 0x62, 0x4c, 0xa8, 0x3e, 0xee, 0x34, 0xa8, 0x3e, 0xec, 0x17, 0xa8, 0x3e,
327    0xde, 0xf7, 0xa7, 0x3e, 0x42, 0xe0, 0xa7, 0x3e, 0x82, 0xca, 0xa7, 0x3e, 0xe6, 0xba, 0xa7, 0x3e,
328    0x52, 0xac, 0xa7, 0x3e, 0x2f, 0xa3, 0xa7, 0x3e, 0xe9, 0x9b, 0xa7, 0x3e, 0xf4, 0x89, 0xa7, 0x3e,
329    0x66, 0x6f, 0xa7, 0x3e, 0x04, 0x50, 0xa7, 0x3e, 0x46, 0x31, 0xa7, 0x3e, 0xfa, 0x10, 0xa7, 0x3e,
330    0x4d, 0xf0, 0xa6, 0x3e, 0x28, 0xd4, 0xa6, 0x3e, 0x64, 0xb8, 0xa6, 0x3e, 0x42, 0xa1, 0xa6, 0x3e,
331    0xff, 0x83, 0xa6, 0x3e, 0x70, 0x5e, 0xa6, 0x3e, 0x0a, 0x37, 0xa6, 0x3e, 0x3a, 0x16, 0xa6, 0x3e,
332    0x5c, 0x00, 0xa6, 0x3e, 0x8e, 0xee, 0xa5, 0x3e, 0xc6, 0xd7, 0xa5, 0x3e, 0xab, 0xb2, 0xa5, 0x3e,
333    0xb2, 0x87, 0xa5, 0x3e, 0x78, 0x61, 0xa5, 0x3e, 0xcf, 0x3f, 0xa5, 0x3e, 0xf3, 0x21, 0xa5, 0x3e,
334    0x6d, 0x02, 0xa5, 0x3e, 0x39, 0xe0, 0xa4, 0x3e, 0x36, 0xbb, 0xa4, 0x3e, 0xf6, 0x94, 0xa4, 0x3e,
335    0x98, 0x6a, 0xa4, 0x3e, 0x4d, 0x3d, 0xa4, 0x3e, 0x20, 0x0f, 0xa4, 0x3e, 0x9f, 0xe0, 0xa3, 0x3e,
336    0xd5, 0xae, 0xa3, 0x3e, 0x75, 0x86, 0xa3, 0x3e, 0x96, 0x75, 0xa3, 0x3e, 0x1f, 0x76, 0xa3, 0x3e,
337    0xf4, 0x68, 0xa3, 0x3e, 0x08, 0x36, 0xa3, 0x3e, 0x4c, 0xe7, 0xa2, 0x3e, 0xd3, 0x99, 0xa2, 0x3e,
338    0xf0, 0x6a, 0xa2, 0x3e, 0xeb, 0x50, 0xa2, 0x3e, 0xd1, 0x33, 0xa2, 0x3e, 0x36, 0x0f, 0xa2, 0x3e,
339    0x9b, 0xe4, 0xa1, 0x3e, 0x7c, 0xb4, 0xa1, 0x3e, 0xe4, 0x80, 0xa1, 0x3e, 0xde, 0x49, 0xa1, 0x3e,
340    0x86, 0x10, 0xa1, 0x3e, 0xc1, 0xdd, 0xa0, 0x3e, 0x58, 0xb5, 0xa0, 0x3e, 0xcc, 0x88, 0xa0, 0x3e,
341    0x0c, 0x56, 0xa0, 0x3e, 0xb8, 0x21, 0xa0, 0x3e, 0xd6, 0xe6, 0x9f, 0x3e, 0xc8, 0xb0, 0x9f, 0x3e,
342    0x4a, 0x7b, 0x9f, 0x3e, 0xa8, 0x47, 0x9f, 0x3e, 0xa4, 0x15, 0x9f, 0x3e, 0xe5, 0xea, 0x9e, 0x3e,
343    0xe2, 0xc0, 0x9e, 0x3e, 0x44, 0x8e, 0x9e, 0x3e, 0x32, 0x5a, 0x9e, 0x3e, 0x72, 0x21, 0x9e, 0x3e,
344    0x7f, 0xe7, 0x9d, 0x3e, 0xe6, 0xab, 0x9d, 0x3e, 0x04, 0x6f, 0x9d, 0x3e, 0xd8, 0x3f, 0x9d, 0x3e,
345    0x9a, 0x1e, 0x9d, 0x3e, 0xf8, 0x04, 0x9d, 0x3e, 0xda, 0xd5, 0x9c, 0x3e, 0xf4, 0x87, 0x9c, 0x3e,
346    0x69, 0x32, 0x9c, 0x3e, 0xc4, 0xe7, 0x9b, 0x3e, 0x64, 0xb3, 0x9b, 0x3e, 0x63, 0x85, 0x9b, 0x3e,
347    0xf8, 0x5d, 0x9b, 0x3e, 0xd6, 0x3f, 0x9b, 0x3e, 0xf8, 0x20, 0x9b, 0x3e, 0x98, 0xf0, 0x9a, 0x3e,
348    0xc6, 0x85, 0x9a, 0x3e, 0x3e, 0x0a, 0x9a, 0x3e, 0xbc, 0xa9, 0x99, 0x3e, 0xd1, 0x89, 0x99, 0x3e,
349    0xdd, 0x87, 0x99, 0x3e, 0x18, 0x73, 0x99, 0x3e, 0x88, 0x30, 0x99, 0x3e, 0x23, 0xce, 0x98, 0x3e,
350    0x3c, 0x7a, 0x98, 0x3e, 0x45, 0x3a, 0x98, 0x3e, 0xb3, 0x02, 0x98, 0x3e, 0x0d, 0xd3, 0x97, 0x3e,
351    0xd5, 0x9d, 0x97, 0x3e, 0xfc, 0x6e, 0x97, 0x3e, 0xfe, 0x34, 0x97, 0x3e, 0x2d, 0xf1, 0x96, 0x3e,
352    0x7a, 0x9b, 0x96, 0x3e, 0xc8, 0x4a, 0x96, 0x3e, 0x68, 0x0c, 0x96, 0x3e, 0x1e, 0xd0, 0x95, 0x3e,
353    0x29, 0x9c, 0x95, 0x3e, 0x12, 0x61, 0x95, 0x3e, 0xa0, 0x27, 0x95, 0x3e, 0x00, 0xea, 0x94, 0x3e,
354    0x18, 0xa1, 0x94, 0x3e, 0x92, 0x51, 0x94, 0x3e, 0xc2, 0xfd, 0x93, 0x3e, 0xd6, 0xbb, 0x93, 0x3e,
355    0xc9, 0x7d, 0x93, 0x3e, 0x62, 0x45, 0x93, 0x3e, 0xd4, 0x08, 0x93, 0x3e, 0xb7, 0xcb, 0x92, 0x3e,
356    0x72, 0x8e, 0x92, 0x3e, 0xf0, 0x42, 0x92, 0x3e, 0x86, 0xf0, 0x91, 0x3e, 0x15, 0x9c, 0x91, 0x3e,
357    0x39, 0x54, 0x91, 0x3e, 0x46, 0x1e, 0x91, 0x3e, 0x12, 0xe8, 0x90, 0x3e, 0xe6, 0xa5, 0x90, 0x3e,
358    0xa8, 0x52, 0x90, 0x3e, 0x84, 0x00, 0x90, 0x3e, 0x25, 0xaf, 0x8f, 0x3e, 0x44, 0x64, 0x8f, 0x3e,
359    0xc1, 0x1f, 0x8f, 0x3e, 0xb3, 0xe0, 0x8e, 0x3e, 0x4f, 0xb5, 0x8e, 0x3e, 0xb6, 0x8c, 0x8e, 0x3e,
360    0x0f, 0x51, 0x8e, 0x3e, 0x72, 0xfe, 0x8d, 0x3e, 0x8e, 0xa3, 0x8d, 0x3e, 0x3b, 0x4a, 0x8d, 0x3e,
361    0x63, 0xef, 0x8c, 0x3e, 0x3e, 0x9b, 0x8c, 0x3e, 0x90, 0x50, 0x8c, 0x3e, 0xa7, 0x21, 0x8c, 0x3e,
362    0x67, 0xfc, 0x8b, 0x3e, 0xa1, 0xc3, 0x8b, 0x3e, 0x1a, 0x67, 0x8b, 0x3e, 0x0c, 0xfd, 0x8a, 0x3e,
363    0x4e, 0x9f, 0x8a, 0x3e, 0xf2, 0x5b, 0x8a, 0x3e, 0xb6, 0x25, 0x8a, 0x3e, 0xe3, 0xea, 0x89, 0x3e,
364    0x30, 0xa5, 0x89, 0x3e, 0x6a, 0x5c, 0x89, 0x3e, 0xb2, 0x0e, 0x89, 0x3e, 0xfe, 0xbc, 0x88, 0x3e,
365    0xd6, 0x65, 0x88, 0x3e, 0x12, 0x13, 0x88, 0x3e, 0x70, 0xc9, 0x87, 0x3e, 0x96, 0x85, 0x87, 0x3e,
366    0x47, 0x3e, 0x87, 0x3e, 0xba, 0xed, 0x86, 0x3e, 0x3d, 0x96, 0x86, 0x3e, 0x14, 0x42, 0x86, 0x3e,
367    0x14, 0xfb, 0x85, 0x3e, 0x77, 0xbb, 0x85, 0x3e, 0xd6, 0x7a, 0x85, 0x3e, 0xca, 0x38, 0x85, 0x3e,
368    0x3d, 0xf3, 0x84, 0x3e, 0x40, 0xaa, 0x84, 0x3e, 0x7b, 0x59, 0x84, 0x3e, 0x87, 0x00, 0x84, 0x3e,
369    0x48, 0xa7, 0x83, 0x3e, 0x58, 0x5a, 0x83, 0x3e, 0x04, 0x14, 0x83, 0x3e, 0x49, 0xce, 0x82, 0x3e,
370    0x94, 0x89, 0x82, 0x3e, 0xf7, 0x44, 0x82, 0x3e, 0x8a, 0xfc, 0x81, 0x3e, 0x41, 0xb0, 0x81, 0x3e,
371    0x40, 0x5b, 0x81, 0x3e, 0x2c, 0x05, 0x81, 0x3e, 0xef, 0xb2, 0x80, 0x3e, 0xdc, 0x63, 0x80, 0x3e,
372    0x87, 0x14, 0x80, 0x3e, 0x42, 0x96, 0x7f, 0x3e, 0x11, 0x11, 0x7f, 0x3e, 0x8e, 0x85, 0x7e, 0x3e,
373    0xd3, 0xdf, 0x7d, 0x3e, 0xca, 0x0e, 0x7d, 0x3e, 0x33, 0x3a, 0x7c, 0x3e, 0x77, 0x96, 0x7b, 0x3e,
374    0xd8, 0x32, 0x7b, 0x3e, 0xee, 0xdc, 0x7a, 0x3e, 0x82, 0x56, 0x7a, 0x3e, 0x8b, 0x93, 0x79, 0x3e,
375    0x5c, 0xba, 0x78, 0x3e, 0xe2, 0xfd, 0x77, 0x3e, 0x09, 0x64, 0x77, 0x3e, 0x10, 0xd9, 0x76, 0x3e,
376    0x79, 0x49, 0x76, 0x3e, 0xac, 0xb9, 0x75, 0x3e, 0xfa, 0x27, 0x75, 0x3e, 0xa8, 0x90, 0x74, 0x3e,
377    0x2e, 0xf2, 0x73, 0x3e, 0xb0, 0x50, 0x73, 0x3e, 0x0c, 0xa7, 0x72, 0x3e, 0xe7, 0xef, 0x71, 0x3e,
378    0x9e, 0x35, 0x71, 0x3e, 0xf5, 0x8f, 0x70, 0x3e, 0x3b, 0x1d, 0x70, 0x3e, 0x66, 0xc2, 0x6f, 0x3e,
379    0x9b, 0x47, 0x6f, 0x3e, 0x84, 0x8d, 0x6e, 0x3e, 0xac, 0xaf, 0x6d, 0x3e, 0x6d, 0xe3, 0x6c, 0x3e,
380    0x0c, 0x3e, 0x6c, 0x3e, 0xd6, 0xb2, 0x6b, 0x3e, 0x3a, 0x26, 0x6b, 0x3e, 0x54, 0x96, 0x6a, 0x3e,
381    0x2c, 0x08, 0x6a, 0x3e, 0x9f, 0x70, 0x69, 0x3e, 0x88, 0xc7, 0x68, 0x3e, 0x88, 0x0d, 0x68, 0x3e,
382    0xd7, 0x5b, 0x67, 0x3e, 0x53, 0xb9, 0x66, 0x3e, 0x20, 0x21, 0x66, 0x3e, 0xd8, 0x8f, 0x65, 0x3e,
383    0xc8, 0xf6, 0x64, 0x3e, 0x4d, 0x63, 0x64, 0x3e, 0x2f, 0xc8, 0x63, 0x3e, 0x01, 0x25, 0x63, 0x3e,
384    0xb4, 0x6e, 0x62, 0x3e, 0x9f, 0xc2, 0x61, 0x3e, 0xd6, 0x28, 0x61, 0x3e, 0xe4, 0x9b, 0x60, 0x3e,
385    0x8a, 0x15, 0x60, 0x3e, 0x64, 0x7b, 0x5f, 0x3e, 0x82, 0xd7, 0x5e, 0x3e, 0x25, 0x2b, 0x5e, 0x3e,
386    0xae, 0x70, 0x5d, 0x3e, 0x5f, 0x96, 0x5c, 0x3e, 0x47, 0xc9, 0x5b, 0x3e, 0x7b, 0x40, 0x5b, 0x3e,
387];