ndsd_playback/dsd_readers/
dff_reader.rs1use byteorder::{BigEndian, ReadBytesExt};
2use std::fs::File;
3use std::io;
4use std::io::{Read, Seek, SeekFrom};
5use crate::dsd_readers::{DSDFormat, DSDReader};
6
7pub struct DFFReader {
8 file: File,
9 buf: Vec<u8>, ch: usize, block_frames: usize, filled_frames: usize, pos_frames: usize, total_frames: u64, read_frames: u64, data_start: u64, }
18
19impl DFFReader {
20 pub fn new(path: &str) -> io::Result<Self> {
21 let file = File::open(path)?;
22 Ok(Self {
23 file,
24 buf: Vec::new(),
25 ch: 0,
26 block_frames: 4096, filled_frames: 0,
28 pos_frames: 0,
29 total_frames: 0,
30 read_frames: 0,
31 data_start: 0,
32 })
33 }
34
35 pub fn empty() -> Self {
36 Self {
37 file: File::create("super_empty").unwrap(),
38 buf: Vec::new(),
39 ch: 0,
40 block_frames: 4096,
41 filled_frames: 0,
42 pos_frames: 0,
43 total_frames: 0,
44 read_frames: 0,
45 data_start: 0,
46 }
47 }
48
49 fn read_id(&mut self) -> io::Result<[u8; 4]> {
51 let mut id = [0u8; 4];
52 self.file.read_exact(&mut id)?;
53 Ok(id)
54 }
55
56 fn read_be_u64(&mut self) -> io::Result<u64> {
58 self.file.read_u64::<BigEndian>()
59 }
60}
61
62impl DSDReader for DFFReader {
63 fn open(&mut self, format: &mut DSDFormat) -> io::Result<()> {
64 let id = self.read_id()?;
66 if &id != b"FRM8" {
67 return Err(io::Error::new(io::ErrorKind::InvalidData, "not FRM8 / DFF"));
68 }
69
70 let _frm8_size = self.read_be_u64()?; let fmt_id = self.read_id()?;
75 if &fmt_id != b"DSD " {
76 return Err(io::Error::new(io::ErrorKind::InvalidData, "not DSD container"));
77 }
78
79 let found_dsd;
82 let dsd_chunk_size: u64;
83
84 let mut sample_rate_hz: Option<u32> = None;
86 let mut channels: Option<u16> = None;
87 format.is_lsb_first = false;
89
90 loop {
91 let mut chunk_id = [0u8; 4];
93 if let Err(e) = self.file.read_exact(&mut chunk_id) {
95 return Err(io::Error::new(io::ErrorKind::InvalidData, format!("unexpected EOF reading chunk id: {}", e)));
96 }
97 let chunk_size = self.read_be_u64()?;
98 let chunk_payload_start = self.file.seek(SeekFrom::Current(0))?;
99
100 match &chunk_id {
101 b"PROP" => {
102 let mut prop_id = [0u8; 4];
104 self.file.read_exact(&mut prop_id)?;
105 if &prop_id == b"SND " {
106 let prop_end = chunk_payload_start + chunk_size;
108 while self.file.seek(SeekFrom::Current(0))? < prop_end {
109 let mut sub_id = [0u8; 4];
111 if let Err(e) = self.file.read_exact(&mut sub_id) {
112 return Err(io::Error::new(io::ErrorKind::InvalidData, format!("unexpected EOF in SND subchunk id: {}", e)));
113 }
114 let sub_size = self.read_be_u64()?;
115 let sub_payload_start = self.file.seek(SeekFrom::Current(0))?;
116
117 match &sub_id {
118 b"FS " => {
119 if sub_size >= 4 {
121 let sr = self.file.read_u32::<BigEndian>()?;
122 sample_rate_hz = Some(sr);
123 } else {
124 self.file.seek(SeekFrom::Start(sub_payload_start + sub_size))?;
126 }
127 }
128 b"CHNL" => {
129 if sub_size >= 2 {
131 let ch = self.file.read_u16::<BigEndian>()?;
132 channels = Some(ch);
133 } else {
134 self.file.seek(SeekFrom::Start(sub_payload_start + sub_size))?;
135 }
136 }
137 b"CMPR" => {
138 if sub_size >= 4 {
140 let mut cmp = [0u8; 4];
141 self.file.read_exact(&mut cmp)?;
142 if &cmp != b"DSD " {
143 return Err(io::Error::new(io::ErrorKind::InvalidData, "unsupported CMPR (not DSD)"));
144 }
145 } else {
146 return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid CMPR chunk"));
147 }
148 }
149 _ => {
150 }
152 }
153
154 let padded = (sub_size + 1) & !1u64;
157 self.file.seek(SeekFrom::Start(sub_payload_start + padded))?;
158 }
159 } else {
160 let padded = (chunk_size + 1) & !1u64;
162 self.file.seek(SeekFrom::Start(chunk_payload_start + padded))?;
163 }
164 }
165
166 b"DSD " => {
167 found_dsd = true;
169 dsd_chunk_size = chunk_size;
170 self.data_start = self.file.seek(SeekFrom::Current(0))?;
172 break;
174 }
175
176 _ => {
177 let padded = (chunk_size + 1) & !1u64;
179 self.file.seek(SeekFrom::Start(chunk_payload_start + padded))?;
180 }
181 }
182 }
183
184 if !found_dsd {
185 return Err(io::Error::new(io::ErrorKind::InvalidData, "DSD chunk not found"));
186 }
187
188 let channels = channels.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "CHNL missing"))?;
190 let fs = sample_rate_hz.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "FS missing"))?;
191
192 format.num_channels = channels as u32;
194 self.ch = channels as usize;
195 format.sampling_rate = fs;
196
197 let total_frames = dsd_chunk_size / (self.ch as u64);
199 format.total_samples = total_frames; self.total_frames = total_frames;
201
202 self.buf.resize(self.block_frames * self.ch, 0);
204
205 Ok(())
206 }
207
208 fn read(&mut self, data: &mut [&mut [u8]], bytes_per_channel: usize) -> io::Result<usize> {
209 if self.ch == 0 {
211 return Ok(0);
212 }
213 if data.len() < self.ch {
214 return Err(io::Error::new(io::ErrorKind::InvalidInput, "not enough channel buffers"));
215 }
216
217 let mut written = 0usize;
219
220 while written < bytes_per_channel {
221 if self.pos_frames == self.filled_frames {
223 let frames_to_read = (bytes_per_channel - written).min(self.block_frames);
225 let bytes_to_read = frames_to_read * self.ch;
226 self.buf.resize(bytes_to_read, 0);
227 let n = self.file.read(&mut self.buf)?;
228 if n == 0 {
229 return Ok(written);
231 }
232 self.filled_frames = n / self.ch;
234 self.pos_frames = 0;
235 }
236
237 let available_frames = self.filled_frames - self.pos_frames;
238 let need_frames = bytes_per_channel - written;
239 let take_frames = available_frames.min(need_frames);
240
241 for ch_idx in 0..self.ch {
244 let dst = &mut data[ch_idx][written..written + take_frames];
245 let mut dst_i = 0usize;
247 let mut src_offset = self.pos_frames * self.ch + ch_idx;
248 for _ in 0..take_frames {
249 dst[dst_i] = self.buf[src_offset];
250 dst_i += 1;
251 src_offset += self.ch;
252 }
253 }
254
255 self.pos_frames += take_frames;
256 written += take_frames;
257 self.read_frames = self.read_frames.saturating_add(take_frames as u64);
258 }
259
260 Ok(written)
261 }
262
263 fn seek_percent(&mut self, percent: f64) -> io::Result<()> {
264 if !(0.0..=1.0).contains(&percent) {
265 return Err(io::Error::new(io::ErrorKind::InvalidInput, "percent out of range"));
266 }
267 let target_frame = (self.total_frames as f64 * percent) as u64;
268 self.seek_samples(target_frame)
269 }
270
271 fn seek_samples(&mut self, sample_index: u64) -> io::Result<()> {
272 let byte_offset = sample_index.checked_mul(self.ch as u64)
274 .ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "seek overflow"))?;
275 let offset = self.data_start + byte_offset;
276 self.file.seek(SeekFrom::Start(offset))?;
277 self.read_frames = sample_index;
278 self.pos_frames = 0;
279 self.filled_frames = 0;
280 Ok(())
281 }
282
283 fn get_position_frames(&self) -> u64 {
284 self.read_frames
285 }
286
287 fn get_position_percent(&self) -> f64 {
288 if self.total_frames == 0 {
289 0.0
290 } else {
291 (self.get_position_frames() as f64 / self.total_frames as f64).min(1.0)
292 }
293 }
294}