ndsd_playback/dsd_readers/
dsf_reader.rs

1use byteorder::{LittleEndian, ReadBytesExt};
2use std::fs::File;
3use std::io;
4use std::io::{Read, Seek, SeekFrom};
5use crate::dsd_readers::{DSDFormat, DSDReader};
6
7pub struct DSFReader {
8    file: File,
9    buf: Vec<u8>,
10    ch: usize,
11    blocksize: usize,
12    filled: usize,
13    pos: usize,
14    total_samples: u64,
15    read_samples: u64,
16    data_start: u64, // <---- new: start offset of "data" chunk content
17}
18
19impl DSFReader {
20    pub(crate) 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            blocksize: 0,
27            filled: 0,
28            pos: 0,
29            total_samples: 0,
30            read_samples: 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            blocksize: 0,
41            filled: 0,
42            pos: 0,
43            total_samples: 0,
44            read_samples: 0,
45            data_start: 0,
46        }
47    }
48}
49
50impl DSDReader for DSFReader {
51    fn open(&mut self, format: &mut DSDFormat) -> io::Result<()> {
52        let mut ident = [0u8; 4];
53
54        // --- DSD chunk ---
55        self.file.read_exact(&mut ident)?;
56        if &ident != b"DSD " {
57            return Err(io::Error::new(io::ErrorKind::InvalidData, "not DSF"));
58        }
59        let dsd_size = self.file.read_u64::<LittleEndian>()?;
60        self.file.seek(SeekFrom::Current(dsd_size as i64 - 12))?;
61
62        // --- fmt chunk ---
63        self.file.read_exact(&mut ident)?;
64        if &ident != b"fmt " {
65            return Err(io::Error::new(
66                io::ErrorKind::InvalidData,
67                "fmt chunk missing",
68            ));
69        }
70        let fmt_size = self.file.read_u64::<LittleEndian>()?;
71        let format_version = self.file.read_u32::<LittleEndian>()?;
72        if format_version != 1 {
73            return Err(io::Error::new(
74                io::ErrorKind::InvalidData,
75                "unsupported format version",
76            ));
77        }
78        let format_id = self.file.read_u32::<LittleEndian>()?;
79        if format_id != 0 {
80            return Err(io::Error::new(
81                io::ErrorKind::InvalidData,
82                "unsupported format id",
83            ));
84        }
85        let _channel_type = self.file.read_u32::<LittleEndian>()?;
86        let channels = self.file.read_u32::<LittleEndian>()?;
87        format.num_channels = channels;
88        self.ch = channels as usize;
89
90        let sampling_freq = self.file.read_u32::<LittleEndian>()?;
91        format.sampling_rate = sampling_freq;
92        let bits_per_sample = self.file.read_u32::<LittleEndian>()?;
93        format.is_lsb_first = bits_per_sample == 1;
94
95        let sample_count = self.file.read_u64::<LittleEndian>()?;
96        format.total_samples = sample_count;
97        self.total_samples = sample_count;
98
99        let block_size = self.file.read_u32::<LittleEndian>()? as usize;
100        self.blocksize = block_size;
101
102        self.file.seek(SeekFrom::Current(fmt_size as i64 - 48))?;
103
104        // --- data chunk ---
105        self.file.read_exact(&mut ident)?;
106        if &ident != b"data" {
107            return Err(io::Error::new(
108                io::ErrorKind::InvalidData,
109                "data chunk missing",
110            ));
111        }
112        let _data_size = self.file.read_u64::<LittleEndian>()?;
113
114        // mark data start
115        self.data_start = self.file.seek(SeekFrom::Current(0))?;
116
117        // allocate buffer
118        self.buf.resize(self.blocksize * self.ch, 0);
119
120        Ok(())
121    }
122
123    fn read(&mut self, data: &mut [&mut [u8]], bytes_per_channel: usize) -> io::Result<usize> {
124        let mut read_bytes = 0usize;
125        let mut want = bytes_per_channel;
126
127        while want > 0 {
128            if self.pos == self.filled {
129                // read next interleaved block
130                let to_read = self.blocksize * self.ch;
131                self.buf.resize(to_read, 0);
132                let n = self.file.read(&mut self.buf)?;
133                if n == 0 {
134                    return Ok(read_bytes);
135                }
136                self.filled = n / self.ch;
137                self.pos = 0;
138            }
139
140            let available = self.filled - self.pos;
141            let size = available.min(want);
142            for i in 0..self.ch {
143                let src_offset = self.blocksize * i + self.pos;
144                let src = &self.buf[src_offset..src_offset + size];
145                let dst = &mut data[i][read_bytes..read_bytes + size];
146                dst.copy_from_slice(src);
147            }
148
149            self.pos += size;
150            want -= size;
151            read_bytes += size;
152        }
153
154        self.read_samples = self.read_samples.saturating_add((read_bytes as u64) * 8);
155        Ok(read_bytes)
156    }
157
158    fn seek_percent(&mut self, percent: f64) -> io::Result<()> {
159        if percent < 0.0 || percent > 1.0 {
160            return Err(io::Error::new(
161                io::ErrorKind::InvalidInput,
162                "percent out of range",
163            ));
164        }
165        let target_sample = (self.total_samples as f64 * percent) as u64;
166        self.seek_samples(target_sample)
167    }
168
169    fn seek_samples(&mut self, sample_index: u64) -> io::Result<()> {
170        // DSD = 1 bit per sample per channel
171        let total_bits = sample_index * self.ch as u64;
172        let total_bytes = total_bits / 8;
173
174        // align to nearest block boundary
175        let aligned_bytes =
176            (total_bytes / (self.blocksize * self.ch) as u64) * (self.blocksize * self.ch) as u64;
177
178        let offset = self.data_start + aligned_bytes;
179        self.file.seek(SeekFrom::Start(offset))?;
180
181        self.read_samples = aligned_bytes * 8;
182        self.pos = 0;
183        self.filled = 0;
184
185        Ok(())
186    }
187
188    fn get_position_frames(&self) -> u64 {
189        // self.read_samples counts *bits*, so divide by channels and by 1 bit/sample
190        // That gives total DSD frames read so far
191        if self.ch == 0 {
192            return 0;
193        }
194        self.read_samples / (self.ch as u64)
195    }
196
197    fn get_position_percent(&self) -> f64 {
198        if self.total_samples == 0 {
199            return 0.0;
200        }
201        let frames = self.get_position_frames();
202        (frames as f64 / self.total_samples as f64).min(1.0)
203    }
204}