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