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 fn read_buf(&self) -> Result<Vec<u8>, AudioFileError> {
51 let f = File::open(&self.path)?;
52 let mut rdr = csv::ReaderBuilder::new()
53 .has_headers(false)
54 .delimiter(self.option.delimiter)
55 .from_reader(f);
56 Ok(rdr
57 .records()
58 .map(|r| {
59 let record = r?;
60 csv::Result::Ok(
61 record
62 .iter()
63 .map(|x| x.trim().to_owned())
64 .collect::<Vec<_>>(),
65 )
66 })
67 .collect::<csv::Result<Vec<_>>>()?
68 .into_iter()
69 .flatten()
70 .map(|s| s.parse::<u8>())
71 .collect::<Result<Vec<u8>, _>>()?)
72 }
73}
74
75impl<P, Config> Modulation for Csv<P, Config>
76where
77 P: AsRef<Path> + Clone + Debug,
78 Config: Into<SamplingConfig> + Debug + Copy,
79{
80 fn calc(self, _: &FirmwareLimits) -> Result<Vec<u8>, ModulationError> {
81 Ok(self.read_buf()?)
82 }
83
84 fn sampling_config(&self) -> SamplingConfig {
85 self.sampling_config.into()
86 }
87}
88
89#[cfg(test)]
90mod tests {
91 use autd3_core::common::{Freq, Hz};
92
93 use super::*;
94 use std::io::Write;
95
96 fn create_csv(path: impl AsRef<Path>, data: &[u8]) -> anyhow::Result<()> {
97 let mut f = File::create(path)?;
98 data.iter().try_for_each(|d| writeln!(f, "{d}"))?;
99 Ok(())
100 }
101
102 #[rstest::rstest]
103 #[test]
104 #[case(vec![0xFF, 0x7F, 0x00], 4000. * Hz)]
105 fn new(#[case] data: Vec<u8>, #[case] sample_rate: Freq<f32>) -> anyhow::Result<()> {
106 let dir = tempfile::tempdir().unwrap();
107 let path = dir.path().join("tmp.csv");
108 create_csv(&path, &data)?;
109
110 let m = Csv::new(path, sample_rate, CsvOption::default());
111 assert_eq!(sample_rate.hz(), m.sampling_config().freq()?.hz());
112 assert_eq!(data, *m.calc(&FirmwareLimits::unused())?);
113
114 Ok(())
115 }
116
117 #[test]
118 fn not_exists() -> anyhow::Result<()> {
119 let m = Csv {
120 path: Path::new("not_exists.csv"),
121 sampling_config: 4000. * Hz,
122 option: CsvOption::default(),
123 };
124 assert!(m.calc(&FirmwareLimits::unused()).is_err());
125 Ok(())
126 }
127}