1#![warn(clippy::pedantic)]
51
52mod id3_display;
53
54use crate::id3_display::id3_tag_to_string;
55use id3::Tag;
56use sampled_data_duration::ConstantRateDuration;
57use std::convert::TryFrom;
58use std::fmt;
59use std::fs::File;
60use std::io;
61use std::io::prelude::*;
62use std::io::Read;
63use std::io::SeekFrom;
64use std::path::Path;
65use std::u64;
66
67pub const DSF_SAMPLE_DATA_OFFSET: u64 = 92;
69
70pub struct DsfFile {
85 file: File,
86 dsd_chunk: DsdChunk,
87 fmt_chunk: FmtChunk,
88 data_chunk: DataChunk,
89 id3_tag: Option<Tag>,
90 tag_read_err: Option<Error>,
91}
92impl DsfFile {
93 pub fn open(path: &Path) -> Result<DsfFile, Error> {
120 let mut file = File::open(path)?;
121
122 let mut dsd_chunk_buffer: [u8; 28] = [0; 28];
123 file.read_exact(&mut dsd_chunk_buffer)?;
124 let dsd_chunk = DsdChunk::try_from(dsd_chunk_buffer)?;
125
126 let mut fmt_chunk_buffer: [u8; 52] = [0; 52];
127 file.read_exact(&mut fmt_chunk_buffer)?;
128 let fmt_chunk = FmtChunk::try_from(fmt_chunk_buffer)?;
129
130 let mut data_chunk_buffer: [u8; 12] = [0; 12];
131 file.read_exact(&mut data_chunk_buffer)?;
132 let data_chunk = DataChunk::try_from(data_chunk_buffer)?;
133
134 let mut dsf_file = DsfFile {
135 file: file,
136 dsd_chunk: dsd_chunk,
137 fmt_chunk: fmt_chunk,
138 data_chunk: data_chunk,
139 id3_tag: None,
140 tag_read_err: None
141 };
142
143 dsf_file.id3_tag = if dsf_file.dsd_chunk.metadata_offset == 0 {
144 None
145 } else {
146 match dsf_file.file.seek(SeekFrom::Start(dsf_file.dsd_chunk.metadata_offset)) {
147 Ok(_n) => match Tag::read_from2(&dsf_file.file) {
148 Ok(tag) => Some(tag),
149 Err(e) => {
150 let partial = e.partial_tag.clone();
151 dsf_file.tag_read_err = Some(Error::from(e));
152 partial
153 }
154 },
155 Err(e) => {
156 dsf_file.tag_read_err = Some(Error::from(e));
157 None
158 }
159 }
160 };
161
162 Ok(dsf_file)
163 }
164
165 #[must_use]
167 pub fn file(&self) -> &File {
168 &self.file
169 }
170
171 #[must_use]
173 pub fn dsd_chunk(&self) -> &DsdChunk {
174 &self.dsd_chunk
175 }
176
177 #[must_use]
179 pub fn fmt_chunk(&self) -> &FmtChunk {
180 &self.fmt_chunk
181 }
182
183 #[must_use]
185 pub fn data_chunk(&self) -> &DataChunk {
186 &self.data_chunk
187 }
188
189 #[must_use]
191 pub fn id3_tag(&self) -> &Option<Tag> {
192 &self.id3_tag
193 }
194
195 pub fn tag_read_err(&self) -> Option<&Error> {
197 self.tag_read_err.as_ref()
198 }
199}
200impl fmt::Display for DsfFile {
201 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
202 let id3_tag_as_string = match &self.id3_tag {
203 Some(tag) => id3_tag_to_string(tag),
204 None => String::from("None"),
205 };
206 write!(
207 f,
208 "DSD chunk:\n{}\n\nFmt chunk:\n{}\n\nData chunk:\n{}\n\nID3Tag:\n{}",
209 self.dsd_chunk, self.fmt_chunk, self.data_chunk, &id3_tag_as_string,
210 )
211 }
212}
213
214fn u64_from_byte_buffer(buffer: &[u8], index: usize) -> u64 {
217 let mut byte_array: [u8; 8] = [0; 8];
218 byte_array.copy_from_slice(&buffer[index..index + 8]);
219
220 u64::from_le_bytes(byte_array)
221}
222
223fn u32_from_byte_buffer(buffer: &[u8], index: usize) -> u32 {
226 let mut byte_array: [u8; 4] = [0; 4];
227 byte_array.copy_from_slice(&buffer[index..index + 4]);
228
229 u32::from_le_bytes(byte_array)
230}
231
232const DSD_CHUNK_HEADER: [u8; 4] = [b'D', b'S', b'D', b' '];
236
237pub struct DsdChunk {
242 file_size: u64,
243 metadata_offset: u64,
244}
245impl DsdChunk {
246 fn new(file_size: u64, metadata_offset: u64) -> DsdChunk {
248 DsdChunk {
249 file_size,
250 metadata_offset,
251 }
252 }
253
254 #[must_use]
256 pub fn file_size(&self) -> u64 {
257 self.file_size
258 }
259}
260impl fmt::Display for DsdChunk {
261 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
262 write!(
263 f,
264 "File size = {} bytes\nMetadata offset = {} bytes",
265 self.file_size, self.metadata_offset
266 )
267 }
268}
269impl TryFrom<[u8; 28]> for DsdChunk {
270 type Error = Error;
271
272 fn try_from(buffer: [u8; 28]) -> Result<Self, Self::Error> {
273 if buffer[0..4] != DSD_CHUNK_HEADER {
274 return Err(Error::DsdChunkHeader);
275 }
276
277 let chunk_size = u64_from_byte_buffer(&buffer, 4);
278 if chunk_size != 28 {
279 return Err(Error::DsdChunkSize);
280 }
281
282 let file_size = u64_from_byte_buffer(&buffer, 12);
283 let metadata_offset = u64_from_byte_buffer(&buffer, 20);
284
285 Ok(DsdChunk::new(file_size, metadata_offset))
286 }
287}
288
289const FMT_CHUNK_HEADER: [u8; 4] = [b'f', b'm', b't', b' '];
291
292pub struct FmtChunk {
300 channel_type: ChannelType,
301 channel_num: u32,
302 sampling_frequency: u32,
303 bits_per_sample: u32,
304 sample_count: u64,
305 block_size_per_channel: u32,
306}
307impl FmtChunk {
308 fn new(
310 channel_type: ChannelType,
311 channel_num: u32,
312 sampling_frequency: u32,
313 bits_per_sample: u32,
314 sample_count: u64,
315 block_size_per_channel: u32,
316 ) -> FmtChunk {
317 FmtChunk {
318 channel_type,
319 channel_num,
320 sampling_frequency,
321 bits_per_sample,
322 sample_count,
323 block_size_per_channel,
324 }
325 }
326
327 #[must_use]
330 pub fn channel_type(&self) -> &ChannelType {
331 &self.channel_type
332 }
333
334 #[must_use]
337 pub fn channel_num(&self) -> u32 {
338 self.channel_num
339 }
340
341 #[must_use]
351 pub fn sampling_frequency(&self) -> u32 {
352 self.sampling_frequency
353 }
354
355 #[must_use]
363 pub fn bits_per_sample(&self) -> u32 {
364 self.bits_per_sample
365 }
366
367 #[must_use]
369 pub fn sample_count(&self) -> u64 {
370 self.sample_count
371 }
372
373 #[must_use]
376 pub fn block_size_per_channel(&self) -> u32 {
377 self.block_size_per_channel
378 }
379
380 fn duration(&self) -> ConstantRateDuration {
382 ConstantRateDuration::new(self.sample_count, u64::from(self.sampling_frequency))
383 }
384}
385impl fmt::Display for FmtChunk {
386 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
387 write!(
388 f,
389 "Channel type = {}
390Channel number = {}
391Sampling frequency = {} Hz
392Bits per sample = {}
393Sample count per channel = {}
394Block size per channel = {} bytes
395Calculated duration = {} h:min:s;samples",
396 self.channel_type,
397 self.channel_num,
398 self.sampling_frequency,
399 self.bits_per_sample,
400 self.sample_count,
401 self.block_size_per_channel,
402 self.duration()
403 )
404 }
405}
406impl TryFrom<[u8; 52]> for FmtChunk {
407 type Error = Error;
408
409 fn try_from(buffer: [u8; 52]) -> Result<Self, Self::Error> {
410 if buffer[0..4] != FMT_CHUNK_HEADER {
411 return Err(Error::FmtChunkHeader);
412 }
413
414 let chunk_size = u64_from_byte_buffer(&buffer, 4);
415 if chunk_size != 52 {
416 return Err(Error::FmtChunkSize);
417 }
418
419 let format_version = u32_from_byte_buffer(&buffer, 12);
420 if format_version != 1 {
421 return Err(Error::FormatVersion);
422 }
423
424 let format_id = u32_from_byte_buffer(&buffer, 16);
425 if format_id != 0 {
426 return Err(Error::FormatId);
427 }
428
429 let channel_type = ChannelType::try_from(u32_from_byte_buffer(&buffer, 20))?;
430
431 let channel_num = u32_from_byte_buffer(&buffer, 24);
432 match channel_num {
433 1 | 2 | 3 | 4 | 5 | 6 => (),
434 _ => return Err(Error::ChannelNum),
435 }
436
437 let sampling_frequency = u32_from_byte_buffer(&buffer, 28);
438 let bits_per_sample = u32_from_byte_buffer(&buffer, 32);
439 let sample_count = u64_from_byte_buffer(&buffer, 36);
440
441 let block_size_per_channel = u32_from_byte_buffer(&buffer, 44);
442 if block_size_per_channel != BLOCK_SIZE_AS_U32 {
443 return Err(Error::BlockSizePerChannelNonStandard);
444 }
445
446 let reserved = u32_from_byte_buffer(&buffer, 48);
447 if reserved != 0 {
448 return Err(Error::ReservedNotZero);
449 }
450
451 Ok(FmtChunk::new(
452 channel_type,
453 channel_num,
454 sampling_frequency,
455 bits_per_sample,
456 sample_count,
457 block_size_per_channel,
458 ))
459 }
460}
461
462#[derive(Debug, Eq, PartialEq)]
499pub enum ChannelType {
500 Mono,
501 Stereo,
502 ThreeChannels,
503 Quad,
504 FourChannels,
505 FiveChannels,
506 FivePointOneChannels,
507}
508impl fmt::Display for ChannelType {
509 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
510 let channel_type_as_str = match self {
511 ChannelType::Mono => "Mono",
512 ChannelType::Stereo => "Stereo: FL, FR.",
513 ChannelType::ThreeChannels => "3 channels: FL, FR, C.",
514 ChannelType::Quad => "Quad: FL, FR, BL, BR.",
515 ChannelType::FourChannels => "4 channels: FL, FR, C, LFE.",
516 ChannelType::FiveChannels => "5 channels: FL, FR, C, BL, BR.",
517 ChannelType::FivePointOneChannels => "5.1 channels: FL, FR, C, LFE, BL, BR.",
518 };
519
520 write!(f, "{}", channel_type_as_str)
521 }
522}
523impl TryFrom<u32> for ChannelType {
524 type Error = Error;
525
526 fn try_from(channel_type_as_u32: u32) -> Result<Self, Self::Error> {
527 match channel_type_as_u32 {
528 1 => Ok(ChannelType::Mono),
529 2 => Ok(ChannelType::Stereo),
530 3 => Ok(ChannelType::ThreeChannels),
531 4 => Ok(ChannelType::Quad),
532 5 => Ok(ChannelType::FourChannels),
533 6 => Ok(ChannelType::FiveChannels),
534 7 => Ok(ChannelType::FivePointOneChannels),
535 _ => Err(Error::ChannelType),
536 }
537 }
538}
539
540const DATA_CHUNK_HEADER: [u8; 4] = [b'd', b'a', b't', b'a'];
542
543pub struct DataChunk {
545 chunk_size: u64,
546}
547impl DataChunk {
548 fn new(chunk_size: u64) -> DataChunk {
550 DataChunk { chunk_size }
551 }
552
553 #[must_use]
557 pub fn chunk_size(&self) -> u64 {
558 self.chunk_size
559 }
560}
561impl fmt::Display for DataChunk {
562 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
563 write!(f, "Data chunk size = {} bytes", self.chunk_size)
564 }
565}
566impl TryFrom<[u8; 12]> for DataChunk {
567 type Error = Error;
568
569 fn try_from(buffer: [u8; 12]) -> Result<Self, Self::Error> {
570 if buffer[0..4] != DATA_CHUNK_HEADER {
571 return Err(Error::DataChunkHeader);
572 }
573
574 let chunk_size = u64_from_byte_buffer(&buffer, 4);
575
576 Ok(DataChunk::new(chunk_size))
577 }
578}
579
580const BLOCK_SIZE: usize = 4096;
582const BLOCK_SIZE_AS_U32: u32 = 4096;
593
594#[derive(Debug)]
596pub enum Error {
597 BlockSizePerChannelNonStandard,
598 ChannelNum,
599 ChannelType,
600 DataChunkHeader,
601 DsdChunkHeader,
602 DsdChunkSize,
603 FmtChunkHeader,
604 FmtChunkSize,
605 FormatId,
606 FormatVersion,
607 Id3Error(id3::Error),
608 IoError(io::Error),
609 ReservedNotZero,
610 ChannelIndexOutOfRange,
611 SampleIndexOutOfRange,
612 FrameIndexOutOfRange,
613}
614impl fmt::Display for Error {
615 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
616 match self {
617 Error::BlockSizePerChannelNonStandard => write!(
618 f,
619 "A fmt chunk is expected to specify its block size per channel as {}.",
620 BLOCK_SIZE
621 ),
622 Error::ChannelNum => {
623 f.write_str("A fmt chunk’s channel num is expected to be in the range 1–6.")
624 }
625 Error::ChannelType => {
626 f.write_str("A fmt chunk’s channel type is expected to be in the range 1–7.")
627 }
628 Error::DataChunkHeader => f.write_str("A data chunk must start with the bytes 'data'."),
629 Error::DsdChunkHeader => f.write_str("A DSD chunk must start with the bytes 'DSD '."),
630 Error::DsdChunkSize => f.write_str("A DSD chunk must specify its size as 28 bytes."),
631 Error::FmtChunkHeader => f.write_str("A fmt chunk must start with the bytes 'fmt '."),
632 Error::FmtChunkSize => {
633 f.write_str("A fmt chunk is expected to specify its size as 52 bytes.")
634 }
635 Error::FormatId => f.write_str("A fmt chumk must specifiy a format ID of 0."),
636 Error::FormatVersion => f.write_str("A fmt chunk must specify version 1."),
637 Error::Id3Error(id3_error) => write!(f, "Id3 error: {}", id3_error),
638 Error::IoError(io_error) => write!(f, "IO error: {}", io_error),
639 Error::ReservedNotZero => {
640 f.write_str("A fmt chunk’s reserved space is expected to be zero filled.")
641 }
642 Error::ChannelIndexOutOfRange => f.write_str("Channel index is out of range."),
643 Error::SampleIndexOutOfRange => f.write_str("Sample index is out of range."),
644 Error::FrameIndexOutOfRange => f.write_str("Frame index is out of range."),
645 }
646 }
647}
648impl std::error::Error for Error {
649 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
650 match self {
651 Error::Id3Error(id3_error) => Some(id3_error),
652 Error::IoError(io_error) => Some(io_error),
653 _ => None,
654 }
655 }
656}
657impl From<id3::Error> for Error {
658 fn from(error: id3::Error) -> Self {
659 Error::Id3Error(error)
660 }
661}
662impl From<io::Error> for Error {
663 fn from(error: io::Error) -> Self {
664 Error::IoError(error)
665 }
666}
667
668#[cfg(test)]
669mod tests {
670 use super::*;
671 use std::process::Command;
672
673 fn get_sweep_dsf_file() -> Result<DsfFile, Error> {
674 let sweep_filename = "sweep-176400hz-0-22050hz-20s-D64-2.8mhz.dsf";
675 let path = Path::new(sweep_filename);
676
677 if !path.is_file() {
678 let sweep_url =
679 "http://samplerateconverter.com/free-files/samples/dsf/sweep-176400hz-0-22050hz-20s-D64-2.8mhz.zip";
680
681 Command::new("wget")
682 .arg(sweep_url)
683 .status()
684 .unwrap_or_else(|_| panic!("Failed to download {}", sweep_url));
685
686 let sweep_zip_filename = "sweep-176400hz-0-22050hz-20s-D64-2.8mhz.zip";
687 Command::new("unzip")
688 .arg(sweep_zip_filename)
689 .status()
690 .unwrap_or_else(|_| panic!("Failed to unzip {}", sweep_zip_filename));
691 }
692
693 DsfFile::open(path)
694 }
695
696 #[test]
697 fn sweep_file() {
698 let dsf_file = get_sweep_dsf_file().unwrap();
699
700 assert_eq!(dsf_file.dsd_chunk.file_size, 14_114_908);
701 assert_eq!(dsf_file.dsd_chunk.metadata_offset, 0);
702
703 assert_eq!(dsf_file.fmt_chunk.channel_type, ChannelType::Stereo);
704 assert_eq!(dsf_file.fmt_chunk.channel_num, 2);
705 assert_eq!(dsf_file.fmt_chunk.sampling_frequency, 2_822_400);
706 assert_eq!(dsf_file.fmt_chunk.bits_per_sample, 1);
707 assert_eq!(dsf_file.fmt_chunk.sample_count, 56_459_264);
708 assert_eq!(dsf_file.fmt_chunk.block_size_per_channel, 4096);
709 assert_eq!(dsf_file.data_chunk.chunk_size, 14_114_828);
712
713 }
715}