const AIFF_SAMPLE_RATE_TABLE : [(usize, [u8;10]); 19] =
[
(8000, [64, 11, 250, 0, 0, 0, 0, 0, 0, 0]),
(11025, [64, 12, 172, 68, 0, 0, 0, 0, 0, 0]),
(16000, [64, 12, 250, 0, 0, 0, 0, 0, 0, 0]),
(22050, [64, 13, 172, 68, 0, 0, 0, 0, 0, 0]),
(32000, [64, 13, 250, 0, 0, 0, 0, 0, 0, 0]),
(37800, [64, 14, 147, 168, 0, 0, 0, 0, 0, 0]),
(44056, [64, 14, 172, 24, 0, 0, 0, 0, 0, 0]),
(44100, [64, 14, 172, 68, 0, 0, 0, 0, 0, 0]),
(47250, [64, 14, 184, 146, 0, 0, 0, 0, 0, 0]),
(48000, [64, 14, 187, 128, 0, 0, 0, 0, 0, 0]),
(50000, [64, 14, 195, 80, 0, 0, 0, 0, 0, 0]),
(50400, [64, 14, 196, 224, 0, 0, 0, 0, 0, 0]),
(88200, [64, 15, 172, 68, 0, 0, 0, 0, 0, 0]),
(96000, [64, 15, 187, 128, 0, 0, 0, 0, 0, 0]),
(176400, [64, 16, 172, 68, 0, 0, 0, 0, 0, 0]),
(192000, [64, 16, 187, 128, 0, 0, 0, 0, 0, 0]),
(352800, [64, 17, 172, 68, 0, 0, 0, 0, 0, 0]),
(2822400, [64, 20, 172, 68, 0, 0, 0, 0, 0, 0]),
(5644800, [64, 21, 172, 68, 0, 0, 0, 0, 0, 0])
];
#[derive(Clone, Copy, PartialEq, Eq)]
enum WavAudioFormat
{
PCM,
IEEEFloat,
ALaw,
MULaw,
Extensible
}
impl WavAudioFormat
{
fn from_num(num : usize) -> Option<Self>
{
if num == 0x0001 { return Some(Self::PCM) }
else if num == 0x0003 { return Some(Self::IEEEFloat) }
else if num == 0x0006 { return Some(Self::ALaw) }
else if num == 0x0007 { return Some(Self::MULaw) }
else if num == 0xFFFE { return Some(Self::Extensible) }
None
}
fn to_num(self) -> usize
{
match self
{
WavAudioFormat::PCM => 0x0001,
WavAudioFormat::IEEEFloat => 0x0003,
WavAudioFormat::ALaw => 0x0006,
WavAudioFormat::MULaw => 0x0007,
WavAudioFormat::Extensible => 0xFFFE,
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum AIFFAudioFormat
{
Uncompressed,
Compressed,
Error
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum FileFormat
{
None,
NotLoaded,
Wav,
Aiff
}
impl FileFormat
{
fn determine(data : &[u8]) -> Self
{
if let Ok(header) = String::from_utf8(data[0..4].to_vec())
{
if header == "RIFF" { return Self::Wav }
else if header == "Form" { return Self::Aiff }
}
eprintln!("ERROR: Failed to determine audio format.");
Self::None
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
enum Endianness
{
Big,
Little
}
#[derive(Clone, Debug)]
pub struct BextChunk
{
pub description : String,
pub originator : String,
pub originator_reference : String,
pub origination_date : String,
pub origination_time : String,
pub time_reference : u64,
pub version : u16,
pub umid : [u8; 64],
pub loudness_value : i16,
pub loudness_range : i16,
pub max_true_peak_level : i16,
pub max_momentary_loudness : i16,
pub max_short_term_loudness : i16,
pub coding_history : String,
}
impl Default for BextChunk
{
fn default() -> Self
{
Self
{
description: String::new(),
originator: String::new(),
originator_reference: String::new(),
origination_date: String::new(),
origination_time: String::new(),
time_reference: 0,
version: 2,
umid: [0u8; 64],
loudness_value: 0,
loudness_range: 0,
max_true_peak_level: 0,
max_momentary_loudness: 0,
max_short_term_loudness: 0,
coding_history: String::new(),
}
}
}
impl BextChunk
{
pub fn new() -> Self { Self::default() }
pub fn with_description(description : &str, originator : &str) -> Self
{
Self
{
description: description.chars().take(256).collect(),
originator: originator.chars().take(32).collect(),
version: 2,
..Default::default()
}
}
pub fn set_datetime(&mut self, date : &str, time : &str)
{
self.origination_date = date.chars().take(10).collect();
self.origination_time = time.chars().take(8).collect();
}
pub fn set_current_datetime(&mut self)
{
self.origination_date = String::from("2025-01-01");
self.origination_time = String::from("00:00:00");
}
}
#[derive(Clone, Debug)]
pub struct Marker
{
pub id : u32,
pub position : u64,
pub label : String,
}
impl Marker
{
pub fn new(position : u64, label : &str) -> Self
{
Self
{
id: 0,
position,
label: label.to_string(),
}
}
pub fn with_id(id : u32, position : u64, label : &str) -> Self
{
Self
{
id,
position,
label: label.to_string(),
}
}
}
impl Default for Marker
{
fn default() -> Self
{
Self
{
id: 0,
position: 0,
label: String::new(),
}
}
}
#[derive(Clone, Debug)]
pub struct TempoInfo
{
pub bpm : f64,
pub time_sig_numerator : u8,
pub time_sig_denominator : u8,
pub position : u64,
}
impl Default for TempoInfo
{
fn default() -> Self
{
Self
{
bpm: 120.0,
time_sig_numerator: 4,
time_sig_denominator: 4,
position: 0,
}
}
}
impl TempoInfo
{
pub fn new(bpm : f64) -> Self
{
Self { bpm, ..Default::default() }
}
pub fn at_position(bpm : f64, position : u64) -> Self
{
Self { bpm, position, ..Default::default() }
}
pub fn with_time_signature(bpm : f64, numerator : u8, denominator : u8) -> Self
{
Self
{
bpm,
time_sig_numerator: numerator,
time_sig_denominator: denominator,
position: 0,
}
}
pub fn with_time_signature_at(bpm : f64, numerator : u8, denominator : u8, position : u64) -> Self
{
Self
{
bpm,
time_sig_numerator: numerator,
time_sig_denominator: denominator,
position,
}
}
}
use crate::buffer::Buffer;
pub struct AudioFile
{
audio_buffer : Vec<Vec<f64>>,
pub xml_chunk : String,
file_format : FileFormat,
sample_rate : usize,
bit_depth : usize,
bext_chunk : Option<BextChunk>,
markers : Vec<Marker>,
tempo : Option<TempoInfo>,
}
impl AudioFile
{
pub fn new(channels : usize, samples : usize) -> Self
{
Self
{
audio_buffer: vec![vec![0.0; samples]; channels],
xml_chunk: String::new(),
file_format: FileFormat::None,
sample_rate: 44100,
bit_depth: 16,
bext_chunk: None,
markers: Vec::new(),
tempo: None,
}
}
pub fn load(&mut self, path : &str)
{
if let Ok(mut file) = std::fs::File::open(path)
{
let mut buffer = vec![];
if let Err(error) = std::io::Read::read_to_end(&mut file, &mut buffer) { eprintln!("{}", error); }
else { self.load_bytes(&buffer); }
}
else { eprintln!("ERROR: Failed to open file: {}", path); }
}
pub fn save(&self, path : &str, format : FileFormat)
{
match format
{
FileFormat::Wav => self.save_wav(path),
FileFormat::Aiff => self.save_aiff(path),
_ => {}
}
}
pub fn load_bytes(&mut self, data : &[u8])
{
self.file_format = FileFormat::determine(data);
match self.file_format
{
FileFormat::Wav => self.read_wav(data),
FileFormat::Aiff => self.read_aiff(data),
_ => {}
}
}
pub fn channel(&self, index : usize) -> Option<&[f64]>
{
self.audio_buffer.get(index).map(|v| v.as_slice())
}
pub fn channel_mut(&mut self, index : usize) -> Option<&mut [f64]>
{
self.audio_buffer.get_mut(index).map(|v| v.as_mut_slice())
}
pub fn get_sample(&self, channel : usize, sample : usize) -> Option<f64>
{
self.audio_buffer.get(channel).and_then(|c| c.get(sample).copied())
}
pub fn set_sample(&mut self, channel : usize, sample : usize, value : f64)
{
if let Some(ch) = self.audio_buffer.get_mut(channel)
{
if let Some(s) = ch.get_mut(sample) { *s = value; }
}
}
pub fn to_buffer(&self, channel : usize) -> Option<Buffer<f64>>
{
self.audio_buffer.get(channel).map(|c| Buffer::from_slice(c))
}
pub fn from_buffer(&mut self, channel : usize, buffer : &Buffer<f64>)
{
if let Some(ch) = self.audio_buffer.get_mut(channel)
{
let guard = buffer.read();
let len = ch.len().min(guard.len());
ch[..len].copy_from_slice(&guard[..len]);
}
}
pub fn to_buffers(&self) -> Vec<Buffer<f64>>
{
self.audio_buffer.iter().map(|c| Buffer::from_slice(c)).collect()
}
pub fn from_buffers(&mut self, buffers : &[Buffer<f64>])
{
for (channel, buffer) in self.audio_buffer.iter_mut().zip(buffers.iter())
{
let guard = buffer.read();
let len = channel.len().min(guard.len());
channel[..len].copy_from_slice(&guard[..len]);
}
}
pub fn num_channel(&self) -> usize { self.audio_buffer.len() }
pub fn num_sample(&self) -> usize { if self.audio_buffer.is_empty() { 0 } else { self.audio_buffer[0].len() } }
pub fn is_mono(&self) -> bool { self.audio_buffer.len() == 1 }
pub fn is_stereo(&self) -> bool { self.audio_buffer.len() == 2 }
pub fn bit_depth(&self) -> usize { self.bit_depth }
pub fn sample_rate(&self) -> usize { self.sample_rate }
pub fn length(&self) -> f64 { self.num_sample() as f64 / self.sample_rate as f64 }
pub fn format(&self) -> FileFormat { self.file_format }
pub fn set_buffer_size(&mut self, channel : usize, sample : usize)
{
self.audio_buffer.resize(channel, vec![0.0; sample]);
for channel in &mut self.audio_buffer { channel.resize(sample, 0.0); }
}
pub fn set_channels(&mut self, count : usize) { self.audio_buffer.resize(count, vec![0.0; self.num_sample()]); }
pub fn set_samples(&mut self, count : usize) { for buffer in &mut self.audio_buffer { buffer.resize(count, 0.0); } }
pub fn set_bit_depth(&mut self, bit_depth : usize) { self.bit_depth = bit_depth; }
pub fn set_sample_rate(&mut self, sample_rate : usize) { self.sample_rate = sample_rate }
pub fn bext(&self) -> Option<&BextChunk> { self.bext_chunk.as_ref() }
pub fn bext_mut(&mut self) -> Option<&mut BextChunk> { self.bext_chunk.as_mut() }
pub fn set_bext(&mut self, bext : BextChunk) { self.bext_chunk = Some(bext); }
pub fn clear_bext(&mut self) { self.bext_chunk = None; }
pub fn is_bwf(&self) -> bool { self.bext_chunk.is_some() }
pub fn markers(&self) -> &[Marker] { &self.markers }
pub fn markers_mut(&mut self) -> &mut Vec<Marker> { &mut self.markers }
pub fn add_marker(&mut self, mut marker : Marker)
{
if marker.id == 0
{
marker.id = self.markers.iter().map(|m| m.id).max().unwrap_or(0) + 1;
}
self.markers.push(marker);
self.markers.sort_by_key(|m| m.position);
}
pub fn remove_marker(&mut self, id : u32) -> bool
{
if let Some(pos) = self.markers.iter().position(|m| m.id == id)
{
self.markers.remove(pos);
true
}
else { false }
}
pub fn clear_markers(&mut self) { self.markers.clear(); }
pub fn tempo(&self) -> Option<&TempoInfo> { self.tempo.as_ref() }
pub fn set_tempo(&mut self, bpm : f64) { self.tempo = Some(TempoInfo::new(bpm)); }
pub fn set_tempo_at(&mut self, bpm : f64, position : u64)
{
self.tempo = Some(TempoInfo::at_position(bpm, position));
}
pub fn set_tempo_with_time_sig(&mut self, bpm : f64, numerator : u8, denominator : u8)
{
self.tempo = Some(TempoInfo::with_time_signature(bpm, numerator, denominator));
}
pub fn set_tempo_with_time_sig_at(&mut self, bpm : f64, numerator : u8, denominator : u8, position : u64)
{
self.tempo = Some(TempoInfo::with_time_signature_at(bpm, numerator, denominator, position));
}
pub fn clear_tempo(&mut self) { self.tempo = None; }
pub fn save_bwf(&mut self, path : &str)
{
if self.bext_chunk.is_none()
{
self.bext_chunk = Some(BextChunk::new());
}
self.save_wav_internal(path, true);
}
fn read_wav(&mut self, buffer : &[u8])
{
if let Ok(header_chunk_id) = String::from_utf8(buffer[0..4].to_vec())
{
if header_chunk_id != "RIFF"
{
eprintln!("ERROR: Wrong header chunk id.");
return
}
}
if let Ok(format) = String::from_utf8(buffer[8..12].to_vec())
{
if format != "WAVE"
{
eprintln!("ERROR: Wrong format.");
return
}
}
let index_of_data_chunk = get_index_of_chunk(buffer, "data", 12, Endianness::Little);
let index_of_format_chunk = get_index_of_chunk(buffer, "fmt ", 12, Endianness::Little);
let index_of_xmlchunk = get_index_of_chunk(buffer, "iXML", 12, Endianness::Little);
let _format_chunk_id = String::from_utf8(buffer[index_of_format_chunk..index_of_format_chunk + 4].to_vec());
let _format_chunk_size = get_u32(buffer, index_of_format_chunk + 4, Endianness::Little) as usize;
let audio_format = WavAudioFormat::from_num(get_u16(buffer, index_of_format_chunk + 8, Endianness::Little) as usize);
let num_channels = get_u16(buffer, index_of_format_chunk + 10, Endianness::Little) as usize;
self.sample_rate = get_u32(buffer, index_of_format_chunk + 12, Endianness::Little) as usize;
let num_bytes_per_second = get_u32(buffer, index_of_format_chunk + 16, Endianness::Little) as usize;
let num_bytes_per_block = get_u16(buffer, index_of_format_chunk + 20, Endianness::Little) as usize;
self.bit_depth = get_u16(buffer, index_of_format_chunk + 22, Endianness::Little) as usize;
if self.bit_depth > size_of::<f64>() * 8
{
eprintln!("ERROR: you are trying to read a {}-bit file using a {}-bit sample type", self.bit_depth, size_of::<f64>() * 8);
return
}
if audio_format.is_none()
{
eprintln!("ERROR: this .WAV file is encoded in a format that this library does not support at present");
return
}
if num_channels < 1 || num_channels > 128
{
eprintln!("ERROR: this WAV file seems to be an invalid number of channels (or corrupted?)");
return
}
if num_bytes_per_second != num_channels * self.sample_rate * self.bit_depth / 8 || num_bytes_per_block != num_channels * num_bytes_per_second
{
eprintln!("ERROR: the header data in this WAV file seems to be inconsistent");
return
}
if self.bit_depth != 8 && self.bit_depth != 16 && self.bit_depth != 24 && self.bit_depth != 32
{
eprintln!("ERROR: this file has a bit depth that is not 8, 16, 24 or 32 bits");
return
}
let num_bytes_per_sample = self.bit_depth / 8;
let _data_chunk_id = String::from_utf8(buffer[index_of_data_chunk..index_of_data_chunk+ 4].to_vec());
let data_chunk_size = get_u32(buffer, index_of_data_chunk + 4, Endianness::Little) as usize;
let num_samples = data_chunk_size / (num_channels * self.bit_depth / 8);
let samples_start_index = index_of_data_chunk + 8;
self.audio_buffer.clear();
self.audio_buffer.resize(num_channels, vec![]);
for index in 0..num_samples
{
for channel in 0..num_channels
{
let sample_index = samples_start_index + num_bytes_per_block * index + channel * num_bytes_per_sample;
if sample_index + (self.bit_depth / 8) - 1 >= buffer.len()
{
eprintln!("ERROR: read file error as the metadata indicates more samples than there are in the file data");
return
}
if self.bit_depth == 8 { self.audio_buffer[channel].push(buffer[sample_index].cast_signed() as f64 / i8::MAX as f64); }
else if self.bit_depth == 16
{
let sample = get_u16(buffer, sample_index, Endianness::Little).cast_signed();
let sample = sample as f64 / i16::MAX as f64;
self.audio_buffer[channel].push(sample);
}
else if self.bit_depth == 24
{
let mut sample = (((buffer[sample_index + 2] as u32) << 16) | ((buffer[sample_index + 1] as u32) << 8) | buffer[sample_index] as u32).cast_signed();
if sample & 0x800000 == 0 { sample = sample | !0xFFFFFF };
self.audio_buffer[channel].push(sample as f64 / 8388607.0);
}
else if self.bit_depth == 32
{
let sample = get_u32(buffer, sample_index, Endianness::Little);
if audio_format.unwrap() == WavAudioFormat::IEEEFloat { self.audio_buffer[channel].push(f32::from_bits(sample) as f64); }
else { self.audio_buffer[channel].push(sample.cast_signed() as f64 / i32::MAX as f64); }
}
else
{
eprintln!("ERROR: Wrong bit depth detected.");
return;
}
}
}
if index_of_xmlchunk > 0
{
let chunk_size = get_u32(buffer, index_of_xmlchunk + 4, Endianness::Little) as usize;
if let Ok(chunk) = String::from_utf8(buffer[index_of_xmlchunk + 8..index_of_xmlchunk + 8 + chunk_size].to_vec())
{
self.xml_chunk = chunk;
}
}
let index_of_bext = get_index_of_chunk(buffer, "bext", 12, Endianness::Little);
if index_of_bext > 0
{
self.bext_chunk = Some(read_bext_chunk(buffer, index_of_bext));
}
let index_of_cue = get_index_of_chunk(buffer, "cue ", 12, Endianness::Little);
if index_of_cue > 0
{
self.markers = read_cue_chunk(buffer, index_of_cue);
let index_of_list = get_index_of_chunk(buffer, "LIST", 12, Endianness::Little);
if index_of_list > 0
{
read_marker_labels(buffer, index_of_list, &mut self.markers);
}
}
let index_of_acid = get_index_of_chunk(buffer, "acid", 12, Endianness::Little);
if index_of_acid > 0
{
self.tempo = read_acid_chunk(buffer, index_of_acid);
}
}
fn read_aiff(&mut self, buffer : &[u8])
{
if let Ok(header_chunk_id) = String::from_utf8(buffer[0..4].to_vec())
{
if header_chunk_id != "FORM"
{
eprintln!("ERROR: Wrong header chunk id.");
return
}
}
let audio_format = if let Ok(format) = String::from_utf8(buffer[8..12].to_vec())
{
if format == "AIFF" { AIFFAudioFormat::Uncompressed } else if format == "AIFC" { AIFFAudioFormat::Compressed } else { AIFFAudioFormat::Error }
}
else
{
eprintln!("ERROR: Wrong format.");
return
};
let index_of_comm_chunk = get_index_of_chunk(buffer, "COMM", 12, Endianness::Big);
let index_of_sound_data_chunk = get_index_of_chunk(buffer, "SSND", 12, Endianness::Big);
let index_of_xmlchunk = get_index_of_chunk(buffer, "iXML", 12, Endianness::Big);
if index_of_sound_data_chunk == 0 || index_of_comm_chunk == 0 || audio_format == AIFFAudioFormat::Error
{
eprintln!("ERROR: this doesn't seem to be a valid AIFF file");
return
}
let _comm_chunk_id = String::from_utf8(buffer[index_of_comm_chunk..index_of_comm_chunk + 4].to_vec());
let _comm_chunk_size = get_u32(buffer, index_of_comm_chunk + 4, Endianness::Big) as usize;
let num_channels = get_u16(buffer, index_of_comm_chunk + 8, Endianness::Big) as usize;
let num_samples_per_channel = get_u32(buffer, index_of_comm_chunk + 10, Endianness::Big) as usize;
self.bit_depth = get_u16(buffer, index_of_comm_chunk + 14, Endianness::Big) as usize;
self.sample_rate = get_aiff_sample_rate(buffer, index_of_comm_chunk + 16);
if self.bit_depth > size_of::<f64>() * 8
{
eprintln!("ERROR: you are trying to read a {}-bit file using a {}-bit sample type", self.bit_depth, size_of::<f64>() * 8);
return
}
if self.sample_rate == 0
{
eprintln!("ERROR: this AIFF file has an unsupported sample rate");
return
}
if num_channels < 1 ||num_channels > 2
{
eprintln!("ERROR: this AIFF file seems to be neither mono nor stereo (perhaps multi-track, or corrupted?)");
return
}
if self.bit_depth != 8 && self.bit_depth != 16 && self.bit_depth != 24 && self.bit_depth != 32
{
eprintln!("ERROR: this file has a bit depth that is not 8, 16, 24 or 32 bits");
return
}
let _sound_data_chunk_id = String::from_utf8(buffer[index_of_sound_data_chunk..index_of_sound_data_chunk + 4].to_vec());
let sound_data_chunk_size = get_u32(buffer, index_of_sound_data_chunk + 4, Endianness::Big) as usize;
let offset = get_u32(buffer, index_of_sound_data_chunk + 8, Endianness::Big) as usize;
let _block_size = get_u32(buffer, index_of_sound_data_chunk + 12, Endianness::Big) as usize;
let num_bytes_per_sample = self.bit_depth / 8;
let num_bytes_per_frame = num_bytes_per_sample * num_channels;
let total_num_audio_sample_bytes = num_samples_per_channel * num_bytes_per_frame;
let samples_start_index = index_of_sound_data_chunk + 16 + offset;
if sound_data_chunk_size - 8 != total_num_audio_sample_bytes || total_num_audio_sample_bytes > buffer.len() - samples_start_index
{
eprintln!("ERROR: the metadatafor this file doesn't seem right");
return
}
self.audio_buffer.clear();
self.audio_buffer.resize(num_channels, vec![]);
for index in 0..num_samples_per_channel
{
for channel in 0..num_channels
{
let sample_index = samples_start_index + (num_bytes_per_frame * index) + channel * num_bytes_per_sample;
if sample_index + self.bit_depth / 8 - 1 >= buffer.len()
{
eprintln!("ERROR: read file error as the metadata indicates more samples than there are in the file data");
return
}
if self.bit_depth == 8 { self.audio_buffer[channel].push(buffer[sample_index].cast_signed() as f64 / i8::MAX as f64); }
else if self.bit_depth == 16 { self.audio_buffer[channel].push(get_u16(buffer, sample_index, Endianness::Big) as f64 / u16::MAX as f64); }
else if self.bit_depth == 24
{
let mut sample = ((buffer[sample_index] as i32) << 16) | ((buffer[sample_index + 1] as i32) << 8) | buffer[sample_index + 2] as i32;
if sample & 0x800000 == 0 { sample = sample | !0xFFFFFF; }
self.audio_buffer[channel].push(sample as f64 / 8388607.0);
}
else if self.bit_depth == 32
{
let sample = get_u32(buffer, sample_index, Endianness::Big);
if audio_format == AIFFAudioFormat::Compressed { self.audio_buffer[channel].push(f32::from_bits(sample) as f64); }
else { self.audio_buffer[channel].push(sample.cast_signed() as f64 / i32::MAX as f64) }
}
else
{
eprintln!("ERROR: Wrong bit depth detected.");
return;
}
}
}
let chunk_size = get_u32(buffer, index_of_xmlchunk + 4, Endianness::Little) as usize;
if let Ok(xml) = String::from_utf8(buffer[index_of_xmlchunk + 8..index_of_xmlchunk + 8 + chunk_size].to_vec()) { self.xml_chunk = xml; }
}
fn save_wav(&self, path : &str)
{
self.save_wav_internal(path, false);
}
fn save_wav_internal(&self, path : &str, include_bwf : bool)
{
let mut buffer = vec![];
let data_chunk_size = self.num_sample() * self.num_channel() * self.bit_depth / 8;
let audio_format = WavAudioFormat::PCM;
let format_chunk_size = 16;
let i_xmlchunk_size = self.xml_chunk.len();
let bext_chunk_size = if include_bwf && self.bext_chunk.is_some()
{
let bext = self.bext_chunk.as_ref().unwrap();
602 + bext.coding_history.len() }
else { 0 };
let (cue_chunk_size, list_chunk_size) = if !self.markers.is_empty()
{
let cue_size = 4 + self.markers.len() * 24; let mut list_size = 4; for marker in &self.markers
{
if !marker.label.is_empty()
{
let label_len = marker.label.len() + 1; let padded_len = if label_len % 2 == 1 { label_len + 1 } else { label_len };
list_size += 8 + 4 + padded_len; }
}
(cue_size, if list_size > 4 { list_size } else { 0 })
}
else { (0, 0) };
let acid_chunk_size = if self.tempo.is_some() { 24 } else { 0 };
set_string(&mut buffer, "RIFF");
let mut file_size_in_bytes = 4 + format_chunk_size + 8 + 8 + data_chunk_size;
if i_xmlchunk_size > 0 { file_size_in_bytes += 8 + i_xmlchunk_size; }
if bext_chunk_size > 0 { file_size_in_bytes += 8 + bext_chunk_size; }
if cue_chunk_size > 0 { file_size_in_bytes += 8 + cue_chunk_size; }
if list_chunk_size > 0 { file_size_in_bytes += 8 + list_chunk_size; }
if acid_chunk_size > 0 { file_size_in_bytes += 8 + acid_chunk_size; }
set_u32(&mut buffer, file_size_in_bytes as u32, Endianness::Little);
set_string(&mut buffer, "WAVE");
if bext_chunk_size > 0
{
write_bext_chunk(&mut buffer, self.bext_chunk.as_ref().unwrap());
}
set_string(&mut buffer, "fmt ");
set_u32(&mut buffer, format_chunk_size as u32, Endianness::Little);
set_u16(&mut buffer, audio_format.to_num() as u16, Endianness::Little);
set_u16(&mut buffer, self.num_channel() as u16, Endianness::Little);
set_u32(&mut buffer, self.sample_rate as u32, Endianness::Little);
set_u32(&mut buffer, (self.num_channel() * self.sample_rate * self.bit_depth / 8) as u32, Endianness::Little);
set_u16(&mut buffer, (self.num_channel() * (self.bit_depth / 8)) as u16, Endianness::Little);
set_u16(&mut buffer, self.bit_depth as u16, Endianness::Little);
if cue_chunk_size > 0
{
write_cue_chunk(&mut buffer, &self.markers);
}
if list_chunk_size > 0
{
write_list_adtl_chunk(&mut buffer, &self.markers);
}
if acid_chunk_size > 0
{
write_acid_chunk(&mut buffer, self.tempo.as_ref().unwrap(), self.num_sample(), self.sample_rate);
}
set_string(&mut buffer, "data");
set_u32(&mut buffer, data_chunk_size as u32, Endianness::Little);
for index in 0..self.num_sample()
{
for channel in 0..self.num_channel()
{
let sample = self.audio_buffer[channel][index].clamp(-1.0, 1.0);
if self.bit_depth == 8 { buffer.push(((sample * i8::MAX as f64) as i8).cast_unsigned()); }
else if self.bit_depth == 16
{
set_u16(&mut buffer, ((sample * i16::MAX as f64) as i16).cast_unsigned(), Endianness::Little);
}
else if self.bit_depth == 24
{
let mut bytes = [0;3];
let sample = (sample * 8388607.0) as i32;
bytes[2] = (sample >> 16 & 0xFF) as u8;
bytes[1] = (sample >> 8 & 0xFF) as u8;
bytes[0] = (sample & 0xFF) as u8;
buffer.extend_from_slice(&bytes);
}
else if self.bit_depth == 32
{
set_u32(&mut buffer, ((sample * i32::MAX as f64) as i32).cast_unsigned(), Endianness::Little);
}
else
{
eprintln!("ERROR: Trying to write a file with unsupported bit depth");
return;
}
}
}
if i_xmlchunk_size > 0
{
set_string(&mut buffer, "iXML");
set_u32(&mut buffer, i_xmlchunk_size as u32, Endianness::Little);
set_string(&mut buffer, &self.xml_chunk);
}
if let Ok(mut file) = std::fs::File::create(path)
{
if let Err(error) = std::io::Write::write(&mut file, &buffer)
{
eprintln!("ERROR: couldn't save file to {} from error : {}", path, error);
}
} else { eprintln!("ERROR: couldn't create file to {}", path); }
}
fn save_aiff(&self, path : &str)
{
let mut buffer = vec![];
let num_bytes_per_sample = self.bit_depth / 8;
let num_bytes_per_frame = num_bytes_per_sample * self.num_channel();
let total_num_audio_sample_bytes = self.num_sample() * num_bytes_per_frame;
let sound_data_chunk_size = total_num_audio_sample_bytes + 8;
let i_xmlchunk_size = self.xml_chunk.len();
set_string(&mut buffer, "FORM");
let mut file_size_in_bytes = 4 + 26 + 16 + total_num_audio_sample_bytes;
if i_xmlchunk_size > 0
{
file_size_in_bytes += 8 + i_xmlchunk_size;
}
set_u32(&mut buffer, file_size_in_bytes as u32, Endianness::Big);
set_string(&mut buffer, "AIFF");
set_string(&mut buffer, "COMM");
set_u32(&mut buffer, 18, Endianness::Big);
set_u16(&mut buffer, self.num_channel() as u16, Endianness::Big);
set_u32(&mut buffer, self.num_sample() as u32, Endianness::Big);
set_u16(&mut buffer, self.bit_depth as u16, Endianness::Big);
set_aiff_sample_rate(&mut buffer, self.sample_rate);
set_string(&mut buffer, "SSND");
set_u32(&mut buffer, sound_data_chunk_size as u32, Endianness::Big);
set_u32(&mut buffer, 0, Endianness::Big);
set_u32(&mut buffer, 0, Endianness::Big);
for index in 0..self.num_sample()
{
for channel in 0..self.num_channel()
{
let sample = self.audio_buffer[channel][index].clamp(-1.0, 1.0);
if self.bit_depth == 8 { buffer.push(((sample * i8::MAX as f64) as i8).cast_unsigned()); }
else if self.bit_depth == 16
{
set_u16(&mut buffer, ((sample * i16::MAX as f64) as i16).cast_unsigned(), Endianness::Big);
}
else if self.bit_depth == 24
{
let mut bytes = [0;3];
let sample = (sample * 8388607.0) as i32;
bytes[0] = (sample >> 16 & 0xFF) as u8;
bytes[1] = (sample >> 8 & 0xFF) as u8;
bytes[2] = (sample & 0xFF) as u8;
buffer.extend(&bytes);
}
else if self.bit_depth == 32 { set_u32(&mut buffer, ((sample * i32::MAX as f64) as i32).cast_unsigned(), Endianness::Big); }
else
{
eprintln!("Trying to write a file with unsupported bit depth");
return
}
}
}
if i_xmlchunk_size > 0
{
set_string(&mut buffer, "iXML");
set_u32(&mut buffer, i_xmlchunk_size as u32, Endianness::Big);
set_string(&mut buffer, &self.xml_chunk);
}
if let Ok(mut file) = std::fs::File::create(path)
{
if let Err(error) = std::io::Write::write(&mut file, &buffer)
{
eprintln!("ERROR: couldn't save file to {} from error : {}", path, error);
}
} else { eprintln!("ERROR: couldn't create file to {}", path); }
}
}
impl Default for AudioFile
{
fn default() -> Self
{
Self
{
audio_buffer: vec![vec![]],
xml_chunk: String::new(),
file_format: FileFormat::NotLoaded,
sample_rate: 44100,
bit_depth: 16,
bext_chunk: None,
markers: Vec::new(),
tempo: None,
}
}
}
#[inline]
fn ten_byte_match(buffer1 : &[u8], start1 : usize, buffer2 : &[u8], start2 : usize) -> bool
{
for index in 0..10 { if buffer1[start1 + index] != buffer2[start2 + index] { return false } }
true
}
#[inline]
fn get_aiff_sample_rate(buffer : &[u8], start : usize) -> usize
{
for table in &AIFF_SAMPLE_RATE_TABLE { if ten_byte_match(buffer, start, &table.1, 0) { return table.0 } }
eprintln!("ERROR: Sample rate not detected.");
0
}
#[inline]
fn set_aiff_sample_rate(buffer : &mut Vec<u8>, sample_rate : usize)
{
for data in &AIFF_SAMPLE_RATE_TABLE
{
if data.0 == sample_rate
{
buffer.extend_from_slice(&data.1);
return
}
}
eprintln!("ERROR: Sample rate not matching.");
}
#[inline]
fn set_string(buffer : &mut Vec<u8>, string : &str) { buffer.extend_from_slice(string.as_bytes()); }
#[inline]
fn get_index_of_chunk(buffer : &[u8], chunk : &str, start : usize, endianness : Endianness) -> usize
{
let datalen = 4;
if chunk.len() != datalen
{
eprintln!("ERROR: Invalid chunk header ID string");
return 0;
}
let mut index = start;
while index < buffer.len() - datalen
{
if &buffer[index..index + datalen] == chunk.as_bytes() { return index }
index += datalen;
if (index + 4) >= buffer.len()
{
eprintln!("ERROR: Chunk header ID not found.");
return 0;
}
let chunk_size = get_u32(buffer, index, endianness) as usize;
index += datalen + chunk_size;
}
return 0;
}
#[inline]
fn get_u32(buffer : &[u8], start : usize, endianness : Endianness) -> u32
{
if buffer.len() >= (start + 4)
{
return match endianness
{
Endianness::Big =>
{
((buffer[start + 3] as u32) << 24) | ((buffer[start + 2] as u32) << 16) | ((buffer[start + 1] as u32) << 8) | buffer[start] as u32
},
Endianness::Little =>
{
((buffer[start] as u32) << 24) | ((buffer[start + 1] as u32) << 16) | ((buffer[start + 2] as u32) << 8) | buffer[start + 3] as u32
},
}
}
eprintln!("ERROR: Insufficient buffer length.");
0
}
#[inline]
fn set_u32(buffer : &mut Vec<u8>, data : u32, endianness : Endianness)
{
let mut bytes = [0;4];
match endianness
{
Endianness::Big =>
{
bytes[0] = ((data >> 24) & 0xFF) as u8;
bytes[1] = ((data >> 16) & 0xFF) as u8;
bytes[2] = ((data >> 8) & 0xFF) as u8;
bytes[3] = (data & 0xFF) as u8;
},
Endianness::Little =>
{
bytes[3] = ((data >> 24) & 0xFF) as u8;
bytes[2] = ((data >> 16) & 0xFF) as u8;
bytes[1] = ((data >> 8) & 0xFF) as u8;
bytes[0] = (data & 0xFF) as u8;
},
}
buffer.extend_from_slice(&bytes);
}
#[inline]
fn get_u16(buffer : &[u8], start : usize, endianness : Endianness) -> u16
{
if buffer.len() >= (start + 2)
{
return match endianness
{
Endianness::Big =>
{
((buffer[start + 1] as u16) << 8) | buffer[start] as u16
},
Endianness::Little =>
{
((buffer[start] as u16) << 8) | buffer[start + 1] as u16
},
}
}
eprintln!("ERROR: Insufficient buffer length.");
0
}
#[inline]
fn set_u16(buffer : &mut Vec<u8>, data : u16, endianness : Endianness)
{
let mut bytes = [0;2];
match endianness
{
Endianness::Big =>
{
bytes[0] = ((data >> 8) & 0xFF) as u8;
bytes[1] = (data & 0xFF) as u8;
},
Endianness::Little =>
{
bytes[1] = ((data >> 8) & 0xFF) as u8;
bytes[0] = (data & 0xFF) as u8;
},
}
buffer.extend_from_slice(&bytes);
}
#[inline]
fn get_u64(buffer : &[u8], start : usize, endianness : Endianness) -> u64
{
if buffer.len() >= (start + 8)
{
return match endianness
{
Endianness::Big =>
{
((buffer[start + 7] as u64) << 56) | ((buffer[start + 6] as u64) << 48) |
((buffer[start + 5] as u64) << 40) | ((buffer[start + 4] as u64) << 32) |
((buffer[start + 3] as u64) << 24) | ((buffer[start + 2] as u64) << 16) |
((buffer[start + 1] as u64) << 8) | buffer[start] as u64
},
Endianness::Little =>
{
((buffer[start] as u64) << 56) | ((buffer[start + 1] as u64) << 48) |
((buffer[start + 2] as u64) << 40) | ((buffer[start + 3] as u64) << 32) |
((buffer[start + 4] as u64) << 24) | ((buffer[start + 5] as u64) << 16) |
((buffer[start + 6] as u64) << 8) | buffer[start + 7] as u64
},
}
}
0
}
#[inline]
fn set_u64(buffer : &mut Vec<u8>, data : u64, endianness : Endianness)
{
let mut bytes = [0u8; 8];
match endianness
{
Endianness::Big =>
{
bytes[0] = ((data >> 56) & 0xFF) as u8;
bytes[1] = ((data >> 48) & 0xFF) as u8;
bytes[2] = ((data >> 40) & 0xFF) as u8;
bytes[3] = ((data >> 32) & 0xFF) as u8;
bytes[4] = ((data >> 24) & 0xFF) as u8;
bytes[5] = ((data >> 16) & 0xFF) as u8;
bytes[6] = ((data >> 8) & 0xFF) as u8;
bytes[7] = (data & 0xFF) as u8;
},
Endianness::Little =>
{
bytes[7] = ((data >> 56) & 0xFF) as u8;
bytes[6] = ((data >> 48) & 0xFF) as u8;
bytes[5] = ((data >> 40) & 0xFF) as u8;
bytes[4] = ((data >> 32) & 0xFF) as u8;
bytes[3] = ((data >> 24) & 0xFF) as u8;
bytes[2] = ((data >> 16) & 0xFF) as u8;
bytes[1] = ((data >> 8) & 0xFF) as u8;
bytes[0] = (data & 0xFF) as u8;
},
}
buffer.extend_from_slice(&bytes);
}
#[inline]
fn read_fixed_string(buffer : &[u8], start : usize, len : usize) -> String
{
if start + len > buffer.len() { return String::new(); }
String::from_utf8_lossy(&buffer[start..start + len])
.trim_end_matches('\0')
.to_string()
}
#[inline]
fn write_fixed_string(buffer : &mut Vec<u8>, string : &str, len : usize)
{
let bytes = string.as_bytes();
let write_len = bytes.len().min(len);
buffer.extend_from_slice(&bytes[..write_len]);
for _ in write_len..len { buffer.push(0); }
}
fn read_bext_chunk(buffer : &[u8], index : usize) -> BextChunk
{
let _chunk_size = get_u32(buffer, index + 4, Endianness::Little) as usize;
let data_start = index + 8;
let mut bext = BextChunk::new();
bext.description = read_fixed_string(buffer, data_start, 256);
bext.originator = read_fixed_string(buffer, data_start + 256, 32);
bext.originator_reference = read_fixed_string(buffer, data_start + 288, 32);
bext.origination_date = read_fixed_string(buffer, data_start + 320, 10);
bext.origination_time = read_fixed_string(buffer, data_start + 330, 8);
bext.time_reference = get_u64(buffer, data_start + 338, Endianness::Little);
bext.version = get_u16(buffer, data_start + 346, Endianness::Little);
if data_start + 412 <= buffer.len()
{
bext.umid.copy_from_slice(&buffer[data_start + 348..data_start + 412]);
}
if bext.version >= 2 && data_start + 422 <= buffer.len()
{
bext.loudness_value = get_u16(buffer, data_start + 412, Endianness::Little) as i16;
bext.loudness_range = get_u16(buffer, data_start + 414, Endianness::Little) as i16;
bext.max_true_peak_level = get_u16(buffer, data_start + 416, Endianness::Little) as i16;
bext.max_momentary_loudness = get_u16(buffer, data_start + 418, Endianness::Little) as i16;
bext.max_short_term_loudness = get_u16(buffer, data_start + 420, Endianness::Little) as i16;
}
let coding_history_start = data_start + 602;
if coding_history_start < buffer.len()
{
let chunk_end = index + 8 + _chunk_size;
if chunk_end <= buffer.len()
{
bext.coding_history = read_fixed_string(buffer, coding_history_start, chunk_end - coding_history_start);
}
}
bext
}
fn read_cue_chunk(buffer : &[u8], index : usize) -> Vec<Marker>
{
let mut markers = Vec::new();
let data_start = index + 8;
let num_cue_points = get_u32(buffer, data_start, Endianness::Little) as usize;
for i in 0..num_cue_points
{
let cue_start = data_start + 4 + i * 24;
if cue_start + 24 > buffer.len() { break; }
let id = get_u32(buffer, cue_start, Endianness::Little);
let position = get_u32(buffer, cue_start + 4, Endianness::Little) as u64;
let sample_offset = get_u32(buffer, cue_start + 20, Endianness::Little) as u64;
markers.push(Marker
{
id,
position: position + sample_offset,
label: String::new(),
});
}
markers
}
fn read_marker_labels(buffer : &[u8], index : usize, markers : &mut [Marker])
{
let chunk_size = get_u32(buffer, index + 4, Endianness::Little) as usize;
let data_start = index + 8;
if data_start + 4 > buffer.len() { return; }
let list_type = read_fixed_string(buffer, data_start, 4);
if list_type != "adtl" { return; }
let mut pos = data_start + 4;
let chunk_end = index + 8 + chunk_size;
while pos + 8 < chunk_end && pos + 8 < buffer.len()
{
let sub_chunk_id = read_fixed_string(buffer, pos, 4);
let sub_chunk_size = get_u32(buffer, pos + 4, Endianness::Little) as usize;
if sub_chunk_id == "labl" || sub_chunk_id == "note"
{
let cue_id = get_u32(buffer, pos + 8, Endianness::Little);
let label_len = sub_chunk_size.saturating_sub(4);
let label = read_fixed_string(buffer, pos + 12, label_len);
if let Some(marker) = markers.iter_mut().find(|m| m.id == cue_id)
{
marker.label = label;
}
}
pos += 8 + sub_chunk_size;
if sub_chunk_size % 2 == 1 { pos += 1; }
}
}
fn read_acid_chunk(buffer : &[u8], index : usize) -> Option<TempoInfo>
{
let data_start = index + 8;
if data_start + 24 > buffer.len() { return None; }
let tempo_bits = get_u32(buffer, data_start + 20, Endianness::Little);
let tempo = f32::from_bits(tempo_bits) as f64;
if tempo > 0.0 && tempo < 1000.0
{
let numerator = get_u16(buffer, data_start + 18, Endianness::Little) as u8;
let denominator = get_u16(buffer, data_start + 16, Endianness::Little) as u8;
Some(TempoInfo
{
bpm: tempo,
time_sig_numerator: if numerator > 0 { numerator } else { 4 },
time_sig_denominator: if denominator > 0 { denominator } else { 4 },
position: 0, })
}
else { None }
}
fn write_bext_chunk(buffer : &mut Vec<u8>, bext : &BextChunk)
{
let chunk_size = 602 + bext.coding_history.len();
set_string(buffer, "bext");
set_u32(buffer, chunk_size as u32, Endianness::Little);
write_fixed_string(buffer, &bext.description, 256);
write_fixed_string(buffer, &bext.originator, 32);
write_fixed_string(buffer, &bext.originator_reference, 32);
write_fixed_string(buffer, &bext.origination_date, 10);
write_fixed_string(buffer, &bext.origination_time, 8);
set_u64(buffer, bext.time_reference, Endianness::Little);
set_u16(buffer, bext.version, Endianness::Little);
buffer.extend_from_slice(&bext.umid);
set_u16(buffer, bext.loudness_value as u16, Endianness::Little);
set_u16(buffer, bext.loudness_range as u16, Endianness::Little);
set_u16(buffer, bext.max_true_peak_level as u16, Endianness::Little);
set_u16(buffer, bext.max_momentary_loudness as u16, Endianness::Little);
set_u16(buffer, bext.max_short_term_loudness as u16, Endianness::Little);
for _ in 0..180 { buffer.push(0); }
set_string(buffer, &bext.coding_history);
}
fn write_cue_chunk(buffer : &mut Vec<u8>, markers : &[Marker])
{
let chunk_size = 4 + markers.len() * 24;
set_string(buffer, "cue ");
set_u32(buffer, chunk_size as u32, Endianness::Little);
set_u32(buffer, markers.len() as u32, Endianness::Little);
for marker in markers
{
set_u32(buffer, marker.id, Endianness::Little); set_u32(buffer, marker.position as u32, Endianness::Little); set_string(buffer, "data"); set_u32(buffer, 0, Endianness::Little); set_u32(buffer, 0, Endianness::Little); set_u32(buffer, 0, Endianness::Little); }
}
fn write_list_adtl_chunk(buffer : &mut Vec<u8>, markers : &[Marker])
{
let mut list_size = 4; for marker in markers
{
if !marker.label.is_empty()
{
let label_len = marker.label.len() + 1; let padded_len = if label_len % 2 == 1 { label_len + 1 } else { label_len };
list_size += 8 + 4 + padded_len; }
}
if list_size <= 4 { return; }
set_string(buffer, "LIST");
set_u32(buffer, list_size as u32, Endianness::Little);
set_string(buffer, "adtl");
for marker in markers
{
if !marker.label.is_empty()
{
let label_len = marker.label.len() + 1;
let padded_len = if label_len % 2 == 1 { label_len + 1 } else { label_len };
set_string(buffer, "labl");
set_u32(buffer, (4 + padded_len) as u32, Endianness::Little);
set_u32(buffer, marker.id, Endianness::Little);
set_string(buffer, &marker.label);
buffer.push(0); if label_len % 2 == 1 { buffer.push(0); } }
}
}
fn write_acid_chunk(buffer : &mut Vec<u8>, tempo : &TempoInfo, num_samples : usize, sample_rate : usize)
{
set_string(buffer, "acid");
set_u32(buffer, 24, Endianness::Little);
set_u32(buffer, 0, Endianness::Little);
set_u16(buffer, 60, Endianness::Little);
set_u16(buffer, 0, Endianness::Little);
set_u32(buffer, 0, Endianness::Little);
let duration_seconds = num_samples as f64 / sample_rate as f64;
let num_beats = (duration_seconds * tempo.bpm / 60.0) as u32;
set_u32(buffer, num_beats, Endianness::Little);
set_u16(buffer, tempo.time_sig_denominator as u16, Endianness::Little);
set_u16(buffer, tempo.time_sig_numerator as u16, Endianness::Little);
set_u32(buffer, (tempo.bpm as f32).to_bits(), Endianness::Little);
}