autd3_modulation_audio_file/
csv.rs1use autd3_core::derive::*;
2
3use std::{fmt::Debug, fs::File, path::Path};
4
5use crate::error::AudioFileError;
6
7#[derive(Debug, Clone)]
9pub struct CsvOption {
10 pub delimiter: u8,
12}
13
14impl Default for CsvOption {
15 fn default() -> Self {
16 Self { delimiter: b',' }
17 }
18}
19
20#[derive(Modulation, Debug, Clone)]
22pub struct Csv<P, Config>
23where
24 P: AsRef<Path> + Clone + Debug,
25 Config: Into<SamplingConfig> + Debug + Copy,
26{
27 pub path: P,
29 pub sampling_config: Config,
31 pub option: CsvOption,
33}
34
35impl<P, Config> Csv<P, Config>
36where
37 P: AsRef<Path> + Clone + Debug,
38 Config: Into<SamplingConfig> + Debug + Copy,
39{
40 #[must_use]
42 pub const fn new(path: P, sampling_config: Config, option: CsvOption) -> Self {
43 Self {
44 path,
45 sampling_config,
46 option,
47 }
48 }
49
50 #[tracing::instrument]
51 fn read_buf(&self) -> Result<Vec<u8>, AudioFileError> {
52 let f = File::open(&self.path)?;
53 let mut rdr = csv::ReaderBuilder::new()
54 .has_headers(false)
55 .delimiter(self.option.delimiter)
56 .from_reader(f);
57 Ok(rdr
58 .records()
59 .map(|r| {
60 let record = r?;
61 csv::Result::Ok(
62 record
63 .iter()
64 .map(|x| x.trim().to_owned())
65 .collect::<Vec<_>>(),
66 )
67 })
68 .collect::<csv::Result<Vec<_>>>()?
69 .into_iter()
70 .flatten()
71 .map(|s| s.parse::<u8>())
72 .collect::<Result<Vec<u8>, _>>()?)
73 }
74}
75
76impl<P, Config> Modulation for Csv<P, Config>
77where
78 P: AsRef<Path> + Clone + Debug,
79 Config: Into<SamplingConfig> + Debug + Copy,
80{
81 fn calc(self) -> Result<Vec<u8>, ModulationError> {
82 let buffer = self.read_buf()?;
83 tracing::debug!("Read buffer: {:?}", buffer);
84 Ok(buffer)
85 }
86
87 fn sampling_config(&self) -> SamplingConfig {
88 self.sampling_config.into()
89 }
90}
91
92#[cfg(test)]
93mod tests {
94 use autd3_core::common::{Freq, Hz};
95
96 use super::*;
97 use std::io::Write;
98
99 fn create_csv(path: impl AsRef<Path>, data: &[u8]) -> anyhow::Result<()> {
100 let mut f = File::create(path)?;
101 data.iter().try_for_each(|d| writeln!(f, "{}", d))?;
102 Ok(())
103 }
104
105 #[rstest::rstest]
106 #[test]
107 #[case(vec![0xFF, 0x7F, 0x00], 4000. * Hz)]
108 fn new(#[case] data: Vec<u8>, #[case] sample_rate: Freq<f32>) -> anyhow::Result<()> {
109 let dir = tempfile::tempdir().unwrap();
110 let path = dir.path().join("tmp.csv");
111 create_csv(&path, &data)?;
112
113 let m = Csv::new(path, sample_rate, CsvOption::default());
114 assert_eq!(sample_rate.hz(), m.sampling_config().freq()?.hz());
115 assert_eq!(data, *m.calc()?);
116
117 Ok(())
118 }
119
120 #[test]
121 fn not_exists() -> anyhow::Result<()> {
122 let m = Csv {
123 path: Path::new("not_exists.csv"),
124 sampling_config: 4000. * Hz,
125 option: CsvOption::default(),
126 };
127 assert!(m.calc().is_err());
128 Ok(())
129 }
130}