1pub mod dsd_file;
101use crate::dsd_file::{
102 DFF_BLOCK_SIZE, DSD_64_RATE, DSF_BLOCK_SIZE, DsdFile, DsdFileFormat,
103 FormatExtensions,
104};
105use log::{debug, error, info, warn};
106use std::convert::{TryFrom, TryInto};
107use std::error::Error;
108use std::ffi::OsString;
109use std::fs::File;
110use std::io::{self, BufReader, ErrorKind, Read, Seek, SeekFrom};
111use std::path::{Path, PathBuf};
112
113const RETRIES: usize = 1; #[derive(Copy, Clone, PartialEq, Debug)]
117pub enum Endianness {
118 LsbFirst,
119 MsbFirst,
120}
121
122#[derive(Copy, Clone, PartialEq, Debug)]
124pub enum FmtType {
125 Planar,
127 Interleaved,
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
133pub enum DsdRate {
134 #[default]
135 DSD64 = 1,
136 DSD128 = 2,
137 DSD256 = 4,
138}
139
140impl TryFrom<u32> for DsdRate {
141 type Error = &'static str;
142 fn try_from(v: u32) -> Result<Self, Self::Error> {
143 match v {
144 1 => Ok(DsdRate::DSD64),
145 2 => Ok(DsdRate::DSD128),
146 4 => Ok(DsdRate::DSD256),
147 _ => Err("Invalid DSD rate multiplier (expected 1,2,4)"),
148 }
149 }
150}
151
152struct InputPathAttrs {
153 std_in: bool,
154 file_format: Option<DsdFileFormat>,
155 parent_path: Option<PathBuf>,
156 file_name: OsString,
157}
158
159pub struct DsdReader {
161 dsd_rate: DsdRate,
162 channels_num: usize,
163 std_in: bool,
164 tag: Option<id3::Tag>,
165 file_name: OsString,
166 audio_length: u64,
167 block_size: u32,
168 parent_path: Option<PathBuf>,
169 in_path: Option<PathBuf>,
170 lsbit_first: bool,
171 interleaved: bool,
172 audio_pos: u64,
173 file: Option<File>,
174 file_format: Option<DsdFileFormat>,
175}
176impl Default for DsdReader {
177 fn default() -> Self {
178 DsdReader {
179 dsd_rate: DsdRate::default(),
180 channels_num: 2,
181 std_in: true,
182 tag: None,
183 file_name: OsString::from("stdin"),
184 audio_length: 0,
185 block_size: DSF_BLOCK_SIZE,
186 parent_path: None,
187 in_path: None,
188 lsbit_first: false,
189 interleaved: true,
190 audio_pos: 0,
191 file: None,
192 file_format: None,
193 }
194 }
195}
196
197impl DsdReader {
198 pub fn dsd_rate(&self) -> i32 {
199 self.dsd_rate as i32
200 }
201 pub fn channels_num(&self) -> usize {
202 self.channels_num
203 }
204 pub fn std_in(&self) -> bool {
205 self.std_in
206 }
207 pub fn tag(&self) -> &Option<id3::Tag> {
208 &self.tag
209 }
210 pub fn file_name(&self) -> &OsString {
211 &self.file_name
212 }
213 pub fn audio_length(&self) -> u64 {
214 self.audio_length
215 }
216 pub fn block_size(&self) -> u32 {
217 self.block_size
218 }
219 pub fn parent_path(&self) -> &Option<PathBuf> {
220 &self.parent_path
221 }
222 pub fn in_path(&self) -> &Option<PathBuf> {
223 &self.in_path
224 }
225
226 pub fn new(
239 in_path: Option<PathBuf>,
240 format: FmtType,
241 endianness: Endianness,
242 dsd_rate: DsdRate,
243 block_size: u32,
244 channels: usize,
245 ) -> Result<Self, Box<dyn Error>> {
246 let lsbit_first = match endianness {
247 Endianness::LsbFirst => true,
248 Endianness::MsbFirst => false,
249 };
250
251 let interleaved = match format {
252 FmtType::Planar => false,
253 FmtType::Interleaved => true,
254 };
255
256 let path_attrs = Self::get_path_attrs(&in_path)?;
257
258 if path_attrs.std_in && ![1, 2, 4].contains(&(dsd_rate as u32)) {
260 return Err("Unsupported DSD input rate.".into());
261 }
262
263 let mut ctx = Self {
264 lsbit_first,
265 interleaved,
266 std_in: path_attrs.std_in,
267 dsd_rate,
268 in_path,
269 parent_path: path_attrs.parent_path,
270 channels_num: channels,
271 block_size: block_size,
272 audio_length: 0,
273 audio_pos: 0,
274 file: None,
275 tag: None,
276 file_name: path_attrs.file_name,
277 file_format: path_attrs.file_format,
278 };
279
280 debug!("Set block_size={}", ctx.block_size);
281
282 ctx.init()?;
283
284 Ok(ctx)
285 }
286
287 pub fn from_container(
289 in_path: PathBuf,
290 ) -> Result<Self, Box<dyn Error>> {
291 if in_path.is_dir() {
292 return Err("Input path cannot be a directory".into());
293 }
294 let file_format = DsdFileFormat::from(&in_path);
295 let file_name = in_path
296 .file_name()
297 .unwrap_or_else(|| "stdin".as_ref())
298 .to_os_string();
299 if !file_format.is_container() {
300 let name = file_name.to_string_lossy();
301 return Err(format!(
302 "{name} is not a recognized container format ({:?})",
303 file_format
304 )
305 .into());
306 }
307 let parent_path =
308 Some(in_path.parent().unwrap_or(Path::new("")).to_path_buf());
309
310 let mut ctx = Self::default();
311 ctx.in_path = Some(in_path);
312 ctx.std_in = false;
313 ctx.parent_path = parent_path;
314 ctx.file_name = file_name;
315 ctx.file_format = Some(file_format);
316
317 debug!("Set block_size={}", ctx.block_size);
318
319 ctx.update_from_file(file_format)?;
320
321 Ok(ctx)
322 }
323
324 pub fn dsd_iter(&self) -> Result<DsdIter, Box<dyn Error>> {
326 DsdIter::new(self)
327 }
328
329 fn get_path_attrs(
330 path: &Option<PathBuf>,
331 ) -> Result<InputPathAttrs, Box<dyn Error>> {
332 if let Some(p) = path {
333 if p.is_dir() {
334 return Err("Input path cannot be a directory".into());
335 }
336 let file_format = Some(DsdFileFormat::from(p));
337 let parent_path =
338 Some(p.parent().unwrap_or(Path::new("")).to_path_buf());
339 let file_name = p
340 .file_name()
341 .unwrap_or_else(|| "stdin".as_ref())
342 .to_os_string();
343
344 Ok(InputPathAttrs {
345 std_in: false,
346 file_format,
347 parent_path,
348 file_name,
349 })
350 } else {
351 Ok(InputPathAttrs {
352 std_in: true,
353 file_format: None,
354 parent_path: None,
355 file_name: OsString::from("stdin"),
356 })
357 }
358 }
359
360 fn init(&mut self) -> Result<(), Box<dyn Error>> {
361 if self.std_in {
362 self.debug_stdin();
363 } else if let Some(format) = self.file_format {
364 self.update_from_file(format)?;
365 } else {
366 return Err("No valid input specified".into());
367 }
368
369 Ok(())
370 }
371
372 fn debug_stdin(&mut self) {
373 debug!("Reading from stdin");
374 debug!(
375 "Using CLI parameters: {} channels, LSB first: {}, Interleaved: {}",
376 self.channels_num,
377 if self.lsbit_first == true {
378 "true"
379 } else {
380 "false"
381 },
382 self.interleaved
383 );
384 }
385
386 fn update_from_file(
387 &mut self,
388 dsd_file_format: DsdFileFormat,
389 ) -> Result<(), Box<dyn std::error::Error>> {
390 let Some(path) = &self.in_path else {
391 return Err("No readable input file".into());
392 };
393
394 info!("Opening input file: {}", self.file_name.to_string_lossy());
395 debug!(
396 "Parent path: {}",
397 self.parent_path.as_ref().unwrap().display()
398 );
399 match DsdFile::new(path, dsd_file_format) {
400 Ok(my_dsd) => {
401 let file_len = my_dsd.file().metadata()?.len();
403 debug!("File size: {} bytes", file_len);
404
405 self.file = Some(my_dsd.file().try_clone()?);
406 self.tag = my_dsd.tag().cloned();
407
408 self.audio_pos = my_dsd.audio_pos();
409 let max_len: u64 = (file_len - self.audio_pos).max(0);
411 self.audio_length = if my_dsd.audio_length() > 0
412 && my_dsd.audio_length() <= max_len
413 {
414 my_dsd.audio_length()
415 } else {
416 max_len
417 };
418
419 if let Some(chans_num) = my_dsd.channel_count() {
421 self.channels_num = chans_num;
422 }
423
424 if let Some(lsb) = my_dsd.is_lsb() {
426 self.lsbit_first = lsb;
427 }
428
429 match my_dsd.container_format() {
431 DsdFileFormat::Dsdiff => self.interleaved = true,
432 DsdFileFormat::Dsf => self.interleaved = false,
433 DsdFileFormat::Raw => {}
434 }
435
436 if let Some(block_size) = my_dsd.block_size()
444 && block_size > DFF_BLOCK_SIZE
445 {
446 self.block_size = block_size;
447 debug!("Set block_size={}", self.block_size,);
448 }
449
450 if let Some(sample_rate) = my_dsd.sample_rate() {
452 if sample_rate % DSD_64_RATE == 0 {
453 self.dsd_rate =
454 (sample_rate / DSD_64_RATE).try_into()?;
455 } else {
456 info!(
458 "Container sample_rate {} not standard; keeping CLI dsd_rate={}",
459 sample_rate, self.dsd_rate as i32
460 );
461 }
462 }
463
464 debug!("Audio length in bytes: {}", self.audio_length);
465 debug!(
466 "Container: sr={}Hz channels={} interleaved={}",
467 self.dsd_rate as u32 * DSD_64_RATE,
468 self.channels_num,
469 self.interleaved,
470 );
471 }
472 Err(e) if dsd_file_format != DsdFileFormat::Raw => {
473 info!("Container open failed with error: {}", e);
474 info!("Treating input as raw DSD (no container)");
475 self.update_from_file(DsdFileFormat::Raw)?;
476 }
477 Err(e) => {
478 return Err(e);
479 }
480 }
481 Ok(())
482 }
483}
484
485pub struct DsdIter {
487 std_in: bool,
488 bytes_remaining: u64,
489 channels_num: usize,
490 channel_buffers: Vec<Box<[u8]>>,
491 block_size: u32,
492 reader: Box<dyn Read + Send>,
493 frame_size: u32,
494 interleaved: bool,
495 lsbit_first: bool,
496 dsd_data: Vec<u8>,
497 file: Option<File>,
498 audio_pos: u64,
499 retries: usize,
500}
501impl DsdIter {
502 pub fn new(dsd_input: &DsdReader) -> Result<Self, Box<dyn Error>> {
503 let mut ctx = DsdIter {
504 std_in: dsd_input.std_in,
505 bytes_remaining: if dsd_input.std_in {
506 dsd_input.block_size as u64 * dsd_input.channels_num as u64
507 } else {
508 dsd_input.audio_length
509 },
510 channels_num: dsd_input.channels_num,
511 channel_buffers: Vec::new(),
512 block_size: 0,
513 reader: Box::new(io::empty()),
514 frame_size: 0,
515 interleaved: dsd_input.interleaved,
516 lsbit_first: dsd_input.lsbit_first,
517 dsd_data: vec![
518 0;
519 dsd_input.block_size as usize
520 * dsd_input.channels_num
521 ],
522 file: if let Some(file) = &dsd_input.file {
523 Some(file.try_clone()?)
524 } else {
525 None
526 },
527 audio_pos: dsd_input.audio_pos,
528 retries: 0,
529 };
530 ctx.set_block_size(dsd_input.block_size);
531 ctx.set_reader()?;
532 Ok(ctx)
533 }
534
535 #[inline(always)]
537 pub fn load_frame(&mut self) -> Result<usize, Box<dyn Error>> {
538 let partial_frame = if self.bytes_remaining
540 < self.frame_size as u64
541 && !self.std_in
542 {
543 true
544 } else {
545 false
546 };
547
548 if self.interleaved {
549 if partial_frame {
550 self.set_block_size(
551 (self.bytes_remaining / self.channels_num as u64)
552 as u32,
553 );
554 }
555 self.reader.read_exact(
556 &mut self.dsd_data[..self.frame_size as usize],
557 )?;
558 for chan in 0..self.channels_num {
560 let chan_bytes = self
561 .get_chan_bytes_interl(chan, self.frame_size as usize);
562 self.channel_buffers[chan].copy_from_slice(&chan_bytes);
563 }
564 Ok(self.frame_size as usize)
565 } else {
566 let mut total_valid = 0usize;
569 let remaining = if partial_frame {
570 self.reset_buffers();
571 self.bytes_remaining
572 } else {
573 self.frame_size as u64
574 };
575 let valid_for_chan =
576 (remaining / self.channels_num as u64) as usize;
577 let padding = self.block_size as usize - valid_for_chan;
578
579 for chan in 0..self.channels_num {
580 let chan_buf = &mut self.channel_buffers[chan];
581
582 self.reader.read_exact(&mut chan_buf[..valid_for_chan])?;
583 total_valid += valid_for_chan;
584
585 if padding > 0 {
586 let byte_reader = self.reader.as_mut();
588 for _ in 0..padding {
589 byte_reader.bytes().next();
590 }
591 }
592 }
593 Ok(total_valid)
594 }
595 }
596
597 #[inline(always)]
600 fn get_chan_bytes_interl(
601 &self,
602 chan: usize,
603 read_size: usize,
604 ) -> Vec<u8> {
605 let chan_size = read_size / self.channels_num;
606 let mut chan_bytes: Vec<u8> = Vec::with_capacity(chan_size);
607
608 for i in 0..chan_size {
609 let byte_index = chan + i * self.channels_num;
610 if byte_index >= self.dsd_data.len() {
611 break;
612 }
613 let b = self.dsd_data[byte_index];
614 chan_bytes.push(if self.lsbit_first {
615 b
616 } else {
617 b.reverse_bits()
618 });
619 }
620 chan_bytes
621 }
622
623 fn set_block_size(&mut self, block_size: u32) {
624 self.block_size = block_size;
625 self.frame_size = self.block_size * self.channels_num as u32;
626
627 self.channel_buffers = (0..self.channels_num)
628 .map(|_| {
629 vec![0x69u8; self.block_size as usize].into_boxed_slice()
630 })
631 .collect();
632
633 debug!("Set block_size={}", self.block_size,);
634 }
635
636 fn reset_buffers(&mut self) {
637 for chan in 0..self.channels_num {
638 let chan_buf = &mut self.channel_buffers[chan];
639 for byte in chan_buf.iter_mut() {
640 *byte = 0x69;
641 }
642 }
643 }
644
645 fn set_reader(&mut self) -> Result<(), Box<dyn Error>> {
646 if self.std_in {
647 self.reader = Box::new(BufReader::with_capacity(
649 self.frame_size as usize * 8,
650 io::stdin(),
651 ));
652 return Ok(());
653 }
654 let mut file = self
656 .file
657 .as_ref()
658 .ok_or_else(|| {
659 io::Error::new(
660 io::ErrorKind::Other,
661 "Missing input file handle",
662 )
663 })?
664 .try_clone()?;
665
666 if self.audio_pos > 0 {
667 file.seek(SeekFrom::Start(self.audio_pos as u64))?;
668 debug!(
669 "Seeked to audio start position: {}",
670 file.stream_position()?
671 );
672 }
673 self.reader = Box::new(BufReader::with_capacity(
674 self.frame_size as usize * 8,
675 file,
676 ));
677 Ok(())
678 }
679
680 #[inline(always)]
682 pub fn is_eof(&self) -> bool {
683 !self.std_in && self.bytes_remaining <= 0
684 }
685}
686
687impl Iterator for DsdIter {
688 type Item = (usize, Vec<Box<[u8]>>);
690
691 fn next(&mut self) -> Option<Self::Item> {
692 if self.is_eof() {
693 return None;
694 }
695 match self.load_frame() {
696 Ok(read_size) => {
697 self.retries = 0;
698 if !self.std_in {
699 self.bytes_remaining -= read_size as u64;
700 }
701 return Some((read_size, self.channel_buffers.clone()));
702 }
703 Err(e) => {
704 if let Some(io_err) = e.downcast_ref::<io::Error>() {
705 match io_err.kind() {
706 ErrorKind::Interrupted => {
707 if self.retries < RETRIES {
708 warn!("I/O interrupted, retrying read.");
709 self.retries += 1;
710 return self.next();
711 } else {
712 error!(
713 "Max retries reached for interrupted I/O."
714 );
715 return None;
716 }
717 }
718 _ => {
719 return None;
720 }
721 }
722 }
723 error!("Error reading DSD frame: {}", e);
724 return None;
725 }
726 }
727 }
728}