x3/
decodefile.rs

1/**************************************************************************
2 *                                                                        *
3 * Rust implementation of the X3 lossless audio compression protocol.     *
4 *                                                                        *
5 * Copyright (C) 2019 Simon M. Werner <simonwerner@gmail.com>             *
6 *                                                                        *
7 * This program is free software; you can redistribute it and/or modify   *
8 * it under the terms of the GNU General Public License as published by   *
9 * the Free Software Foundation, either version 3 of the License, or      *
10 * (at your option) any later version.                                    *
11 *                                                                        *
12 * This program is distributed in the hope that it will be useful,        *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of         *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the           *
15 * GNU General Public License for more details.                           *
16 *                                                                        *
17 * You should have received a copy of the GNU General Public License      *
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.   *
19 *                                                                        *
20 **************************************************************************/
21
22// std
23use std::fs::File;
24use std::io::{prelude::*, BufReader};
25use std::path;
26
27// externs
28use crate::hound;
29
30// this crate
31use crate::decoder;
32use crate::error;
33use crate::{crc, x3};
34
35use crate::x3::{FrameHeader, X3aSpec};
36use error::X3Error;
37use quick_xml::events::Event;
38use quick_xml::Reader;
39
40pub const X3_READ_BUFFER_SIZE: usize = 1024 * 24;
41pub const X3_WRITE_BUFFER_SIZE: usize = X3_READ_BUFFER_SIZE * 8;
42
43pub struct X3aReader {
44  reader: BufReader<File>,
45  spec: X3aSpec,
46  remaing_bytes: usize,
47  read_buf: [u8; X3_READ_BUFFER_SIZE],
48
49  /// The count of errors.
50  /// TODO: Count each type of error
51  frame_errors: usize,
52}
53
54impl X3aReader {
55  pub fn open<P: AsRef<path::Path>>(filename: P) -> Result<Self, X3Error> {
56    let file = File::open(filename).unwrap();
57    let mut remaing_bytes = file.metadata()?.len() as usize;
58    let mut reader = BufReader::with_capacity(64 * 1024, file);
59
60    let (spec, header_size) = read_archive_header(&mut reader)?;
61    remaing_bytes -= header_size;
62
63    Ok(Self {
64      reader,
65      spec,
66      remaing_bytes,
67      read_buf: [0u8; X3_READ_BUFFER_SIZE],
68      frame_errors: 0,
69    })
70  }
71
72  pub fn spec(&self) -> &X3aSpec {
73    &self.spec
74  }
75
76  fn read_bytes(&mut self, mut buf_len: usize) -> std::io::Result<()> {
77    if self.remaing_bytes < buf_len {
78      buf_len = self.remaing_bytes;
79    }
80    self.remaing_bytes -= buf_len;
81    self.reader.read_exact(&mut self.read_buf[0..buf_len])
82  }
83
84  fn read_frame_header(&mut self) -> Result<FrameHeader, X3Error> {
85    self.read_bytes(x3::FrameHeader::LENGTH)?;
86    decoder::read_frame_header(&self.read_buf[0..x3::FrameHeader::LENGTH])
87  }
88
89  fn read_frame_payload(&mut self, header: &FrameHeader) -> Result<(), X3Error> {
90    self.read_bytes(header.payload_len)?;
91
92    let payload = &self.read_buf[0..header.payload_len];
93    let crc = crc::crc16(&payload);
94    if crc != header.payload_crc {
95      return Err(X3Error::FrameHeaderInvalidPayloadCRC);
96    }
97
98    Ok(())
99  }
100
101  pub fn decode_next_frame(&mut self, wav_buf: &mut [i16; X3_WRITE_BUFFER_SIZE]) -> Result<Option<usize>, X3Error> {
102    // We have reached the end of the file
103    if self.remaing_bytes <= x3::FrameHeader::LENGTH {
104      return Ok(None);
105    }
106
107    // Get the header details
108    let frame_header = self.read_frame_header()?;
109    let samples = frame_header.samples as usize;
110    if self.remaing_bytes < frame_header.payload_len {
111      return Ok(None);
112    }
113
114    if frame_header.payload_len > X3_READ_BUFFER_SIZE {
115      // Payload is larger than the available buffer size
116      return Err(X3Error::FrameHeaderInvalidPayloadLen);
117    }
118
119    // Get the Payload
120    self.read_frame_payload(&frame_header)?;
121    let x3_bytes = &mut self.read_buf[0..frame_header.payload_len];
122
123    // Do the decoding
124    match decoder::decode_frame(x3_bytes, wav_buf, &self.spec.params, samples) {
125      Ok(result) => Ok(result),
126      Err(err) => {
127        self.frame_errors += 1;
128        println!("Frame error: {:?}", err);
129        Ok(None)
130      }
131    }
132  }
133}
134
135///
136/// Read the <Archive Header> from in the input buffer.
137///
138fn read_archive_header(reader: &mut BufReader<File>) -> Result<(X3aSpec, usize), X3Error> {
139  // <Archive Id>
140  {
141    let mut arc_header = [0u8; x3::Archive::ID.len()];
142    reader.read_exact(&mut arc_header)?;
143    if !arc_header.eq(x3::Archive::ID) {
144      return Err(X3Error::ArchiveHeaderXMLInvalidKey);
145    }
146  }
147
148  // <XML MetaData>
149  let header = {
150    let mut header_buf = [0u8; x3::FrameHeader::LENGTH];
151    reader.read_exact(&mut header_buf)?;
152    decoder::read_frame_header(&mut header_buf)?
153  };
154
155  // Get the payload
156  let mut payload: Vec<u8> = vec![0; header.payload_len];
157  reader.read_exact(&mut payload)?;
158  let xml = String::from_utf8_lossy(&payload);
159
160  let (sample_rate, params) = parse_xml(&xml)?;
161
162  let header_size = x3::FrameHeader::LENGTH + payload.len();
163
164  Ok((
165    X3aSpec {
166      sample_rate,
167      params,
168      channels: header.channels,
169    },
170    header_size,
171  ))
172}
173
174///
175/// Convert an .x3a (X3 Archive) file to a .wav file.  
176///
177/// Note: the x3a can contain some meta data of the recording that may be lost, such as the time
178///       of the recording and surplus XML payload data that has been embedded into the X3A header.
179///
180/// ### Arguments
181///
182/// * `x3a_filename` - the input X3A file to decode.
183/// * `wav_filename` - the output wav file to write to.  It will be overwritten.
184///
185pub fn x3a_to_wav<P: AsRef<path::Path>>(x3a_filename: P, wav_filename: P) -> Result<(), X3Error> {
186  let mut x3a_reader = X3aReader::open(x3a_filename)?;
187
188  let x3_spec = x3a_reader.spec();
189  let spec = hound::WavSpec {
190    channels: 1, //x3_spec.channels as u16,
191    sample_rate: x3_spec.sample_rate,
192    bits_per_sample: 16,
193    sample_format: hound::SampleFormat::Int,
194  };
195
196  let mut writer = hound::WavWriter::create(wav_filename, spec)?;
197  let mut wav = [0i16; X3_WRITE_BUFFER_SIZE];
198  loop {
199    match x3a_reader.decode_next_frame(&mut wav)? {
200      Some(samples) => {
201        write_samples(&mut writer, &wav, samples)?;
202      }
203      None => break,
204    }
205  }
206
207  Ok(())
208}
209
210fn write_samples(
211  writer: &mut hound::WavWriter<std::io::BufWriter<std::fs::File>>,
212  buf: &[i16],
213  num_samples: usize,
214) -> Result<(), X3Error> {
215  let mut fast_writer = writer.get_i16_writer(num_samples as u32);
216  for i in 0..num_samples {
217    unsafe {
218      fast_writer.write_sample_unchecked(buf[i]);
219    }
220  }
221  fast_writer.flush()?;
222  Ok(())
223}
224
225///
226/// Parse the XML header that contains the parameters for the wav output.
227///
228fn parse_xml(xml: &str) -> Result<(u32, x3::Parameters), X3Error> {
229  let mut reader = Reader::from_str(xml);
230  reader.trim_text(true);
231
232  let mut buf = Vec::new();
233  let mut fs = Vec::with_capacity(3);
234  let mut bl = Vec::with_capacity(3);
235  let mut codes = Vec::with_capacity(3);
236  let mut th = Vec::with_capacity(3);
237
238  // The `Reader` does not implement `Iterator` because it outputs borrowed data (`Cow`s)
239  loop {
240    match reader.read_event(&mut buf) {
241      Ok(Event::Start(ref e)) => match e.name() {
242        b"FS" => fs.push(reader.read_text(e.name(), &mut Vec::new()).unwrap()),
243        b"BLKLEN" => bl.push(reader.read_text(e.name(), &mut Vec::new()).unwrap()),
244        b"CODES" => codes.push(reader.read_text(e.name(), &mut Vec::new()).unwrap()),
245        b"T" => th.push(reader.read_text(e.name(), &mut Vec::new()).unwrap()),
246        _ => (),
247      },
248      Ok(Event::Eof) => break, // exits the loop when reaching end of file
249      Err(e) => {
250        println!(
251          "Error reading X3 Archive header (XML) at position {}: {:?}",
252          reader.buffer_position(),
253          e
254        );
255        return Err(X3Error::ArchiveHeaderXMLInvalid);
256      }
257      _ => (), // There are several other `Event`s we do not consider here
258    }
259
260    // if we don't keep a borrow elsewhere, we can clear the buffer to keep memory usage low
261    buf.clear();
262  }
263  println!("sample rate: {}", fs[0]);
264  println!("block length: {}", bl[0]);
265  println!("Rice codes: {}", codes[0]);
266  println!("thresholds: {}", th[0]);
267
268  let sample_rate = fs[0].parse::<u32>().unwrap();
269  let block_len = bl[0].parse::<u32>().unwrap();
270  let mut rice_code_ids = Vec::new();
271  for word in codes[0].split(',') {
272    match word {
273      "RICE0" => rice_code_ids.push(0),
274      "RICE1" => rice_code_ids.push(1),
275      "RICE2" => rice_code_ids.push(2),
276      "RICE3" => rice_code_ids.push(3),
277      "BFP" => (),
278      _ => return Err(X3Error::ArchiveHeaderXMLRiceCode),
279    };
280  }
281  let thresholds: Vec<usize> = th[0].split(',').map(|s| s.parse::<usize>().unwrap()).collect();
282
283  let mut rc_array: [usize; 3] = [0; 3];
284  let mut th_array: [usize; 3] = [0; 3];
285
286  #[allow(clippy::manual_memcpy)]
287  for i in 0..3 {
288    rc_array[i] = rice_code_ids[i];
289    th_array[i] = thresholds[i];
290  }
291  let params = x3::Parameters::new(
292    block_len as usize,
293    x3::Parameters::DEFAULT_BLOCKS_PER_FRAME,
294    rc_array,
295    th_array,
296  )?;
297
298  Ok((sample_rate, params))
299}
300
301//
302//
303//            #######
304//               #       ######     ####     #####     ####
305//               #       #         #           #      #
306//               #       #####      ####       #       ####
307//               #       #              #      #           #
308//               #       #         #    #      #      #    #
309//               #       ######     ####       #       ####
310//
311//
312
313#[cfg(test)]
314mod tests {
315  // use crate::decodefile::x3a_to_wav;
316
317  // #[test]
318  // fn test_decode_x3a_file() {
319  //   x3a_to_wav("~/tmp/test.x3a", "~/tmp/test.wav").unwrap();
320  // }
321}