Skip to main content

dxx/
lib.rs

1/// dxx is a library for io and converting audio files with a .DXX extension.
2use std::io::prelude::*;
3use std::io::{BufReader, BufWriter};
4use std::fmt;
5use std::fmt::{Formatter, Display};
6use std::str;
7use std::str::FromStr;
8use std::error::Error;
9use std::fs;
10use std::fs::File;
11use byteorder::{ReadBytesExt, WriteBytesExt, LittleEndian};
12
13const TEXT_BIN_FILE_SIZE_MEAN_RATE: &'static usize = &13;
14const DSX_AMP: i16 = i16::max_value();
15const DFX_AMP: f32 = 10000.;
16const DDX_AMP: f64 = 10000.;
17
18/// DType is an enum for describing data type of file.
19pub enum DType {
20    DSA,
21    DFA,
22    DDA,
23    DSB,
24    DFB,
25    DDB,
26}
27
28impl Display for DType {
29    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
30        match *self {
31            DType::DSA => write!(f, "DSA"),
32            DType::DFA => write!(f, "DFA"),
33            DType::DDA => write!(f, "DDA"),
34            DType::DSB => write!(f, "DSB"),
35            DType::DFB => write!(f, "DFB"),
36            DType::DDB => write!(f, "DDB"),
37        }
38    }
39}
40
41impl FromStr for DType {
42    type Err = &'static str;
43
44    fn from_str(s: &str) -> Result<Self, Self::Err> {
45        match s {
46            "DSA" => Ok(DType::DSA),
47            "DFA" => Ok(DType::DFA),
48            "DDA" => Ok(DType::DDA),
49            "DSB" => Ok(DType::DSB),
50            "DFB" => Ok(DType::DFB),
51            "DDB" => Ok(DType::DDB),
52            _ => Err("invalid string")
53        }
54    }
55}
56
57impl DType {
58    /// from_filename determines the data type from the specified file name.
59    pub fn from_filename(filename: &str) -> Result<DType, &'static str> {
60        let suffix = match filename.split(".").last() {
61            Some(s) => s,
62            None => return Err("invalid file name") // TODO improve filename
63        };
64        DType::from_str(suffix)
65    }
66
67    /// byte_width returns the byte width of a sample.
68    pub fn byte_width(&self) -> u32 {
69        match *self {
70            DType::DSA | DType::DSB => 2,
71            DType::DFA | DType::DFB => 4,
72            DType::DDA | DType::DDB => 8,
73        }
74    }
75
76    /// byte_width returns the bits width of a sample.
77    pub fn bits_width(&self) -> u32 {
78        match *self {
79            DType::DSA | DType::DSB => 16,
80            DType::DFA | DType::DFB => 32,
81            DType::DDA | DType::DDB => 64,
82        }
83    }
84}
85
86/// len_file returns the byte length of the specified file.
87pub fn len_file(filename: &str) -> Result<u64, Box<dyn Error>> {
88    let meta = fs::metadata(filename)?;
89    Ok(meta.len())
90}
91
92/// read_file reads .DXX file.
93/// This func determines the data type from the filename extension and reads that data.
94/// The return type is Vec<f64> to make the data easier to handle.
95pub fn read_file(filename: &str) -> Result<Vec<f64>, Box<dyn Error>> {
96    let mut f = File::open(filename)?;
97    let file_size = f.metadata()?.len() as usize;
98    let dtype = DType::from_filename(filename)?;
99
100    match dtype {
101        DType::DSA |
102        DType::DFA |
103        DType::DDA => read_dxa(&mut f, file_size),
104
105        DType::DSB => read_dsb(&mut f, file_size),
106        DType::DFB => read_dfb(&mut f, file_size),
107        DType::DDB => read_ddb(&mut f, file_size),
108    }
109}
110
111
112fn read_dxa<T: Read>(src: &mut T, size: usize) -> Result<Vec<f64>, Box<dyn Error>> {
113    let mut ret: Vec<f64> = Vec::with_capacity(size / *TEXT_BIN_FILE_SIZE_MEAN_RATE);
114    for result in BufReader::new(src).lines() {
115        let line = result?;
116        let buf: f64 = line.parse::<f64>()?;
117        ret.push(buf);
118    }
119    Ok(ret)
120}
121
122fn read_dsb<T: Read>(src: &mut T, size: usize) -> Result<Vec<f64>, Box<dyn Error>> {
123    let byte_width = DType::DSB.byte_width();
124    let mut buf: Vec<i16> = vec![0; size / byte_width as usize];
125    let mut reader = BufReader::new(src);
126    reader.read_i16_into::<LittleEndian>(&mut buf)?;
127    Ok(buf.iter().map(|x| f64::from(*x)).collect())
128}
129
130fn read_dfb<T: Read>(src: &mut T, size: usize) -> Result<Vec<f64>, Box<dyn Error>> {
131    let byte_width = DType::DFB.byte_width();
132    let mut buf: Vec<f32> = vec![0.; size / byte_width as usize];
133    let mut reader = BufReader::new(src);
134    reader.read_f32_into::<LittleEndian>(&mut buf)?;
135    Ok(buf.iter().map(|x| f64::from(*x)).collect())
136}
137
138fn read_ddb<T: Read>(src: &mut T, size: usize) -> Result<Vec<f64>, Box<dyn Error>> {
139    let byte_width = DType::DDB.byte_width();
140    let mut buf: Vec<f64> = vec![0.; size / byte_width as usize];
141    let mut reader = BufReader::new(src);
142    reader.read_f64_into::<LittleEndian>(&mut buf)?;
143    Ok(buf.iter().map(|x| f64::from(*x)).collect())
144}
145
146/// write_file writes data to .DXX file.
147/// This func determines the data type from the filename extension and writes the data to the file.
148pub fn write_file(filename: &str, src: Vec<f64>) -> Result<(), Box<dyn Error>> {
149    let mut f = File::create(filename)?;
150    let dtype = DType::from_filename(filename)?;
151
152    match dtype {
153        DType::DSA => write_dxa(&mut f, f64s_to_i16s(&src, DSX_AMP)),
154        DType::DFA => write_dxa(&mut f, f64s_to_f32s(&src, DFX_AMP)),
155        DType::DDA => write_dxa(&mut f, normalize_f64s(src, DDX_AMP)),
156
157        DType::DSB => write_dsb(&mut f, f64s_to_i16s(&src, DSX_AMP)),
158        DType::DFB => write_dfb(&mut f, f64s_to_f32s(&src, DFX_AMP)),
159        DType::DDB => write_ddb(&mut f, normalize_f64s(src, DDX_AMP)),
160    }
161}
162
163fn write_dxa<T: Write, U: std::fmt::Display>(dst: T, src: Vec<U>) -> Result<(), Box<dyn Error>> {
164    let mut writer = BufWriter::new(dst);
165    for x in src {
166        writeln!(writer, "{}", x)?;
167    }
168    Ok(())
169}
170
171fn write_dsb<T: Write>(dst: T, src: Vec<i16>) -> Result<(), Box<dyn Error>> {
172    let mut writer = BufWriter::new(dst);
173    for x in src {
174        writer.write_i16::<LittleEndian>(x)?;
175    }
176    Ok(())
177}
178
179fn write_dfb<T: Write>(dst: T, src: Vec<f32>) -> Result<(), Box<dyn Error>> {
180    let mut writer = BufWriter::new(dst);
181    for x in src {
182        writer.write_f32::<LittleEndian>(x)?;
183    }
184    Ok(())
185}
186
187fn write_ddb<T: Write>(dst: T, src: Vec<f64>) -> Result<(), Box<dyn Error>> {
188    let mut writer = BufWriter::new(dst);
189    for x in src {
190        writer.write_f64::<LittleEndian>(x)?;
191    }
192    Ok(())
193}
194
195fn normalize_f64s(src: Vec<f64>, amp: f64) -> Vec<f64> {
196    let abs_src: Vec<f64> = src.iter().map(|x| x.clone().abs()).collect();
197    let max = max_f64s(&abs_src);
198    src.iter().map(|x| (x / max * amp)).collect()
199}
200
201fn f64s_to_i16s(src: &Vec<f64>, amp: i16) -> Vec<i16> {
202    let abs_src: Vec<f64> = src.iter().map(|x| x.clone().abs()).collect();
203    let max = max_f64s(&abs_src);
204    src.iter().map(|x| (x / max * amp as f64) as i16).collect()
205}
206
207fn f64s_to_f32s(src: &Vec<f64>, amp: f32) -> Vec<f32> {
208    let abs_src: Vec<f64> = src.iter().map(|x| x.clone().abs()).collect();
209    let max = max_f64s(&abs_src);
210    src.iter().map(|x| (x / max * amp as f64) as f32).collect()
211}
212
213fn max_f64s(src: &Vec<f64>) -> f64 {
214    src.iter().fold(0.0 / 0.0, |m, v| v.max(m))
215}
216
217#[cfg(test)]
218mod tests {
219    use crate::*;
220
221    #[test]
222    fn test_f64s_to_i16s() {
223        let src: Vec<f64> = vec![5., -2., 4., -3.];
224        assert_eq!(f64s_to_i16s(&src, DSX_AMP), vec![32767, -13106, 26213, -19660]);
225    }
226
227    #[test]
228    fn test_write_file() {
229        let src: Vec<f64> = vec![5., -2., 4., -3.];
230        write_file("a.DSA", src).unwrap();
231        let src: Vec<f64> = vec![5., -2., 4., -3.];
232        write_file("a.DFA", src).unwrap();
233        let src: Vec<f64> = vec![5., -2., 4., -3.];
234        write_file("a.DDA", src).unwrap();
235        let src: Vec<f64> = vec![5., -2., 4., -3.];
236        write_file("a.DSB", src).unwrap();
237        let src: Vec<f64> = vec![5., -2., 4., -3.];
238        write_file("a.DFB", src).unwrap();
239        let src: Vec<f64> = vec![5., -2., 4., -3.];
240        write_file("a.DDB", src).unwrap();
241    }
242
243    #[test]
244    fn test_convert() {
245        let data = read_file("sine.DSB").unwrap();
246        write_file("sine.DSA", data).unwrap();
247        let data = read_file("sine.DSB").unwrap();
248        write_file("sine.DFA", data).unwrap();
249        let data = read_file("sine.DSB").unwrap();
250        write_file("sine.DFB", data).unwrap();
251        let data = read_file("sine.DSB").unwrap();
252        write_file("sine.DDA", data).unwrap();
253        let data = read_file("sine.DSB").unwrap();
254        write_file("sine.DDB", data).unwrap();
255        let data = read_file("sine.DSA").unwrap();
256        write_file("sine1.DSB", data).unwrap();
257    }
258}