mod id3_display;
use crate::id3_display::id3_tag_to_string;
use audio_duration::AudioDuration;
use id3::Tag;
use std::convert::TryFrom;
use std::fmt;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::io::Read;
use std::io::SeekFrom;
use std::path::Path;
use std::u64;
pub struct DsfFile {
file: File,
dsd_chunk: DsdChunk,
fmt_chunk: FmtChunk,
data_chunk: DataChunk,
id3_tag: Option<Tag>,
}
impl DsfFile {
pub fn open(path: &Path) -> Result<DsfFile, Error> {
let mut file = File::open(path)?;
let mut dsd_chunk_buffer: [u8; 28] = [0; 28];
file.read_exact(&mut dsd_chunk_buffer)?;
let dsd_chunk = DsdChunk::try_from(dsd_chunk_buffer)?;
let mut fmt_chunk_buffer: [u8; 52] = [0; 52];
file.read_exact(&mut fmt_chunk_buffer)?;
let fmt_chunk = FmtChunk::try_from(fmt_chunk_buffer)?;
let mut data_chunk_buffer: [u8; 12] = [0; 12];
file.read_exact(&mut data_chunk_buffer)?;
let data_chunk = DataChunk::try_from(data_chunk_buffer)?;
let id3_tag: Option<Tag> = match dsd_chunk.metadata_offset {
0 => None,
_ => {
file.seek(SeekFrom::Start(dsd_chunk.metadata_offset))?;
Some(Tag::read_from(&file)?)
}
};
Ok(DsfFile {
file,
dsd_chunk,
fmt_chunk,
data_chunk,
id3_tag,
})
}
pub fn file(&self) -> &File {
&self.file
}
pub fn dsd_chunk(&self) -> &DsdChunk {
&self.dsd_chunk
}
pub fn fmt_chunk(&self) -> &FmtChunk {
&self.fmt_chunk
}
pub fn data_chunk(&self) -> &DataChunk {
&self.data_chunk
}
pub fn id3_tag(&self) -> &Option<Tag> {
&self.id3_tag
}
pub fn frames(&mut self) -> Result<Frames, Error> {
Frames::new(self)
}
pub fn interleaved_u32_samples_iter(&mut self) -> Result<InterleavedU32SamplesIter, Error> {
InterleavedU32SamplesIter::new(self)
}
}
impl fmt::Display for DsfFile {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let id3_tag_as_string = match &self.id3_tag {
Some(tag) => id3_tag_to_string(tag),
None => String::from("None"),
};
write!(
f,
"DSD chunk:\n{}\n\nFmt chunk:\n{}\n\nData chunk:\n{}\n\nID3Tag:\n{}",
self.dsd_chunk, self.fmt_chunk, self.data_chunk, &id3_tag_as_string,
)
}
}
fn u64_from_byte_buffer(buffer: &[u8], index: usize) -> u64 {
let mut byte_array: [u8; 8] = [0; 8];
byte_array.copy_from_slice(&buffer[index..index + 8]);
u64::from_le_bytes(byte_array)
}
fn u32_from_byte_buffer(buffer: &[u8], index: usize) -> u32 {
let mut byte_array: [u8; 4] = [0; 4];
byte_array.copy_from_slice(&buffer[index..index + 4]);
u32::from_le_bytes(byte_array)
}
const DSD_CHUNK_HEADER: [u8; 4] = ['D' as u8, 'S' as u8, 'D' as u8, ' ' as u8];
pub struct DsdChunk {
file_size: u64,
metadata_offset: u64,
}
impl DsdChunk {
fn new(file_size: u64, metadata_offset: u64) -> DsdChunk {
DsdChunk {
file_size,
metadata_offset,
}
}
pub fn file_size(&self) -> u64 {
self.file_size
}
}
impl fmt::Display for DsdChunk {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"File size = {} bytes\nMetadata offset = {} bytes",
self.file_size, self.metadata_offset
)
}
}
impl TryFrom<[u8; 28]> for DsdChunk {
type Error = Error;
fn try_from(buffer: [u8; 28]) -> Result<Self, Self::Error> {
if &buffer[0..4] != DSD_CHUNK_HEADER {
return Err(Error::DsdChunkHeader);
}
let chunk_size = u64_from_byte_buffer(&buffer, 4);
if chunk_size != 28 {
return Err(Error::DsdChunkSize);
}
let file_size = u64_from_byte_buffer(&buffer, 12);
let metadata_offset = u64_from_byte_buffer(&buffer, 20);
Ok(DsdChunk::new(file_size, metadata_offset))
}
}
const FMT_CHUNK_HEADER: [u8; 4] = ['f' as u8, 'm' as u8, 't' as u8, ' ' as u8];
pub struct FmtChunk {
channel_type: ChannelType,
channel_num: u32,
sampling_frequency: u32,
bits_per_sample: u32,
sample_count: u64,
block_size_per_channel: u32,
}
impl FmtChunk {
fn new(
channel_type: ChannelType,
channel_num: u32,
sampling_frequency: u32,
bits_per_sample: u32,
sample_count: u64,
block_size_per_channel: u32,
) -> FmtChunk {
FmtChunk {
channel_type,
channel_num,
sampling_frequency,
bits_per_sample,
sample_count,
block_size_per_channel,
}
}
pub fn channel_type(&self) -> &ChannelType {
&self.channel_type
}
pub fn channel_num(&self) -> u32 {
self.channel_num
}
pub fn sampling_frequency(&self) -> u32 {
self.sampling_frequency
}
pub fn bits_per_sample(&self) -> u32 {
self.bits_per_sample
}
pub fn sample_count(&self) -> u64 {
self.sample_count
}
pub fn block_size_per_channel(&self) -> u32 {
self.block_size_per_channel
}
fn duration(&self) -> AudioDuration {
AudioDuration::new(self.sample_count, self.sampling_frequency)
}
}
impl fmt::Display for FmtChunk {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"Channel type = {}
Channel number = {}
Sampling frequency = {} Hz
Bits per sample = {}
Sample count per channel = {}
Block size per channel = {} bytes
Calculated duration = {} h:min:s;samples",
self.channel_type,
self.channel_num,
self.sampling_frequency,
self.bits_per_sample,
self.sample_count,
self.block_size_per_channel,
self.duration()
)
}
}
impl TryFrom<[u8; 52]> for FmtChunk {
type Error = Error;
fn try_from(buffer: [u8; 52]) -> Result<Self, Self::Error> {
if &buffer[0..4] != FMT_CHUNK_HEADER {
return Err(Error::FmtChunkHeader);
}
let chunk_size = u64_from_byte_buffer(&buffer, 4);
if chunk_size != 52 {
return Err(Error::FmtChunkSize);
}
let format_version = u32_from_byte_buffer(&buffer, 12);
if format_version != 1 {
return Err(Error::FormatVersion);
}
let format_id = u32_from_byte_buffer(&buffer, 16);
if format_id != 0 {
return Err(Error::FormatId);
}
let channel_type = ChannelType::try_from(u32_from_byte_buffer(&buffer, 20))?;
let channel_num = u32_from_byte_buffer(&buffer, 24);
match channel_num {
1 | 2 | 3 | 4 | 5 | 6 => (),
_ => return Err(Error::ChannelNum),
}
let sampling_frequency = u32_from_byte_buffer(&buffer, 28);
let bits_per_sample = u32_from_byte_buffer(&buffer, 32);
let sample_count = u64_from_byte_buffer(&buffer, 36);
let block_size_per_channel = u32_from_byte_buffer(&buffer, 44);
if block_size_per_channel != BLOCK_SIZE as u32 {
return Err(Error::BlockSizePerChannelNonStandard);
}
let reserved = u32_from_byte_buffer(&buffer, 48);
if reserved != 0 {
return Err(Error::ReservedNotZero);
}
Ok(FmtChunk::new(
channel_type,
channel_num,
sampling_frequency,
bits_per_sample,
sample_count,
block_size_per_channel,
))
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum ChannelType {
Mono,
Stereo,
ThreeChannels,
Quad,
FourChannels,
FiveChannels,
FivePointOneChannels,
}
impl fmt::Display for ChannelType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let channel_type_as_str;
match self {
ChannelType::Mono => channel_type_as_str = "Mono",
ChannelType::Stereo => channel_type_as_str = "Stereo: FL, FR.",
ChannelType::ThreeChannels => channel_type_as_str = "3 channels: FL, FR, C.",
ChannelType::Quad => channel_type_as_str = "Quad: FL, FR, BL, BR.",
ChannelType::FourChannels => channel_type_as_str = "4 channels: FL, FR, C, LFE.",
ChannelType::FiveChannels => channel_type_as_str = "5 channels: FL, FR, C, BL, BR.",
ChannelType::FivePointOneChannels => {
channel_type_as_str = "5.1 channels: FL, FR, C, LFE, BL, BR."
}
}
write!(f, "{}", channel_type_as_str)
}
}
impl TryFrom<u32> for ChannelType {
type Error = Error;
fn try_from(channel_type_as_u32: u32) -> Result<Self, Self::Error> {
match channel_type_as_u32 {
1 => Ok(ChannelType::Mono),
2 => Ok(ChannelType::Stereo),
3 => Ok(ChannelType::ThreeChannels),
4 => Ok(ChannelType::Quad),
5 => Ok(ChannelType::FourChannels),
6 => Ok(ChannelType::FiveChannels),
7 => Ok(ChannelType::FivePointOneChannels),
_ => Err(Error::ChannelType),
}
}
}
const DATA_CHUNK_HEADER: [u8; 4] = ['d' as u8, 'a' as u8, 't' as u8, 'a' as u8];
pub struct DataChunk {
chunk_size: u64,
}
impl DataChunk {
fn new(chunk_size: u64) -> DataChunk {
DataChunk { chunk_size }
}
pub fn chunk_size(&self) -> u64 {
self.chunk_size
}
}
impl fmt::Display for DataChunk {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Data chunk size = {} bytes", self.chunk_size)
}
}
impl TryFrom<[u8; 12]> for DataChunk {
type Error = Error;
fn try_from(buffer: [u8; 12]) -> Result<Self, Self::Error> {
if &buffer[0..4] != DATA_CHUNK_HEADER {
return Err(Error::DataChunkHeader);
}
let chunk_size = u64_from_byte_buffer(&buffer, 4);
Ok(DataChunk::new(chunk_size))
}
}
const BLOCK_SIZE: usize = 4096;
const SAMPLES_PER_BLOCK: u64 = 8 * BLOCK_SIZE as u64;
const SAMPLE_DATA_OFFSET: u64 = 92;
pub struct Frames<'a> {
channels: usize,
dsf_file: &'a mut DsfFile,
frame: Vec<[u8; BLOCK_SIZE]>,
frame_index: u64,
frame_count: u64,
reverse_bits: bool,
}
impl<'a> Frames<'a> {
fn new(dsf_file: &mut DsfFile) -> Result<Frames, Error> {
let channels = dsf_file.fmt_chunk.channel_num as usize;
let frame_count = dsf_file.fmt_chunk.sample_count / SAMPLES_PER_BLOCK;
let mut frame: Vec<[u8; BLOCK_SIZE]> = Vec::with_capacity(channels);
for _ in 0..channels {
frame.push([0; BLOCK_SIZE]);
}
let reverse_bits = match dsf_file.fmt_chunk.bits_per_sample {
1 => true,
8 => false,
_ => panic!("Illegal value for bits_per_sample"),
};
let mut frames = Frames {
channels,
dsf_file,
frame,
frame_index: 0,
frame_count,
reverse_bits,
};
frames.load_frame(0)?;
Ok(frames)
}
pub fn offset(&self, frame_index: u64) -> Result<u64, Error> {
if frame_index >= self.frame_count {
return Err(Error::FrameIndexOutOfRange);
}
Ok(self.offset_unchecked(frame_index))
}
fn offset_unchecked(&self, frame_index: u64) -> u64 {
debug_assert!(frame_index <= self.frame_count);
SAMPLE_DATA_OFFSET + frame_index * (BLOCK_SIZE * self.channels) as u64
}
pub fn frame_and_block_index(&self, sample_index: u64) -> Result<(u64, usize), Error> {
if sample_index >= self.dsf_file.fmt_chunk.sample_count {
return Err(Error::SampleIndexOutOfRange);
}
Ok(self.frame_and_block_index_unchecked(sample_index))
}
fn frame_and_block_index_unchecked(&self, sample_index: u64) -> (u64, usize) {
debug_assert!(sample_index < self.dsf_file.fmt_chunk.sample_count);
let frame_index = sample_index / SAMPLES_PER_BLOCK;
let block_index = ((sample_index % SAMPLES_PER_BLOCK) / 8) as usize;
(frame_index, block_index)
}
pub fn load_frame(&mut self, frame_index: u64) -> Result<(), Error> {
if frame_index >= self.frame_count {
return Err(Error::FrameIndexOutOfRange);
}
self.dsf_file
.file
.seek(SeekFrom::Start(self.offset_unchecked(frame_index)))?;
for i in 0..self.channels {
self.dsf_file.file.read_exact(&mut self.frame[i])?;
}
self.frame_index = frame_index;
Ok(())
}
fn load_frame_unchecked(&mut self, frame_index: u64) -> Result<(), Error> {
debug_assert!(frame_index < self.frame_count);
self.dsf_file
.file
.seek(SeekFrom::Start(self.offset_unchecked(frame_index)))?;
for i in 0..self.channels {
self.dsf_file.file.read_exact(&mut self.frame[i])?;
}
self.frame_index = frame_index;
Ok(())
}
pub fn samples_as_u32(
&mut self,
channel_index: usize,
sample_index: u64,
) -> Result<u32, Error> {
if channel_index >= self.channels {
return Err(Error::ChannelIndexOutOfRange);
}
let (frame_index, block_index) = self.frame_and_block_index(sample_index)?;
if self.frame_index != frame_index {
self.load_frame(frame_index)?;
}
let samples_as_u8_array: [u8; 4];
if self.reverse_bits {
samples_as_u8_array = [
self.frame[channel_index][block_index].reverse_bits(),
self.frame[channel_index][block_index + 1].reverse_bits(),
self.frame[channel_index][block_index + 2].reverse_bits(),
self.frame[channel_index][block_index + 3].reverse_bits(),
];
} else {
samples_as_u8_array = [
self.frame[channel_index][block_index],
self.frame[channel_index][block_index + 1],
self.frame[channel_index][block_index + 2],
self.frame[channel_index][block_index + 3],
];
}
Ok(u32::from_le_bytes(samples_as_u8_array))
}
fn samples_as_u32_unchecked(
&mut self,
channel_index: usize,
sample_index: u64,
) -> Result<u32, Error> {
debug_assert!(channel_index < self.channels);
let (frame_index, block_index) = self.frame_and_block_index_unchecked(sample_index);
if self.frame_index != frame_index {
self.load_frame_unchecked(frame_index)?;
}
let samples_as_u8_array: [u8; 4];
if self.reverse_bits {
samples_as_u8_array = [
self.frame[channel_index][block_index].reverse_bits(),
self.frame[channel_index][block_index + 1].reverse_bits(),
self.frame[channel_index][block_index + 2].reverse_bits(),
self.frame[channel_index][block_index + 3].reverse_bits(),
];
} else {
samples_as_u8_array = [
self.frame[channel_index][block_index],
self.frame[channel_index][block_index + 1],
self.frame[channel_index][block_index + 2],
self.frame[channel_index][block_index + 3],
];
}
Ok(u32::from_le_bytes(samples_as_u8_array))
}
}
pub struct InterleavedU32SamplesIter<'a> {
channel_index: usize,
channels: usize,
frames: Frames<'a>,
sample_index: u64,
sample_count: u64,
}
impl<'a> InterleavedU32SamplesIter<'a> {
fn new(dsf_file: &mut DsfFile) -> Result<InterleavedU32SamplesIter, Error> {
let channels = dsf_file.fmt_chunk.channel_num as usize;
let sample_count = dsf_file.fmt_chunk.sample_count;
let frames = Frames::new(dsf_file)?;
Ok(InterleavedU32SamplesIter {
channel_index: 0,
channels,
frames,
sample_index: 0,
sample_count,
})
}
pub fn sample_index(&self) -> u64 {
self.sample_index
}
pub fn sample_count(&self) -> u64 {
self.sample_count
}
pub fn set_sample_index(&mut self, sample_index: u64) -> Result<u64, Error> {
if sample_index >= self.sample_count {
return Err(Error::SampleIndexOutOfRange);
}
let normalized_sample_index = 32 * (sample_index / 32);
self.sample_index = normalized_sample_index;
Ok(normalized_sample_index)
}
}
impl<'a> Iterator for InterleavedU32SamplesIter<'a> {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
match self
.frames
.samples_as_u32_unchecked(self.channel_index, self.sample_index)
{
Ok(samples_as_u32) => {
self.channel_index += 1;
if self.channel_index >= self.channels {
self.channel_index = 0;
self.sample_index += 32;
}
Some(samples_as_u32)
}
Err(_) => None,
}
}
}
#[derive(Debug)]
pub enum Error {
BlockSizePerChannelNonStandard,
ChannelNum,
ChannelType,
DataChunkHeader,
DsdChunkHeader,
DsdChunkSize,
FmtChunkHeader,
FmtChunkSize,
FormatId,
FormatVersion,
Id3Error(id3::Error),
IoError(io::Error),
ReservedNotZero,
ChannelIndexOutOfRange,
SampleIndexOutOfRange,
FrameIndexOutOfRange,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::BlockSizePerChannelNonStandard => write!(
f,
"A fmt chunk is expected to specify its block size per channel as {}.",
BLOCK_SIZE
),
Error::ChannelNum => {
f.write_str("A fmt chunk’s channel num is expected to be in the range 1–6.")
}
Error::ChannelType => {
f.write_str("A fmt chunk’s channel type is expected to be in the range 1–7.")
}
Error::DataChunkHeader => f.write_str("A data chunk must start with the bytes 'data'."),
Error::DsdChunkHeader => f.write_str("A DSD chunk must start with the bytes 'DSD '."),
Error::DsdChunkSize => f.write_str("A DSD chunk must specify its size as 28 bytes."),
Error::FmtChunkHeader => f.write_str("A fmt chunk must start with the bytes 'fmt '."),
Error::FmtChunkSize => {
f.write_str("A fmt chunk is expected to specify its size as 52 bytes.")
}
Error::FormatId => f.write_str("A fmt chumk must specifiy a format ID of 0."),
Error::FormatVersion => f.write_str("A fmt chunk must specify version 1."),
Error::Id3Error(id3_error) => write!(f, "Id3 error: {}", id3_error),
Error::IoError(io_error) => write!(f, "IO error: {}", io_error),
Error::ReservedNotZero => {
f.write_str("A fmt chunk’s reserved space is expected to be zero filled.")
}
Error::ChannelIndexOutOfRange => f.write_str("Channel index is out of range."),
Error::SampleIndexOutOfRange => f.write_str("Sample index is out of range."),
Error::FrameIndexOutOfRange => f.write_str("Frame index is out of range."),
}
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::Id3Error(id3_error) => Some(id3_error),
Error::IoError(io_error) => Some(io_error),
_ => None,
}
}
}
impl From<id3::Error> for Error {
fn from(error: id3::Error) -> Self {
Error::Id3Error(error)
}
}
impl From<io::Error> for Error {
fn from(error: io::Error) -> Self {
Error::IoError(error)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::process::Command;
fn get_sweep_dsf_file() -> Result<DsfFile, Error> {
let sweep_filename = "sweep-176400hz-0-22050hz-20s-D64-2.8mhz.dsf";
let path = Path::new(sweep_filename);
if !path.is_file() {
let sweep_url =
"http://samplerateconverter.com/free-files/samples/dsf/sweep-176400hz-0-22050hz-20s-D64-2.8mhz.zip";
Command::new("wget")
.arg(sweep_url)
.status()
.expect(&format!("Failed to download {}", sweep_url));
let sweep_zip_filename = "sweep-176400hz-0-22050hz-20s-D64-2.8mhz.zip";
Command::new("unzip")
.arg(sweep_zip_filename)
.status()
.expect(&format!("Failed to unzip {}", sweep_zip_filename));
}
DsfFile::open(path)
}
#[test]
fn sweep_file() {
let dsf_file = get_sweep_dsf_file().unwrap();
assert_eq!(dsf_file.dsd_chunk.file_size, 14114908);
assert_eq!(dsf_file.dsd_chunk.metadata_offset, 0);
assert_eq!(dsf_file.fmt_chunk.channel_type, ChannelType::Stereo);
assert_eq!(dsf_file.fmt_chunk.channel_num, 2);
assert_eq!(dsf_file.fmt_chunk.sampling_frequency, 2822400);
assert_eq!(dsf_file.fmt_chunk.bits_per_sample, 1);
assert_eq!(dsf_file.fmt_chunk.sample_count, 56459264);
assert_eq!(dsf_file.fmt_chunk.block_size_per_channel, 4096);
assert_eq!(dsf_file.data_chunk.chunk_size, 14114828);
}
}