mirror of
https://github.com/iluvcapra/bwavfile.git
synced 2025-12-31 08:50:44 +00:00
Merge pull request #14 from irh/performance-improvements
Performance improvements
This commit is contained in:
9
Cargo.lock
generated
9
Cargo.lock
generated
@@ -1,5 +1,7 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ansi_term"
|
name = "ansi_term"
|
||||||
version = "0.11.0"
|
version = "0.11.0"
|
||||||
@@ -32,6 +34,7 @@ version = "1.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"clap",
|
"clap",
|
||||||
|
"dasp_sample",
|
||||||
"encoding",
|
"encoding",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"uuid",
|
"uuid",
|
||||||
@@ -58,6 +61,12 @@ dependencies = [
|
|||||||
"vec_map",
|
"vec_map",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dasp_sample"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "encoding"
|
name = "encoding"
|
||||||
version = "0.2.33"
|
version = "0.2.33"
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "bwavfile"
|
name = "bwavfile"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
authors = ["Jamie Hardt <jamiehardt@me.com>"]
|
authors = ["Jamie Hardt <jamiehardt@me.com>", "Ian Hobson <ian.r.hobson@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
description = "Rust Wave File Reader/Writer with Broadcast-WAV, MBWF and RF64 Support"
|
description = "Rust Wave File Reader/Writer with Broadcast-WAV, MBWF and RF64 Support"
|
||||||
@@ -15,6 +15,7 @@ keywords = ["audio", "broadcast", "multimedia","smpte"]
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.3.4"
|
byteorder = "1.3.4"
|
||||||
|
dasp_sample = "0.11.0"
|
||||||
encoding = "0.2.33"
|
encoding = "0.2.33"
|
||||||
uuid = "0.8.1"
|
uuid = "0.8.1"
|
||||||
clap = "2.33.3"
|
clap = "2.33.3"
|
||||||
|
|||||||
@@ -208,7 +208,7 @@ fn create_blits_file(file_name: &str, sample_rate: u32, bits_per_sample: u16) ->
|
|||||||
let mut fw = file.audio_frame_writer()?;
|
let mut fw = file.audio_frame_writer()?;
|
||||||
for frame in frames {
|
for frame in frames {
|
||||||
let buf = vec![frame.0, frame.1, frame.2, frame.3, frame.4, frame.5];
|
let buf = vec![frame.0, frame.1, frame.2, frame.3, frame.4, frame.5];
|
||||||
fw.write_integer_frames(&buf)?;
|
fw.write_frames(&buf)?;
|
||||||
}
|
}
|
||||||
fw.end()?;
|
fw.end()?;
|
||||||
|
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
//! wave-inter.rs
|
//! wave-deinter.rs
|
||||||
//! (c) 2021 Jamie Hardt. All rights reserved.
|
//! (c) 2021 Jamie Hardt. All rights reserved.
|
||||||
//!
|
//!
|
||||||
//! This program demonstrats combining several wave files into a single
|
//! This program demonstrates splitting a multichannel file into separate monophonic files for each
|
||||||
//! polyphonic wave file.
|
//! individual channel.
|
||||||
|
|
||||||
use std::io;
|
use std::io::{Read, Seek};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
extern crate bwavfile;
|
extern crate bwavfile;
|
||||||
use bwavfile::{ChannelDescriptor, ChannelMask, Error, WaveFmt, WaveReader, WaveWriter};
|
use bwavfile::{
|
||||||
|
ChannelDescriptor, ChannelMask, CommonFormat, Error, Sample, WaveFmt, WaveReader, WaveWriter,
|
||||||
|
I24,
|
||||||
|
};
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate clap;
|
extern crate clap;
|
||||||
@@ -48,17 +51,25 @@ fn name_suffix(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_file(infile: &str, delim: &str, numeric_channel_names: bool) -> Result<(), Error> {
|
fn deinterleave_file<S, R>(
|
||||||
let mut input_file = WaveReader::open(infile)?;
|
mut input_file: WaveReader<R>,
|
||||||
|
input_format: WaveFmt,
|
||||||
|
settings: Settings,
|
||||||
|
) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
S: Sample,
|
||||||
|
R: Read + Seek,
|
||||||
|
{
|
||||||
|
let frames_per_read = 4096;
|
||||||
let channel_desc = input_file.channels()?;
|
let channel_desc = input_file.channels()?;
|
||||||
let input_format = input_file.format()?;
|
let channel_count = channel_desc.len();
|
||||||
|
|
||||||
if channel_desc.len() == 1 {
|
if channel_desc.len() == 1 {
|
||||||
println!("Input file in monoaural, exiting.");
|
println!("Input file in monoaural, exiting.");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let infile_path = Path::new(infile);
|
let infile_path = Path::new(&settings.input_path);
|
||||||
let basename = infile_path
|
let basename = infile_path
|
||||||
.file_stem()
|
.file_stem()
|
||||||
.expect("Unable to extract file basename")
|
.expect("Unable to extract file basename")
|
||||||
@@ -68,12 +79,24 @@ fn process_file(infile: &str, delim: &str, numeric_channel_names: bool) -> Resul
|
|||||||
.parent()
|
.parent()
|
||||||
.expect("Unable to derive parent directory");
|
.expect("Unable to derive parent directory");
|
||||||
|
|
||||||
let ouptut_format =
|
let output_block_alignment = input_format.bits_per_sample / 8;
|
||||||
WaveFmt::new_pcm_mono(input_format.sample_rate, input_format.bits_per_sample);
|
let output_format = WaveFmt {
|
||||||
let mut input_wave_reader = input_file.audio_frame_reader()?;
|
channel_count: 1,
|
||||||
|
block_alignment: output_block_alignment,
|
||||||
for (n, channel) in channel_desc.iter().enumerate() {
|
bytes_per_second: output_block_alignment as u32 * input_format.sample_rate,
|
||||||
let suffix = name_suffix(numeric_channel_names, delim, n + 1, channel);
|
..input_format
|
||||||
|
};
|
||||||
|
let mut reader = input_file.audio_frame_reader()?;
|
||||||
|
let mut writers = channel_desc
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(n, channel)| {
|
||||||
|
let suffix = name_suffix(
|
||||||
|
settings.use_numeric_names,
|
||||||
|
&settings.delimiter,
|
||||||
|
n + 1,
|
||||||
|
channel,
|
||||||
|
);
|
||||||
let outfile_name = output_dir
|
let outfile_name = output_dir
|
||||||
.join(format!("{}{}.wav", basename, suffix))
|
.join(format!("{}{}.wav", basename, suffix))
|
||||||
.into_os_string()
|
.into_os_string()
|
||||||
@@ -82,24 +105,65 @@ fn process_file(infile: &str, delim: &str, numeric_channel_names: bool) -> Resul
|
|||||||
|
|
||||||
println!("Will create file {}", outfile_name);
|
println!("Will create file {}", outfile_name);
|
||||||
|
|
||||||
let output_file =
|
WaveWriter::create(&outfile_name, output_format)
|
||||||
WaveWriter::create(&outfile_name, ouptut_format).expect("Failed to create new file");
|
.expect("Failed to create new file")
|
||||||
|
.audio_frame_writer()
|
||||||
|
})
|
||||||
|
.collect::<Result<Vec<_>, _>>()?;
|
||||||
|
|
||||||
let mut output_wave_writer = output_file.audio_frame_writer()?;
|
let mut input_buffer = vec![S::EQUILIBRIUM; frames_per_read * channel_count];
|
||||||
let mut buffer = input_format.create_frame_buffer(1);
|
let mut output_buffer = vec![S::EQUILIBRIUM; frames_per_read];
|
||||||
|
|
||||||
while input_wave_reader.read_integer_frame(&mut buffer)? > 0 {
|
loop {
|
||||||
output_wave_writer.write_integer_frames(&buffer[n..=n])?;
|
let frames_read = reader.read_frames(&mut input_buffer)? as usize;
|
||||||
|
if frames_read == 0 {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
output_wave_writer.end()?;
|
output_buffer.resize(frames_read, S::EQUILIBRIUM);
|
||||||
input_wave_reader.locate(0)?;
|
|
||||||
|
for (n, writer) in writers.iter_mut().enumerate() {
|
||||||
|
for (output, input) in output_buffer
|
||||||
|
.iter_mut()
|
||||||
|
.zip(input_buffer.iter().skip(n).step_by(channel_count))
|
||||||
|
{
|
||||||
|
*output = *input;
|
||||||
|
}
|
||||||
|
writer.write_frames(&output_buffer)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for writer in writers.drain(..) {
|
||||||
|
writer.end()?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> io::Result<()> {
|
fn process_file<R>(mut input: WaveReader<R>, settings: Settings) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
R: Read + Seek,
|
||||||
|
{
|
||||||
|
let format = input.format()?;
|
||||||
|
|
||||||
|
use CommonFormat::*;
|
||||||
|
match (format.common_format(), format.bits_per_sample) {
|
||||||
|
(IntegerPCM, 8) => deinterleave_file::<u8, R>(input, format, settings),
|
||||||
|
(IntegerPCM, 16) => deinterleave_file::<i16, R>(input, format, settings),
|
||||||
|
(IntegerPCM, 24) => deinterleave_file::<I24, R>(input, format, settings),
|
||||||
|
(IntegerPCM, 32) => deinterleave_file::<i32, R>(input, format, settings),
|
||||||
|
(IeeeFloatPCM, 32) => deinterleave_file::<f32, R>(input, format, settings),
|
||||||
|
other => panic!("Unsupported format: {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Settings {
|
||||||
|
input_path: String,
|
||||||
|
delimiter: String,
|
||||||
|
use_numeric_names: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let matches = App::new("wave-deinter")
|
let matches = App::new("wave-deinter")
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.author(crate_authors!())
|
.author(crate_authors!())
|
||||||
@@ -126,13 +190,12 @@ fn main() -> io::Result<()> {
|
|||||||
)
|
)
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
let delimiter = matches.value_of("channel_delimiter").unwrap();
|
let settings = Settings {
|
||||||
let use_numeric_names = matches.is_present("numeric_names");
|
input_path: matches.value_of("INPUT").unwrap().into(),
|
||||||
let infile = matches.value_of("INPUT").unwrap();
|
delimiter: matches.value_of("channel_delimiter").unwrap().into(),
|
||||||
|
use_numeric_names: matches.is_present("numeric_names"),
|
||||||
|
};
|
||||||
|
|
||||||
match process_file(infile, delimiter, use_numeric_names) {
|
process_file(WaveReader::open(&settings.input_path)?, settings)?;
|
||||||
Err(Error::IOError(io)) => Err(io),
|
Ok(())
|
||||||
Err(e) => panic!("Error: {:?}", e),
|
|
||||||
Ok(()) => Ok(()),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
pub type LU = f32;
|
pub type LU = f32;
|
||||||
|
#[allow(clippy::upper_case_acronyms)]
|
||||||
pub type LUFS = f32;
|
pub type LUFS = f32;
|
||||||
pub type Decibels = f32;
|
pub type Decibels = f32;
|
||||||
|
|
||||||
|
|||||||
@@ -21,11 +21,7 @@ pub trait ReadBWaveChunks: Read {
|
|||||||
|
|
||||||
pub trait WriteBWaveChunks: Write {
|
pub trait WriteBWaveChunks: Write {
|
||||||
fn write_wave_fmt(&mut self, format: &WaveFmt) -> Result<(), ParserError>;
|
fn write_wave_fmt(&mut self, format: &WaveFmt) -> Result<(), ParserError>;
|
||||||
fn write_bext_string_field(
|
fn write_bext_string_field(&mut self, string: &str, length: usize) -> Result<(), ParserError>;
|
||||||
&mut self,
|
|
||||||
string: &String,
|
|
||||||
length: usize,
|
|
||||||
) -> Result<(), ParserError>;
|
|
||||||
fn write_bext(&mut self, bext: &Bext) -> Result<(), ParserError>;
|
fn write_bext(&mut self, bext: &Bext) -> Result<(), ParserError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +30,7 @@ where
|
|||||||
T: Write,
|
T: Write,
|
||||||
{
|
{
|
||||||
fn write_wave_fmt(&mut self, format: &WaveFmt) -> Result<(), ParserError> {
|
fn write_wave_fmt(&mut self, format: &WaveFmt) -> Result<(), ParserError> {
|
||||||
self.write_u16::<LittleEndian>(format.tag as u16)?;
|
self.write_u16::<LittleEndian>(format.tag)?;
|
||||||
self.write_u16::<LittleEndian>(format.channel_count)?;
|
self.write_u16::<LittleEndian>(format.channel_count)?;
|
||||||
self.write_u32::<LittleEndian>(format.sample_rate)?;
|
self.write_u32::<LittleEndian>(format.sample_rate)?;
|
||||||
self.write_u32::<LittleEndian>(format.bytes_per_second)?;
|
self.write_u32::<LittleEndian>(format.bytes_per_second)?;
|
||||||
@@ -46,18 +42,14 @@ where
|
|||||||
self.write_u16::<LittleEndian>(ext.valid_bits_per_sample)?;
|
self.write_u16::<LittleEndian>(ext.valid_bits_per_sample)?;
|
||||||
self.write_u32::<LittleEndian>(ext.channel_mask)?;
|
self.write_u32::<LittleEndian>(ext.channel_mask)?;
|
||||||
let uuid = ext.type_guid.as_bytes();
|
let uuid = ext.type_guid.as_bytes();
|
||||||
self.write(uuid)?;
|
self.write_all(uuid)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_bext_string_field(
|
fn write_bext_string_field(&mut self, string: &str, length: usize) -> Result<(), ParserError> {
|
||||||
&mut self,
|
|
||||||
string: &String,
|
|
||||||
length: usize,
|
|
||||||
) -> Result<(), ParserError> {
|
|
||||||
let mut buf = ASCII
|
let mut buf = ASCII
|
||||||
.encode(&string, EncoderTrap::Ignore)
|
.encode(string, EncoderTrap::Ignore)
|
||||||
.expect("Error encoding text");
|
.expect("Error encoding text");
|
||||||
buf.truncate(length);
|
buf.truncate(length);
|
||||||
let filler_length = length - buf.len();
|
let filler_length = length - buf.len();
|
||||||
@@ -142,12 +134,8 @@ where
|
|||||||
|
|
||||||
fn read_bext_string_field(&mut self, length: usize) -> Result<String, ParserError> {
|
fn read_bext_string_field(&mut self, length: usize) -> Result<String, ParserError> {
|
||||||
let mut buffer: Vec<u8> = vec![0; length];
|
let mut buffer: Vec<u8> = vec![0; length];
|
||||||
self.read(&mut buffer)?;
|
self.read_exact(&mut buffer)?;
|
||||||
let trimmed: Vec<u8> = buffer
|
let trimmed: Vec<u8> = buffer.iter().take_while(|c| **c != 0_u8).cloned().collect();
|
||||||
.iter()
|
|
||||||
.take_while(|c| **c != 0 as u8)
|
|
||||||
.cloned()
|
|
||||||
.collect();
|
|
||||||
Ok(ASCII
|
Ok(ASCII
|
||||||
.decode(&trimmed, DecoderTrap::Ignore)
|
.decode(&trimmed, DecoderTrap::Ignore)
|
||||||
.expect("Error decoding text"))
|
.expect("Error decoding text"))
|
||||||
@@ -168,7 +156,7 @@ where
|
|||||||
},
|
},
|
||||||
umid: {
|
umid: {
|
||||||
let mut buf = [0u8; 64];
|
let mut buf = [0u8; 64];
|
||||||
self.read(&mut buf)?;
|
self.read_exact(&mut buf)?;
|
||||||
if version > 0 {
|
if version > 0 {
|
||||||
Some(buf)
|
Some(buf)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
const BASIC_PCM: u16 = 0x0001;
|
pub const WAVE_TAG_PCM: u16 = 0x0001;
|
||||||
const BASIC_FLOAT: u16 = 0x0003;
|
pub const WAVE_TAG_FLOAT: u16 = 0x0003;
|
||||||
const BASIC_MPEG: u16 = 0x0050;
|
pub const WAVE_TAG_MPEG: u16 = 0x0050;
|
||||||
const BASIC_EXTENDED: u16 = 0xFFFE;
|
pub const WAVE_TAG_EXTENDED: u16 = 0xFFFE;
|
||||||
|
|
||||||
/* RC 2361 §4:
|
/* RC 2361 §4:
|
||||||
|
|
||||||
@@ -15,23 +15,23 @@ const BASIC_EXTENDED: u16 = 0xFFFE;
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
pub const UUID_PCM: Uuid = Uuid::from_bytes([
|
pub const WAVE_UUID_PCM: Uuid = Uuid::from_bytes([
|
||||||
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71,
|
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
pub const UUID_FLOAT: Uuid = Uuid::from_bytes([
|
pub const WAVE_UUID_FLOAT: Uuid = Uuid::from_bytes([
|
||||||
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71,
|
0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
pub const UUID_MPEG: Uuid = Uuid::from_bytes([
|
pub const WAVE_UUID_MPEG: Uuid = Uuid::from_bytes([
|
||||||
0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71,
|
0x50, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
pub const UUID_BFORMAT_PCM: Uuid = Uuid::from_bytes([
|
pub const WAVE_UUID_BFORMAT_PCM: Uuid = Uuid::from_bytes([
|
||||||
0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1, 0xca, 0x00, 0x00, 0x00,
|
0x01, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1, 0xca, 0x00, 0x00, 0x00,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
pub const UUID_BFORMAT_FLOAT: Uuid = Uuid::from_bytes([
|
pub const WAVE_UUID_BFORMAT_FLOAT: Uuid = Uuid::from_bytes([
|
||||||
0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1, 0xca, 0x00, 0x00, 0x00,
|
0x03, 0x00, 0x00, 0x00, 0x21, 0x07, 0xd3, 0x11, 0x86, 0x44, 0xc8, 0xc1, 0xca, 0x00, 0x00, 0x00,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -71,14 +71,14 @@ impl CommonFormat {
|
|||||||
/// Resolve a tag and Uuid to a `CommonFormat`.
|
/// Resolve a tag and Uuid to a `CommonFormat`.
|
||||||
pub fn make(basic: u16, uuid: Option<Uuid>) -> Self {
|
pub fn make(basic: u16, uuid: Option<Uuid>) -> Self {
|
||||||
match (basic, uuid) {
|
match (basic, uuid) {
|
||||||
(BASIC_PCM, _) => Self::IntegerPCM,
|
(WAVE_TAG_PCM, _) => Self::IntegerPCM,
|
||||||
(BASIC_FLOAT, _) => Self::IeeeFloatPCM,
|
(WAVE_TAG_FLOAT, _) => Self::IeeeFloatPCM,
|
||||||
(BASIC_MPEG, _) => Self::Mpeg,
|
(WAVE_TAG_MPEG, _) => Self::Mpeg,
|
||||||
(BASIC_EXTENDED, Some(UUID_PCM)) => Self::IntegerPCM,
|
(WAVE_TAG_EXTENDED, Some(WAVE_UUID_PCM)) => Self::IntegerPCM,
|
||||||
(BASIC_EXTENDED, Some(UUID_FLOAT)) => Self::IeeeFloatPCM,
|
(WAVE_TAG_EXTENDED, Some(WAVE_UUID_FLOAT)) => Self::IeeeFloatPCM,
|
||||||
(BASIC_EXTENDED, Some(UUID_BFORMAT_PCM)) => Self::AmbisonicBFormatIntegerPCM,
|
(WAVE_TAG_EXTENDED, Some(WAVE_UUID_BFORMAT_PCM)) => Self::AmbisonicBFormatIntegerPCM,
|
||||||
(BASIC_EXTENDED, Some(UUID_BFORMAT_FLOAT)) => Self::AmbisonicBFormatIeeeFloatPCM,
|
(WAVE_TAG_EXTENDED, Some(WAVE_UUID_BFORMAT_FLOAT)) => Self::AmbisonicBFormatIeeeFloatPCM,
|
||||||
(BASIC_EXTENDED, Some(x)) => CommonFormat::UnknownExtended(x),
|
(WAVE_TAG_EXTENDED, Some(x)) => CommonFormat::UnknownExtended(x),
|
||||||
(x, _) => CommonFormat::UnknownBasic(x),
|
(x, _) => CommonFormat::UnknownBasic(x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,13 +89,13 @@ impl CommonFormat {
|
|||||||
/// returned tag will be 0xFFFE and the `Uuid` will describe the format.
|
/// returned tag will be 0xFFFE and the `Uuid` will describe the format.
|
||||||
pub fn take(self) -> (u16, Uuid) {
|
pub fn take(self) -> (u16, Uuid) {
|
||||||
match self {
|
match self {
|
||||||
Self::IntegerPCM => (BASIC_PCM, UUID_PCM),
|
Self::IntegerPCM => (WAVE_TAG_PCM, WAVE_UUID_PCM),
|
||||||
Self::IeeeFloatPCM => (BASIC_FLOAT, UUID_FLOAT),
|
Self::IeeeFloatPCM => (WAVE_TAG_FLOAT, WAVE_UUID_FLOAT),
|
||||||
Self::Mpeg => (BASIC_MPEG, UUID_MPEG),
|
Self::Mpeg => (WAVE_TAG_MPEG, WAVE_UUID_MPEG),
|
||||||
Self::AmbisonicBFormatIntegerPCM => (BASIC_EXTENDED, UUID_BFORMAT_PCM),
|
Self::AmbisonicBFormatIntegerPCM => (WAVE_TAG_EXTENDED, WAVE_UUID_BFORMAT_PCM),
|
||||||
Self::AmbisonicBFormatIeeeFloatPCM => (BASIC_EXTENDED, UUID_BFORMAT_FLOAT),
|
Self::AmbisonicBFormatIeeeFloatPCM => (WAVE_TAG_EXTENDED, WAVE_UUID_BFORMAT_FLOAT),
|
||||||
Self::UnknownBasic(x) => (x, uuid_from_basic_tag(x)),
|
Self::UnknownBasic(x) => (x, uuid_from_basic_tag(x)),
|
||||||
Self::UnknownExtended(x) => (BASIC_EXTENDED, x),
|
Self::UnknownExtended(x) => (WAVE_TAG_EXTENDED, x),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
55
src/cue.rs
55
src/cue.rs
@@ -1,6 +1,7 @@
|
|||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
use super::fourcc::{FourCC,ReadFourCC, WriteFourCC, LABL_SIG, NOTE_SIG,
|
use super::fourcc::{
|
||||||
ADTL_SIG, LTXT_SIG, DATA_SIG};
|
FourCC, ReadFourCC, WriteFourCC, ADTL_SIG, DATA_SIG, LABL_SIG, LTXT_SIG, NOTE_SIG,
|
||||||
|
};
|
||||||
|
|
||||||
use super::list_form::collect_list_form;
|
use super::list_form::collect_list_form;
|
||||||
|
|
||||||
@@ -68,10 +69,8 @@ struct RawLabel {
|
|||||||
impl RawLabel {
|
impl RawLabel {
|
||||||
fn write_to(&self) -> Vec<u8> {
|
fn write_to(&self) -> Vec<u8> {
|
||||||
let mut writer = Cursor::new(vec![0u8; 0]);
|
let mut writer = Cursor::new(vec![0u8; 0]);
|
||||||
writer
|
writer.write_u32::<LittleEndian>(self.cue_point_id).unwrap();
|
||||||
.write_u32::<LittleEndian>(self.cue_point_id as u32)
|
writer.write_all(&self.text).unwrap();
|
||||||
.unwrap();
|
|
||||||
writer.write(&self.text).unwrap();
|
|
||||||
writer.into_inner()
|
writer.into_inner()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +81,7 @@ impl RawLabel {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
cue_point_id: rdr.read_u32::<LittleEndian>()?,
|
cue_point_id: rdr.read_u32::<LittleEndian>()?,
|
||||||
text: {
|
text: {
|
||||||
let mut buf = vec![0u8; (length - 4) as usize];
|
let mut buf = vec![0u8; length - 4];
|
||||||
rdr.read_exact(&mut buf)?;
|
rdr.read_exact(&mut buf)?;
|
||||||
buf
|
buf
|
||||||
},
|
},
|
||||||
@@ -100,7 +99,7 @@ impl RawNote {
|
|||||||
fn write_to(&self) -> Vec<u8> {
|
fn write_to(&self) -> Vec<u8> {
|
||||||
let mut writer = Cursor::new(vec![0u8; 0]);
|
let mut writer = Cursor::new(vec![0u8; 0]);
|
||||||
writer.write_u32::<LittleEndian>(self.cue_point_id).unwrap();
|
writer.write_u32::<LittleEndian>(self.cue_point_id).unwrap();
|
||||||
writer.write(&self.text).unwrap();
|
writer.write_all(&self.text).unwrap();
|
||||||
writer.into_inner()
|
writer.into_inner()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +110,7 @@ impl RawNote {
|
|||||||
Ok(Self {
|
Ok(Self {
|
||||||
cue_point_id: rdr.read_u32::<LittleEndian>()?,
|
cue_point_id: rdr.read_u32::<LittleEndian>()?,
|
||||||
text: {
|
text: {
|
||||||
let mut buf = vec![0u8; (length - 4) as usize];
|
let mut buf = vec![0u8; length - 4];
|
||||||
rdr.read_exact(&mut buf)?;
|
rdr.read_exact(&mut buf)?;
|
||||||
buf
|
buf
|
||||||
},
|
},
|
||||||
@@ -142,7 +141,7 @@ impl RawLtxt {
|
|||||||
writer.write_u16::<LittleEndian>(self.dialect).unwrap();
|
writer.write_u16::<LittleEndian>(self.dialect).unwrap();
|
||||||
writer.write_u16::<LittleEndian>(self.code_page).unwrap();
|
writer.write_u16::<LittleEndian>(self.code_page).unwrap();
|
||||||
if let Some(ext_text) = &self.text {
|
if let Some(ext_text) = &self.text {
|
||||||
writer.write(ext_text).unwrap();
|
writer.write_all(ext_text).unwrap();
|
||||||
}
|
}
|
||||||
writer.into_inner()
|
writer.into_inner()
|
||||||
}
|
}
|
||||||
@@ -161,7 +160,7 @@ impl RawLtxt {
|
|||||||
code_page: rdr.read_u16::<LittleEndian>()?,
|
code_page: rdr.read_u16::<LittleEndian>()?,
|
||||||
text: {
|
text: {
|
||||||
if length - 20 > 0 {
|
if length - 20 > 0 {
|
||||||
let mut buf = vec![0u8; (length - 20) as usize];
|
let mut buf = vec![0u8; length - 20];
|
||||||
rdr.read_exact(&mut buf)?;
|
rdr.read_exact(&mut buf)?;
|
||||||
Some(buf)
|
Some(buf)
|
||||||
} else {
|
} else {
|
||||||
@@ -186,14 +185,14 @@ impl RawAdtlMember {
|
|||||||
// It seems like all this casing could be done with traits
|
// It seems like all this casing could be done with traits
|
||||||
for member in members.iter() {
|
for member in members.iter() {
|
||||||
let (fcc, buf) = match member {
|
let (fcc, buf) = match member {
|
||||||
RawAdtlMember::Label(l) => ((LABL_SIG, l.write_to())),
|
RawAdtlMember::Label(l) => (LABL_SIG, l.write_to()),
|
||||||
RawAdtlMember::Note(n) => ((NOTE_SIG, n.write_to())),
|
RawAdtlMember::Note(n) => (NOTE_SIG, n.write_to()),
|
||||||
RawAdtlMember::LabeledText(t) => ((LTXT_SIG, t.write_to())),
|
RawAdtlMember::LabeledText(t) => (LTXT_SIG, t.write_to()),
|
||||||
RawAdtlMember::Unrecognized(f) => (*f, vec![0u8; 0]), // <-- this is a dopey case but here for completeness
|
RawAdtlMember::Unrecognized(f) => (*f, vec![0u8; 0]), // <-- this is a dopey case but here for completeness
|
||||||
};
|
};
|
||||||
w.write_fourcc(fcc).unwrap();
|
w.write_fourcc(fcc).unwrap();
|
||||||
w.write_u32::<LittleEndian>(buf.len() as u32).unwrap();
|
w.write_u32::<LittleEndian>(buf.len() as u32).unwrap();
|
||||||
w.write(&buf).unwrap();
|
w.write_all(&buf).unwrap();
|
||||||
if buf.len() % 2 == 1 {
|
if buf.len() % 2 == 1 {
|
||||||
w.write_u8(0).unwrap();
|
w.write_u8(0).unwrap();
|
||||||
}
|
}
|
||||||
@@ -205,7 +204,7 @@ impl RawAdtlMember {
|
|||||||
writer
|
writer
|
||||||
.write_u32::<LittleEndian>(chunk_content.len() as u32)
|
.write_u32::<LittleEndian>(chunk_content.len() as u32)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
writer.write(&chunk_content).unwrap();
|
writer.write_all(&chunk_content).unwrap();
|
||||||
writer.into_inner()
|
writer.into_inner()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,11 +290,7 @@ pub struct Cue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn convert_to_cue_string(buffer: &[u8]) -> String {
|
fn convert_to_cue_string(buffer: &[u8]) -> String {
|
||||||
let trimmed: Vec<u8> = buffer
|
let trimmed: Vec<u8> = buffer.iter().take_while(|c| **c != 0_u8).cloned().collect();
|
||||||
.iter()
|
|
||||||
.take_while(|c| **c != 0 as u8)
|
|
||||||
.cloned()
|
|
||||||
.collect();
|
|
||||||
ASCII
|
ASCII
|
||||||
.decode(&trimmed, DecoderTrap::Ignore)
|
.decode(&trimmed, DecoderTrap::Ignore)
|
||||||
.expect("Error decoding text")
|
.expect("Error decoding text")
|
||||||
@@ -303,7 +298,7 @@ fn convert_to_cue_string(buffer: &[u8]) -> String {
|
|||||||
|
|
||||||
fn convert_from_cue_string(val: &str) -> Vec<u8> {
|
fn convert_from_cue_string(val: &str) -> Vec<u8> {
|
||||||
ASCII
|
ASCII
|
||||||
.encode(&val, EncoderTrap::Ignore)
|
.encode(val, EncoderTrap::Ignore)
|
||||||
.expect("Error encoding text")
|
.expect("Error encoding text")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -324,12 +319,12 @@ impl Cue {
|
|||||||
|
|
||||||
let raw_label = cue.label.as_ref().map(|val| RawLabel {
|
let raw_label = cue.label.as_ref().map(|val| RawLabel {
|
||||||
cue_point_id: n as u32,
|
cue_point_id: n as u32,
|
||||||
text: convert_from_cue_string(&val),
|
text: convert_from_cue_string(val),
|
||||||
});
|
});
|
||||||
|
|
||||||
let raw_note = cue.note.as_ref().map(|val| RawNote {
|
let raw_note = cue.note.as_ref().map(|val| RawNote {
|
||||||
cue_point_id: n as u32,
|
cue_point_id: n as u32,
|
||||||
text: convert_from_cue_string(&val),
|
text: convert_from_cue_string(val),
|
||||||
});
|
});
|
||||||
|
|
||||||
let raw_ltxt = cue.length.map(|val| RawLtxt {
|
let raw_ltxt = cue.length.map(|val| RawLtxt {
|
||||||
@@ -349,9 +344,15 @@ impl Cue {
|
|||||||
(Vec::<RawCue>::new(), Vec::<RawAdtlMember>::new()),
|
(Vec::<RawCue>::new(), Vec::<RawAdtlMember>::new()),
|
||||||
|(mut cues, mut adtls), (cue, label, note, ltxt)| {
|
|(mut cues, mut adtls), (cue, label, note, ltxt)| {
|
||||||
cues.push(cue);
|
cues.push(cue);
|
||||||
label.map(|l| adtls.push(RawAdtlMember::Label(l)));
|
if let Some(l) = label {
|
||||||
note.map(|n| adtls.push(RawAdtlMember::Note(n)));
|
adtls.push(RawAdtlMember::Label(l))
|
||||||
ltxt.map(|m| adtls.push(RawAdtlMember::LabeledText(m)));
|
}
|
||||||
|
if let Some(n) = note {
|
||||||
|
adtls.push(RawAdtlMember::Note(n))
|
||||||
|
}
|
||||||
|
if let Some(m) = ltxt {
|
||||||
|
adtls.push(RawAdtlMember::LabeledText(m))
|
||||||
|
}
|
||||||
(cues, adtls)
|
(cues, adtls)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -5,8 +5,6 @@ use std::{
|
|||||||
io,
|
io,
|
||||||
};
|
};
|
||||||
|
|
||||||
use uuid;
|
|
||||||
|
|
||||||
/// Errors returned by methods in this crate.
|
/// Errors returned by methods in this crate.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
@@ -43,6 +41,12 @@ pub enum Error {
|
|||||||
|
|
||||||
/// The file is not optimized for writing new data
|
/// The file is not optimized for writing new data
|
||||||
DataChunkNotPreparedForAppend,
|
DataChunkNotPreparedForAppend,
|
||||||
|
|
||||||
|
/// A buffer with a length that isn't a multiple of channel_count was provided
|
||||||
|
InvalidBufferSize {
|
||||||
|
buffer_size: usize,
|
||||||
|
channel_count: u16,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StdError for Error {}
|
impl StdError for Error {}
|
||||||
|
|||||||
66
src/fmt.rs
66
src/fmt.rs
@@ -1,9 +1,11 @@
|
|||||||
use super::common_format::{CommonFormat, UUID_BFORMAT_PCM, UUID_PCM};
|
use crate::common_format::{CommonFormat, WAVE_UUID_BFORMAT_PCM, WAVE_UUID_PCM};
|
||||||
|
use crate::Sample;
|
||||||
|
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use byteorder::LittleEndian;
|
use byteorder::LittleEndian;
|
||||||
use byteorder::{ReadBytesExt, WriteBytesExt};
|
use byteorder::ReadBytesExt;
|
||||||
|
|
||||||
// Need more test cases for ADMAudioID
|
// Need more test cases for ADMAudioID
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
@@ -105,7 +107,7 @@ impl ChannelMask {
|
|||||||
(0..18)
|
(0..18)
|
||||||
.map(|i| 1 << i)
|
.map(|i| 1 << i)
|
||||||
.filter(|mask| mask & input_mask > 0)
|
.filter(|mask| mask & input_mask > 0)
|
||||||
.map(|mask| Into::<ChannelMask>::into(mask))
|
.map(ChannelMask::from)
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -238,7 +240,7 @@ impl WaveFmt {
|
|||||||
extended_format: Some(WaveFmtExtended {
|
extended_format: Some(WaveFmtExtended {
|
||||||
valid_bits_per_sample: bits_per_sample,
|
valid_bits_per_sample: bits_per_sample,
|
||||||
channel_mask: ChannelMask::DirectOut as u32,
|
channel_mask: ChannelMask::DirectOut as u32,
|
||||||
type_guid: UUID_BFORMAT_PCM,
|
type_guid: WAVE_UUID_BFORMAT_PCM,
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -261,28 +263,24 @@ impl WaveFmt {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let result: (u16, Option<WaveFmtExtended>) = match channel_bitmap {
|
let result: (u16, Option<WaveFmtExtended>) = match channel_bitmap {
|
||||||
ch if bits_per_sample != container_bits_per_sample => {
|
ch if bits_per_sample != container_bits_per_sample => (
|
||||||
(
|
|
||||||
0xFFFE,
|
0xFFFE,
|
||||||
Some(WaveFmtExtended {
|
Some(WaveFmtExtended {
|
||||||
valid_bits_per_sample: bits_per_sample,
|
valid_bits_per_sample: bits_per_sample,
|
||||||
channel_mask: ch,
|
channel_mask: ch,
|
||||||
type_guid: UUID_PCM,
|
type_guid: WAVE_UUID_PCM,
|
||||||
}),
|
}),
|
||||||
)
|
),
|
||||||
}
|
|
||||||
0b0100 => (0x0001, None),
|
0b0100 => (0x0001, None),
|
||||||
0b0011 => (0x0001, None),
|
0b0011 => (0x0001, None),
|
||||||
ch => {
|
ch => (
|
||||||
(
|
|
||||||
0xFFFE,
|
0xFFFE,
|
||||||
Some(WaveFmtExtended {
|
Some(WaveFmtExtended {
|
||||||
valid_bits_per_sample: bits_per_sample,
|
valid_bits_per_sample: bits_per_sample,
|
||||||
channel_mask: ch,
|
channel_mask: ch,
|
||||||
type_guid: UUID_PCM,
|
type_guid: WAVE_UUID_PCM,
|
||||||
}),
|
}),
|
||||||
)
|
),
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let (tag, extformat) = result;
|
let (tag, extformat) = result;
|
||||||
@@ -313,8 +311,8 @@ impl WaveFmt {
|
|||||||
///
|
///
|
||||||
/// This is a conveneince method that creates a `Vec<i32>` with
|
/// This is a conveneince method that creates a `Vec<i32>` with
|
||||||
/// as many elements as there are channels in the underlying stream.
|
/// as many elements as there are channels in the underlying stream.
|
||||||
pub fn create_frame_buffer(&self, length: usize) -> Vec<i32> {
|
pub fn create_frame_buffer<S: Sample>(&self, length: usize) -> Vec<S> {
|
||||||
vec![0i32; self.channel_count as usize * length]
|
vec![S::EQUILIBRIUM; self.channel_count as usize * length]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a raw byte buffer to hold `length` blocks from a reader or
|
/// Create a raw byte buffer to hold `length` blocks from a reader or
|
||||||
@@ -323,33 +321,11 @@ impl WaveFmt {
|
|||||||
vec![0u8; self.block_alignment as usize * length]
|
vec![0u8; self.block_alignment as usize * length]
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write frames into a byte vector
|
|
||||||
pub fn pack_frames(&self, from_frames: &[i32], into_bytes: &mut [u8]) -> () {
|
|
||||||
let mut write_cursor = Cursor::new(into_bytes);
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
from_frames.len() % self.channel_count as usize == 0,
|
|
||||||
"frames buffer does not contain a number of samples % channel_count == 0"
|
|
||||||
);
|
|
||||||
|
|
||||||
for n in 0..from_frames.len() {
|
|
||||||
match (self.valid_bits_per_sample(), self.bits_per_sample) {
|
|
||||||
(0..=8,8) => write_cursor.write_u8((from_frames[n] + 0x80) as u8 ).unwrap(), // EBU 3285 §A2.2
|
|
||||||
(9..=16,16) => write_cursor.write_i16::<LittleEndian>(from_frames[n] as i16).unwrap(),
|
|
||||||
(10..=24,24) => write_cursor.write_i24::<LittleEndian>(from_frames[n]).unwrap(),
|
|
||||||
(25..=32,32) => write_cursor.write_i32::<LittleEndian>(from_frames[n]).unwrap(),
|
|
||||||
(b,_)=> panic!("Unrecognized integer format, bits per sample {}, channels {}, block_alignment {}",
|
|
||||||
b, self.channel_count, self.block_alignment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read bytes into frames
|
/// Read bytes into frames
|
||||||
pub fn unpack_frames(&self, from_bytes: &[u8], into_frames: &mut [i32]) -> () {
|
pub fn unpack_frames(&self, from_bytes: &[u8], into_frames: &mut [i32]) {
|
||||||
let mut rdr = Cursor::new(from_bytes);
|
let mut rdr = Cursor::new(from_bytes);
|
||||||
for n in 0..(into_frames.len()) {
|
for frame in into_frames {
|
||||||
into_frames[n] = match (self.valid_bits_per_sample(), self.bits_per_sample) {
|
*frame = match (self.valid_bits_per_sample(), self.bits_per_sample) {
|
||||||
(0..=8,8) => rdr.read_u8().unwrap() as i32 - 0x80_i32, // EBU 3285 §A2.2
|
(0..=8,8) => rdr.read_u8().unwrap() as i32 - 0x80_i32, // EBU 3285 §A2.2
|
||||||
(9..=16,16) => rdr.read_i16::<LittleEndian>().unwrap() as i32,
|
(9..=16,16) => rdr.read_i16::<LittleEndian>().unwrap() as i32,
|
||||||
(10..=24,24) => rdr.read_i24::<LittleEndian>().unwrap(),
|
(10..=24,24) => rdr.read_i24::<LittleEndian>().unwrap(),
|
||||||
@@ -425,8 +401,8 @@ where
|
|||||||
) -> Result<usize, std::io::Error> {
|
) -> Result<usize, std::io::Error> {
|
||||||
assert!(into.len() % format.channel_count as usize == 0);
|
assert!(into.len() % format.channel_count as usize == 0);
|
||||||
|
|
||||||
for n in 0..(into.len()) {
|
for frame in into {
|
||||||
into[n] = match (format.valid_bits_per_sample(), format.bits_per_sample) {
|
*frame = match (format.valid_bits_per_sample(), format.bits_per_sample) {
|
||||||
(0..=8,8) => self.read_u8().unwrap() as i32 - 0x80_i32, // EBU 3285 §A2.2
|
(0..=8,8) => self.read_u8().unwrap() as i32 - 0x80_i32, // EBU 3285 §A2.2
|
||||||
(9..=16,16) => self.read_i16::<LittleEndian>().unwrap() as i32,
|
(9..=16,16) => self.read_i16::<LittleEndian>().unwrap() as i32,
|
||||||
(10..=24,24) => self.read_i24::<LittleEndian>().unwrap(),
|
(10..=24,24) => self.read_i24::<LittleEndian>().unwrap(),
|
||||||
@@ -457,10 +433,10 @@ impl<T> WriteWavAudioData for T
|
|||||||
where
|
where
|
||||||
T: std::io::Write,
|
T: std::io::Write,
|
||||||
{
|
{
|
||||||
fn write_i32_frames(&mut self, format: WaveFmt, _: &[i32]) -> Result<usize, std::io::Error> {
|
fn write_i32_frames(&mut self, _format: WaveFmt, _: &[i32]) -> Result<usize, std::io::Error> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
fn write_f32_frames(&mut self, format: WaveFmt, _: &[f32]) -> Result<usize, std::io::Error> {
|
fn write_f32_frames(&mut self, _format: WaveFmt, _: &[f32]) -> Result<usize, std::io::Error> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,13 +52,19 @@ mod chunks;
|
|||||||
mod cue;
|
mod cue;
|
||||||
mod fmt;
|
mod fmt;
|
||||||
|
|
||||||
|
mod sample;
|
||||||
|
|
||||||
mod wavereader;
|
mod wavereader;
|
||||||
mod wavewriter;
|
mod wavewriter;
|
||||||
|
|
||||||
pub use bext::Bext;
|
pub use bext::Bext;
|
||||||
pub use common_format::CommonFormat;
|
pub use common_format::{
|
||||||
|
CommonFormat, WAVE_TAG_EXTENDED, WAVE_TAG_FLOAT, WAVE_TAG_MPEG, WAVE_TAG_PCM,
|
||||||
|
WAVE_UUID_BFORMAT_FLOAT, WAVE_UUID_BFORMAT_PCM, WAVE_UUID_FLOAT, WAVE_UUID_MPEG, WAVE_UUID_PCM,
|
||||||
|
};
|
||||||
pub use cue::Cue;
|
pub use cue::Cue;
|
||||||
pub use errors::Error;
|
pub use errors::Error;
|
||||||
pub use fmt::{ADMAudioID, ChannelDescriptor, ChannelMask, WaveFmt, WaveFmtExtended};
|
pub use fmt::{ADMAudioID, ChannelDescriptor, ChannelMask, WaveFmt, WaveFmtExtended};
|
||||||
|
pub use sample::{Sample, I24};
|
||||||
pub use wavereader::{AudioFrameReader, WaveReader};
|
pub use wavereader::{AudioFrameReader, WaveReader};
|
||||||
pub use wavewriter::{AudioFrameWriter, WaveWriter};
|
pub use wavewriter::{AudioFrameWriter, WaveWriter};
|
||||||
|
|||||||
@@ -71,11 +71,11 @@ impl<R: Read + Seek> Parser<R> {
|
|||||||
let newmap: HashMap<FourCC, u64> = HashMap::new();
|
let newmap: HashMap<FourCC, u64> = HashMap::new();
|
||||||
let mut the_stream = stream;
|
let mut the_stream = stream;
|
||||||
the_stream.seek(Start(0))?;
|
the_stream.seek(Start(0))?;
|
||||||
return Ok(Parser {
|
Ok(Parser {
|
||||||
stream: the_stream,
|
stream: the_stream,
|
||||||
state: State::New,
|
state: State::New,
|
||||||
ds64state: newmap,
|
ds64state: newmap,
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn into_inner(self) -> R {
|
// pub fn into_inner(self) -> R {
|
||||||
@@ -131,7 +131,7 @@ impl<R: Read + Seek> Iterator for Parser<R> {
|
|||||||
fn next(&mut self) -> Option<Event> {
|
fn next(&mut self) -> Option<Event> {
|
||||||
let (event, next_state) = self.advance();
|
let (event, next_state) = self.advance();
|
||||||
self.state = next_state;
|
self.state = next_state;
|
||||||
return event;
|
event
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -171,7 +171,7 @@ impl<R: Read + Seek> Parser<R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok((event, next_state));
|
Ok((event, next_state))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_ds64(&mut self) -> Result<(Event, State), Error> {
|
fn parse_ds64(&mut self) -> Result<(Event, State), Error> {
|
||||||
@@ -182,7 +182,7 @@ impl<R: Read + Seek> Parser<R> {
|
|||||||
let mut read: u64 = 0;
|
let mut read: u64 = 0;
|
||||||
|
|
||||||
if ds64_sig != DS64_SIG {
|
if ds64_sig != DS64_SIG {
|
||||||
return Err(Error::MissingRequiredDS64);
|
Err(Error::MissingRequiredDS64)
|
||||||
} else {
|
} else {
|
||||||
let long_file_size = self.stream.read_u64::<LittleEndian>()?;
|
let long_file_size = self.stream.read_u64::<LittleEndian>()?;
|
||||||
let long_data_size = self.stream.read_u64::<LittleEndian>()?;
|
let long_data_size = self.stream.read_u64::<LittleEndian>()?;
|
||||||
@@ -221,7 +221,7 @@ impl<R: Read + Seek> Parser<R> {
|
|||||||
remaining: long_file_size - (4 + 8 + ds64_size),
|
remaining: long_file_size - (4 + 8 + ds64_size),
|
||||||
};
|
};
|
||||||
|
|
||||||
return Ok((event, state));
|
Ok((event, state))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -262,48 +262,33 @@ impl<R: Read + Seek> Parser<R> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok((event, state));
|
Ok((event, state))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_state(&mut self) -> Result<(Option<Event>, State), Error> {
|
fn handle_state(&mut self) -> Result<(Option<Event>, State), Error> {
|
||||||
match self.state {
|
match self.state {
|
||||||
State::New => {
|
State::New => Ok((Some(Event::StartParse), State::ReadyForHeader)),
|
||||||
return Ok((Some(Event::StartParse), State::ReadyForHeader));
|
|
||||||
}
|
|
||||||
State::ReadyForHeader => {
|
State::ReadyForHeader => {
|
||||||
let (event, state) = self.parse_header()?;
|
let (event, state) = self.parse_header()?;
|
||||||
return Ok((Some(event), state));
|
Ok((Some(event), state))
|
||||||
}
|
}
|
||||||
State::ReadyForDS64 => {
|
State::ReadyForDS64 => {
|
||||||
let (event, state) = self.parse_ds64()?;
|
let (event, state) = self.parse_ds64()?;
|
||||||
return Ok((Some(event), state));
|
Ok((Some(event), state))
|
||||||
}
|
}
|
||||||
State::ReadyForChunk { at, remaining } => {
|
State::ReadyForChunk { at, remaining } => {
|
||||||
let (event, state) = self.enter_chunk(at, remaining)?;
|
let (event, state) = self.enter_chunk(at, remaining)?;
|
||||||
return Ok((Some(event), state));
|
Ok((Some(event), state))
|
||||||
}
|
|
||||||
State::Error => {
|
|
||||||
return Ok((Some(Event::FinishParse), State::Complete));
|
|
||||||
}
|
|
||||||
State::Complete => {
|
|
||||||
return Ok((None, State::Complete));
|
|
||||||
}
|
}
|
||||||
|
State::Error => Ok((Some(Event::FinishParse), State::Complete)),
|
||||||
|
State::Complete => Ok((None, State::Complete)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn advance(&mut self) -> (Option<Event>, State) {
|
fn advance(&mut self) -> (Option<Event>, State) {
|
||||||
match self.handle_state() {
|
match self.handle_state() {
|
||||||
Ok((event, state)) => {
|
Ok((event, state)) => (event, state),
|
||||||
return (event, state);
|
Err(error) => (Some(Event::Failed { error }), State::Error),
|
||||||
}
|
|
||||||
Err(error) => {
|
|
||||||
return (
|
|
||||||
Some(Event::Failed {
|
|
||||||
error: error.into(),
|
|
||||||
}),
|
|
||||||
State::Error,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/sample.rs
Normal file
14
src/sample.rs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
pub use dasp_sample::I24;
|
||||||
|
|
||||||
|
use dasp_sample::Duplex;
|
||||||
|
|
||||||
|
pub trait Sample:
|
||||||
|
dasp_sample::Sample + Duplex<u8> + Duplex<i16> + Duplex<I24> + Duplex<i32> + Duplex<f32>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sample for u8 {}
|
||||||
|
impl Sample for i16 {}
|
||||||
|
impl Sample for I24 {}
|
||||||
|
impl Sample for i32 {}
|
||||||
|
impl Sample for f32 {}
|
||||||
@@ -4,7 +4,7 @@ use std::path::Path;
|
|||||||
|
|
||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use std::io::SeekFrom;
|
use std::io::SeekFrom;
|
||||||
use std::io::SeekFrom::{Current, Start};
|
use std::io::SeekFrom::Start;
|
||||||
use std::io::{BufReader, Read, Seek};
|
use std::io::{BufReader, Read, Seek};
|
||||||
|
|
||||||
use super::bext::Bext;
|
use super::bext::Bext;
|
||||||
@@ -18,11 +18,13 @@ use super::fourcc::{
|
|||||||
IXML_SIG, JUNK_SIG, LIST_SIG,
|
IXML_SIG, JUNK_SIG, LIST_SIG,
|
||||||
};
|
};
|
||||||
use super::parser::Parser;
|
use super::parser::Parser;
|
||||||
use super::CommonFormat;
|
use super::{CommonFormat, Sample, I24};
|
||||||
|
|
||||||
use byteorder::LittleEndian;
|
use byteorder::LittleEndian;
|
||||||
use byteorder::ReadBytesExt;
|
use byteorder::ReadBytesExt;
|
||||||
|
|
||||||
|
use dasp_sample::Sample as _; // Expose to_sample()
|
||||||
|
|
||||||
/// Read audio frames
|
/// Read audio frames
|
||||||
///
|
///
|
||||||
/// The inner reader is interpreted as a raw audio data
|
/// The inner reader is interpreted as a raw audio data
|
||||||
@@ -88,78 +90,79 @@ impl<R: Read + Seek> AudioFrameReader<R> {
|
|||||||
Ok((seek_result - self.start) / self.format.block_alignment as u64)
|
Ok((seek_result - self.start) / self.format.block_alignment as u64)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read a frame
|
/// Reads frames from the file into the provided buffer
|
||||||
///
|
///
|
||||||
/// A single frame is read from the audio stream and the read location
|
/// The function will attempt to fill the buffer, but will stop without error when the end of
|
||||||
/// is advanced one frame.
|
/// the file is reached.
|
||||||
///
|
///
|
||||||
/// Regardless of the number of bits in the audio sample, this method
|
/// The reader will convert from the file's sample type into the buffer's sample type.
|
||||||
/// always writes `i32` samples back to the buffer. These samples are
|
/// Note that no dithering will be applied during sample type conversion,
|
||||||
/// written back "right-aligned" so samples that are shorter than i32
|
/// if dithering is required then it will need to be applied manually.
|
||||||
/// will leave the MSB bits empty.
|
|
||||||
///
|
///
|
||||||
/// For example: A full-code sample in 16 bit (0xFFFF) will be written
|
/// The return value is the number of frames read into the buffer.
|
||||||
/// back to the buffer as 0x0000FFFF.
|
pub fn read_frames<S>(&mut self, buffer: &mut [S]) -> Result<u64, Error>
|
||||||
///
|
where
|
||||||
///
|
S: Sample,
|
||||||
/// ### Panics
|
{
|
||||||
///
|
use CommonFormat::*;
|
||||||
/// The `buffer` must have a number of elements equal to the number of
|
|
||||||
/// channels and this method will panic if this is not the case.
|
|
||||||
pub fn read_integer_frame(&mut self, buffer: &mut [i32]) -> Result<u64, Error> {
|
|
||||||
assert!(
|
|
||||||
buffer.len() as u16 == self.format.channel_count,
|
|
||||||
"read_integer_frame was called with a mis-sized buffer, expected {}, was {}",
|
|
||||||
self.format.channel_count,
|
|
||||||
buffer.len()
|
|
||||||
);
|
|
||||||
|
|
||||||
let framed_bits_per_sample = self.format.block_alignment * 8 / self.format.channel_count;
|
let channel_count = self.format.channel_count as usize;
|
||||||
|
let common_format = self.format.common_format();
|
||||||
|
let bits_per_sample = self.format.bits_per_sample;
|
||||||
|
|
||||||
let tell = self.inner.seek(Current(0))?;
|
if buffer.len() % channel_count != 0 {
|
||||||
|
return Err(Error::InvalidBufferSize {
|
||||||
if (tell - self.start) < self.length {
|
buffer_size: buffer.len(),
|
||||||
for n in 0..(self.format.channel_count as usize) {
|
channel_count: self.format.channel_count,
|
||||||
buffer[n] = match (self.format.bits_per_sample, framed_bits_per_sample) {
|
});
|
||||||
(0..=8,8) => self.inner.read_u8()? as i32 - 0x80_i32, // EBU 3285 §A2.2
|
|
||||||
(9..=16,16) => self.inner.read_i16::<LittleEndian>()? as i32,
|
|
||||||
(10..=24,24) => self.inner.read_i24::<LittleEndian>()?,
|
|
||||||
(25..=32,32) => self.inner.read_i32::<LittleEndian>()?,
|
|
||||||
(b,_)=> panic!("Unrecognized integer format, bits per sample {}, channels {}, block_alignment {}",
|
|
||||||
b, self.format.channel_count, self.format.block_alignment)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(1)
|
|
||||||
} else {
|
|
||||||
Ok(0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_float_frame(&mut self, buffer: &mut [f32]) -> Result<u64, Error> {
|
let position = self.inner.stream_position()? - self.start;
|
||||||
assert!(
|
let frames_requested = (buffer.len() / channel_count) as u64;
|
||||||
buffer.len() as u16 == self.format.channel_count,
|
let bytes_per_frame = self.format.block_alignment as u64;
|
||||||
"read_float_frame was called with a mis-sized buffer, expected {}, was {}",
|
let frames_remaining = (self.length - position) / bytes_per_frame;
|
||||||
self.format.channel_count,
|
let frames_to_read = frames_requested.min(frames_remaining);
|
||||||
buffer.len()
|
let samples_to_read = frames_to_read as usize * channel_count;
|
||||||
);
|
|
||||||
|
|
||||||
let framed_bits_per_sample = self.format.block_alignment * 8 / self.format.channel_count;
|
match (common_format, bits_per_sample) {
|
||||||
|
(IntegerPCM, 8) => read_into_buffer(samples_to_read, buffer, || {
|
||||||
|
Ok(self.inner.read_u8()?.to_sample())
|
||||||
|
}),
|
||||||
|
(IntegerPCM, 16) => read_into_buffer(samples_to_read, buffer, || {
|
||||||
|
Ok(self.inner.read_i16::<LittleEndian>()?.to_sample())
|
||||||
|
}),
|
||||||
|
(IntegerPCM, 24) => read_into_buffer(samples_to_read, buffer, || {
|
||||||
|
Ok(I24::from(self.inner.read_i24::<LittleEndian>()?).to_sample())
|
||||||
|
}),
|
||||||
|
(IntegerPCM, 32) => read_into_buffer(samples_to_read, buffer, || {
|
||||||
|
Ok(self.inner.read_i32::<LittleEndian>()?.to_sample())
|
||||||
|
}),
|
||||||
|
(IeeeFloatPCM, 32) => read_into_buffer(samples_to_read, buffer, || {
|
||||||
|
Ok(self.inner.read_f32::<LittleEndian>()?.to_sample())
|
||||||
|
}),
|
||||||
|
(_, _) => panic!(
|
||||||
|
"Unsupported format, bits per sample {}, channels {}, sample format: {:?}",
|
||||||
|
bits_per_sample, channel_count, common_format
|
||||||
|
),
|
||||||
|
}?;
|
||||||
|
|
||||||
let tell = self.inner.seek(Current(0))?;
|
Ok(frames_to_read)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (tell - self.start) < self.length {
|
fn read_into_buffer<S, F>(
|
||||||
for n in 0..(self.format.channel_count as usize) {
|
sample_count: usize,
|
||||||
buffer[n] = match (self.format.bits_per_sample, framed_bits_per_sample) {
|
buffer: &mut [S],
|
||||||
(25..=32,32) => self.inner.read_f32::<LittleEndian>()?,
|
mut read_fn: F,
|
||||||
(b,_)=> panic!("Unrecognized integer format, bits per sample {}, channels {}, block_alignment {}",
|
) -> Result<(), Error>
|
||||||
b, self.format.channel_count, self.format.block_alignment)
|
where
|
||||||
}
|
F: FnMut() -> Result<S, Error>,
|
||||||
}
|
{
|
||||||
Ok(1)
|
for output in buffer.iter_mut().take(sample_count) {
|
||||||
} else {
|
*output = read_fn()?;
|
||||||
Ok(0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wave, Broadcast-WAV and RF64/BW64 parser/reader.
|
/// Wave, Broadcast-WAV and RF64/BW64 parser/reader.
|
||||||
@@ -173,9 +176,9 @@ impl<R: Read + Seek> AudioFrameReader<R> {
|
|||||||
/// assert_eq!(format.channel_count, 1);
|
/// assert_eq!(format.channel_count, 1);
|
||||||
///
|
///
|
||||||
/// let mut frame_reader = r.audio_frame_reader().unwrap();
|
/// let mut frame_reader = r.audio_frame_reader().unwrap();
|
||||||
/// let mut buffer = format.create_frame_buffer(1);
|
/// let mut buffer = format.create_frame_buffer::<i32>(1);
|
||||||
///
|
///
|
||||||
/// let read = frame_reader.read_integer_frame(&mut buffer).unwrap();
|
/// let read = frame_reader.read_frames(&mut buffer).unwrap();
|
||||||
///
|
///
|
||||||
/// assert_eq!(buffer, [0i32]);
|
/// assert_eq!(buffer, [0i32]);
|
||||||
/// assert_eq!(read, 1);
|
/// assert_eq!(read, 1);
|
||||||
@@ -216,7 +219,7 @@ impl WaveReader<BufReader<File>> {
|
|||||||
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, ParserError> {
|
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, ParserError> {
|
||||||
let f = File::open(path)?;
|
let f = File::open(path)?;
|
||||||
let inner = BufReader::new(f);
|
let inner = BufReader::new(f);
|
||||||
Ok(Self::new(inner)?)
|
Self::new(inner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,10 +227,9 @@ impl WaveReader<File> {
|
|||||||
/// Open a file for reading with unbuffered IO.
|
/// Open a file for reading with unbuffered IO.
|
||||||
///
|
///
|
||||||
/// A convenience that opens `path` and calls `Self::new()`
|
/// A convenience that opens `path` and calls `Self::new()`
|
||||||
|
|
||||||
pub fn open_unbuffered<P: AsRef<Path>>(path: P) -> Result<Self, ParserError> {
|
pub fn open_unbuffered<P: AsRef<Path>>(path: P) -> Result<Self, ParserError> {
|
||||||
let inner = File::open(path)?;
|
let inner = File::open(path)?;
|
||||||
return Ok(Self::new(inner)?);
|
Self::new(inner)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +272,7 @@ impl<R: Read + Seek> WaveReader<R> {
|
|||||||
|
|
||||||
/// Unwrap the inner reader.
|
/// Unwrap the inner reader.
|
||||||
pub fn into_inner(self) -> R {
|
pub fn into_inner(self) -> R {
|
||||||
return self.inner;
|
self.inner
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
@@ -279,12 +281,12 @@ impl<R: Read + Seek> WaveReader<R> {
|
|||||||
pub fn audio_frame_reader(mut self) -> Result<AudioFrameReader<R>, ParserError> {
|
pub fn audio_frame_reader(mut self) -> Result<AudioFrameReader<R>, ParserError> {
|
||||||
let format = self.format()?;
|
let format = self.format()?;
|
||||||
let audio_chunk_reader = self.get_chunk_extent_at_index(DATA_SIG, 0)?;
|
let audio_chunk_reader = self.get_chunk_extent_at_index(DATA_SIG, 0)?;
|
||||||
Ok(AudioFrameReader::new(
|
AudioFrameReader::new(
|
||||||
self.inner,
|
self.inner,
|
||||||
format,
|
format,
|
||||||
audio_chunk_reader.0,
|
audio_chunk_reader.0,
|
||||||
audio_chunk_reader.1,
|
audio_chunk_reader.1,
|
||||||
)?)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The count of audio frames in the file.
|
/// The count of audio frames in the file.
|
||||||
@@ -580,18 +582,16 @@ impl<R: Read + Seek> WaveReader<R> {
|
|||||||
&mut self,
|
&mut self,
|
||||||
ident: FourCC,
|
ident: FourCC,
|
||||||
at: u32,
|
at: u32,
|
||||||
mut buffer: &mut Vec<u8>,
|
buffer: &mut Vec<u8>,
|
||||||
) -> Result<usize, ParserError> {
|
) -> Result<usize, ParserError> {
|
||||||
match self.get_chunk_extent_at_index(ident, at) {
|
match self.get_chunk_extent_at_index(ident, at) {
|
||||||
Ok((start, length)) => {
|
Ok((start, length)) => {
|
||||||
buffer.resize(length as usize, 0x0);
|
buffer.resize(length as usize, 0x0);
|
||||||
self.inner.seek(SeekFrom::Start(start))?;
|
self.inner.seek(SeekFrom::Start(start))?;
|
||||||
self.inner
|
self.inner.read(buffer).map_err(ParserError::IOError)
|
||||||
.read(&mut buffer)
|
|
||||||
.map_err(|e| ParserError::IOError(e))
|
|
||||||
}
|
}
|
||||||
Err(ParserError::ChunkMissing { signature: _ }) => Ok(0),
|
Err(ParserError::ChunkMissing { signature: _ }) => Ok(0),
|
||||||
Err(any) => Err(any.into()),
|
Err(any) => Err(any),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -608,7 +608,7 @@ impl<R: Read + Seek> WaveReader<R> {
|
|||||||
/// Index of first LIST for with the given FORM fourcc
|
/// Index of first LIST for with the given FORM fourcc
|
||||||
fn get_list_form(&mut self, fourcc: FourCC) -> Result<Option<u32>, ParserError> {
|
fn get_list_form(&mut self, fourcc: FourCC) -> Result<Option<u32>, ParserError> {
|
||||||
for (n, (start, _)) in self.get_chunks_extents(LIST_SIG)?.iter().enumerate() {
|
for (n, (start, _)) in self.get_chunks_extents(LIST_SIG)?.iter().enumerate() {
|
||||||
self.inner.seek(SeekFrom::Start(*start as u64))?;
|
self.inner.seek(SeekFrom::Start(*start))?;
|
||||||
let this_fourcc = self.inner.read_fourcc()?;
|
let this_fourcc = self.inner.read_fourcc()?;
|
||||||
if this_fourcc == fourcc {
|
if this_fourcc == fourcc {
|
||||||
return Ok(Some(n as u32));
|
return Ok(Some(n as u32));
|
||||||
@@ -623,7 +623,7 @@ impl<R: Read + Seek> WaveReader<R> {
|
|||||||
fourcc: FourCC,
|
fourcc: FourCC,
|
||||||
index: u32,
|
index: u32,
|
||||||
) -> Result<(u64, u64), ParserError> {
|
) -> Result<(u64, u64), ParserError> {
|
||||||
if let Some((start, length)) = self.get_chunks_extents(fourcc)?.iter().nth(index as usize) {
|
if let Some((start, length)) = self.get_chunks_extents(fourcc)?.get(index as usize) {
|
||||||
Ok((*start, *length))
|
Ok((*start, *length))
|
||||||
} else {
|
} else {
|
||||||
Err(ParserError::ChunkMissing { signature: fourcc })
|
Err(ParserError::ChunkMissing { signature: fourcc })
|
||||||
|
|||||||
@@ -2,12 +2,14 @@ use std::fs::File;
|
|||||||
use std::io::{BufWriter, Cursor, Seek, SeekFrom, Write};
|
use std::io::{BufWriter, Cursor, Seek, SeekFrom, Write};
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
|
use crate::CommonFormat;
|
||||||
|
|
||||||
use super::fmt::WaveFmt;
|
use super::fmt::WaveFmt;
|
||||||
use super::fourcc::{
|
use super::fourcc::{
|
||||||
FourCC, WriteFourCC, AXML_SIG, BEXT_SIG, DATA_SIG, DS64_SIG, ELM1_SIG, FMT__SIG, IXML_SIG,
|
FourCC, WriteFourCC, AXML_SIG, BEXT_SIG, DATA_SIG, DS64_SIG, ELM1_SIG, FMT__SIG, IXML_SIG,
|
||||||
JUNK_SIG, RF64_SIG, RIFF_SIG, WAVE_SIG,
|
JUNK_SIG, RF64_SIG, RIFF_SIG, WAVE_SIG,
|
||||||
};
|
};
|
||||||
use super::Error;
|
use super::{Error, Sample, I24};
|
||||||
//use super::common_format::CommonFormat;
|
//use super::common_format::CommonFormat;
|
||||||
use super::bext::Bext;
|
use super::bext::Bext;
|
||||||
use super::chunks::WriteBWaveChunks;
|
use super::chunks::WriteBWaveChunks;
|
||||||
@@ -23,6 +25,7 @@ where
|
|||||||
W: Write + Seek,
|
W: Write + Seek,
|
||||||
{
|
{
|
||||||
inner: WaveChunkWriter<W>,
|
inner: WaveChunkWriter<W>,
|
||||||
|
write_buffer: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> AudioFrameWriter<W>
|
impl<W> AudioFrameWriter<W>
|
||||||
@@ -30,36 +33,74 @@ where
|
|||||||
W: Write + Seek,
|
W: Write + Seek,
|
||||||
{
|
{
|
||||||
fn new(inner: WaveChunkWriter<W>) -> Self {
|
fn new(inner: WaveChunkWriter<W>) -> Self {
|
||||||
AudioFrameWriter { inner }
|
AudioFrameWriter {
|
||||||
|
inner,
|
||||||
|
write_buffer: Vec::new(),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_integer_frames_to_buffer(&self, from_frames: &[i32], to_buffer: &mut [u8]) -> () {
|
|
||||||
assert!(
|
|
||||||
from_frames.len() % self.inner.inner.format.channel_count as usize == 0,
|
|
||||||
"frames buffer does not contain a number of samples % channel_count == 0"
|
|
||||||
);
|
|
||||||
self.inner.inner.format.pack_frames(&from_frames, to_buffer);
|
|
||||||
()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write interleaved samples in `buffer`
|
/// Write interleaved samples in `buffer`
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// The writer will convert from the buffer's sample type into the file's sample type.
|
||||||
///
|
/// Note that no dithering will be applied during sample type conversion,
|
||||||
/// This function will panic if `buffer.len()` modulo the Wave file's channel count
|
/// if dithering is required then it will need to be applied manually.
|
||||||
/// is not zero.
|
pub fn write_frames<S>(&mut self, buffer: &[S]) -> Result<(), Error>
|
||||||
pub fn write_integer_frames(&mut self, buffer: &[i32]) -> Result<u64, Error> {
|
where
|
||||||
let mut write_buffer = self
|
S: Sample,
|
||||||
.inner
|
{
|
||||||
.inner
|
let format = &self.inner.inner.format;
|
||||||
.format
|
let channel_count = format.channel_count as usize;
|
||||||
.create_raw_buffer(buffer.len() / self.inner.inner.format.channel_count as usize);
|
|
||||||
|
|
||||||
self.write_integer_frames_to_buffer(&buffer, &mut write_buffer);
|
if buffer.len() % channel_count != 0 {
|
||||||
|
return Err(Error::InvalidBufferSize {
|
||||||
|
buffer_size: buffer.len(),
|
||||||
|
channel_count: format.channel_count,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
self.inner.write(&write_buffer)?;
|
let frame_count = buffer.len() / channel_count;
|
||||||
self.inner.flush()?;
|
let write_buffer_size = format.block_alignment as usize * frame_count;
|
||||||
Ok(write_buffer.len() as u64 / self.inner.inner.format.channel_count as u64)
|
self.write_buffer.resize(write_buffer_size, 0);
|
||||||
|
|
||||||
|
let mut write_cursor = Cursor::new(&mut self.write_buffer);
|
||||||
|
|
||||||
|
let common_format = format.common_format();
|
||||||
|
let bits_per_sample = format.bits_per_sample;
|
||||||
|
|
||||||
|
match (common_format, bits_per_sample) {
|
||||||
|
(_, 8) => {
|
||||||
|
for sample in buffer {
|
||||||
|
write_cursor.write_u8(sample.to_sample())?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(_, 16) => {
|
||||||
|
for sample in buffer {
|
||||||
|
write_cursor.write_i16::<LittleEndian>(sample.to_sample())?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(_, 24) => {
|
||||||
|
for sample in buffer {
|
||||||
|
write_cursor.write_i24::<LittleEndian>(sample.to_sample::<I24>().inner())?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(CommonFormat::IntegerPCM, 32) => {
|
||||||
|
for sample in buffer {
|
||||||
|
write_cursor.write_i32::<LittleEndian>(sample.to_sample())?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(CommonFormat::IeeeFloatPCM, 32) => {
|
||||||
|
for sample in buffer {
|
||||||
|
write_cursor.write_f32::<LittleEndian>(sample.to_sample())?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(_, _) => panic!(
|
||||||
|
"Unrecognized format, bits per sample {}, channels {}, sample format {:?}",
|
||||||
|
bits_per_sample, channel_count, common_format
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
self.inner.write_all(&self.write_buffer)?;
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finish writing audio frames and unwrap the inner `WaveWriter`.
|
/// Finish writing audio frames and unwrap the inner `WaveWriter`.
|
||||||
@@ -110,14 +151,14 @@ where
|
|||||||
fn end(mut self) -> Result<WaveWriter<W>, Error> {
|
fn end(mut self) -> Result<WaveWriter<W>, Error> {
|
||||||
if self.length % 2 == 1 {
|
if self.length % 2 == 1 {
|
||||||
self.inner.inner.seek(SeekFrom::End(0))?;
|
self.inner.inner.seek(SeekFrom::End(0))?;
|
||||||
self.inner.inner.write(&[0u8])?;
|
self.inner.inner.write_u8(0)?;
|
||||||
self.inner.increment_form_length(1)?;
|
self.inner.increment_form_length(1)?;
|
||||||
}
|
}
|
||||||
Ok(self.inner)
|
Ok(self.inner)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn increment_chunk_length(&mut self, amount: u64) -> Result<(), std::io::Error> {
|
fn increment_chunk_length(&mut self, amount: u64) -> Result<(), std::io::Error> {
|
||||||
self.length = self.length + amount;
|
self.length += amount;
|
||||||
if !self.inner.is_rf64 {
|
if !self.inner.is_rf64 {
|
||||||
self.inner
|
self.inner
|
||||||
.inner
|
.inner
|
||||||
@@ -125,8 +166,7 @@ where
|
|||||||
self.inner
|
self.inner
|
||||||
.inner
|
.inner
|
||||||
.write_u32::<LittleEndian>(self.length as u32)?;
|
.write_u32::<LittleEndian>(self.length as u32)?;
|
||||||
} else {
|
} else if self.ident == DATA_SIG {
|
||||||
if self.ident == DATA_SIG {
|
|
||||||
let data_chunk_64bit_field_offset = 8 + 4 + 8 + 8;
|
let data_chunk_64bit_field_offset = 8 + 4 + 8 + 8;
|
||||||
self.inner
|
self.inner
|
||||||
.inner
|
.inner
|
||||||
@@ -141,7 +181,6 @@ where
|
|||||||
} else {
|
} else {
|
||||||
todo!("FIXME RF64 wave writing is not yet supported for chunks other than `data`")
|
todo!("FIXME RF64 wave writing is not yet supported for chunks other than `data`")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -198,9 +237,9 @@ where
|
|||||||
///
|
///
|
||||||
/// let mut frame_writer = w.audio_frame_writer().unwrap();
|
/// let mut frame_writer = w.audio_frame_writer().unwrap();
|
||||||
///
|
///
|
||||||
/// frame_writer.write_integer_frames(&[0i32]).unwrap();
|
/// frame_writer.write_frames(&[0i32]).unwrap();
|
||||||
/// frame_writer.write_integer_frames(&[0i32]).unwrap();
|
/// frame_writer.write_frames(&[0i32]).unwrap();
|
||||||
/// frame_writer.write_integer_frames(&[0i32]).unwrap();
|
/// frame_writer.write_frames(&[0i32]).unwrap();
|
||||||
/// frame_writer.end().unwrap();
|
/// frame_writer.end().unwrap();
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
@@ -249,7 +288,7 @@ impl WaveWriter<BufWriter<File>> {
|
|||||||
pub fn create<P: AsRef<Path>>(path: P, format: WaveFmt) -> Result<Self, Error> {
|
pub fn create<P: AsRef<Path>>(path: P, format: WaveFmt) -> Result<Self, Error> {
|
||||||
let f = File::create(path)?;
|
let f = File::create(path)?;
|
||||||
let b = BufWriter::new(f);
|
let b = BufWriter::new(f);
|
||||||
Ok(Self::new(b, format)?)
|
Self::new(b, format)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +296,7 @@ impl WaveWriter<File> {
|
|||||||
/// Creare a new Wave file with unbuffered IO at `path`
|
/// Creare a new Wave file with unbuffered IO at `path`
|
||||||
pub fn create_unbuffered<P: AsRef<Path>>(path: P, format: WaveFmt) -> Result<Self, Error> {
|
pub fn create_unbuffered<P: AsRef<Path>>(path: P, format: WaveFmt) -> Result<Self, Error> {
|
||||||
let f = File::create(path)?;
|
let f = File::create(path)?;
|
||||||
Ok(Self::new(f, format)?)
|
Self::new(f, format)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,11 +338,11 @@ where
|
|||||||
self.inner.write_fourcc(ident)?;
|
self.inner.write_fourcc(ident)?;
|
||||||
assert!(data.len() < u32::MAX as usize);
|
assert!(data.len() < u32::MAX as usize);
|
||||||
self.inner.write_u32::<LittleEndian>(data.len() as u32)?;
|
self.inner.write_u32::<LittleEndian>(data.len() as u32)?;
|
||||||
self.inner.write(data)?;
|
self.inner.write_all(data)?;
|
||||||
if data.len() % 2 == 0 {
|
if data.len() % 2 == 0 {
|
||||||
self.increment_form_length(8 + data.len() as u64)?;
|
self.increment_form_length(8 + data.len() as u64)?;
|
||||||
} else {
|
} else {
|
||||||
self.inner.write(&[0u8])?;
|
self.inner.write_u8(0)?;
|
||||||
self.increment_form_length(8 + data.len() as u64 + 1)?;
|
self.increment_form_length(8 + data.len() as u64 + 1)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -317,7 +356,7 @@ where
|
|||||||
pub fn write_broadcast_metadata(&mut self, bext: &Bext) -> Result<(), Error> {
|
pub fn write_broadcast_metadata(&mut self, bext: &Bext) -> Result<(), Error> {
|
||||||
//FIXME Implement re-writing
|
//FIXME Implement re-writing
|
||||||
let mut c = Cursor::new(vec![0u8; 0]);
|
let mut c = Cursor::new(vec![0u8; 0]);
|
||||||
c.write_bext(&bext)?;
|
c.write_bext(bext)?;
|
||||||
let buf = c.into_inner();
|
let buf = c.into_inner();
|
||||||
self.write_chunk(BEXT_SIG, &buf)?;
|
self.write_chunk(BEXT_SIG, &buf)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -326,13 +365,13 @@ where
|
|||||||
/// Write iXML metadata
|
/// Write iXML metadata
|
||||||
pub fn write_ixml(&mut self, ixml: &[u8]) -> Result<(), Error> {
|
pub fn write_ixml(&mut self, ixml: &[u8]) -> Result<(), Error> {
|
||||||
//FIXME Implement re-writing
|
//FIXME Implement re-writing
|
||||||
self.write_chunk(IXML_SIG, &ixml)
|
self.write_chunk(IXML_SIG, ixml)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write axml/ADM metadata
|
/// Write axml/ADM metadata
|
||||||
pub fn write_axml(&mut self, axml: &[u8]) -> Result<(), Error> {
|
pub fn write_axml(&mut self, axml: &[u8]) -> Result<(), Error> {
|
||||||
//FIXME Implement re-writing
|
//FIXME Implement re-writing
|
||||||
self.write_chunk(AXML_SIG, &axml)
|
self.write_chunk(AXML_SIG, axml)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write a `JUNK` filler chunk
|
/// Write a `JUNK` filler chunk
|
||||||
@@ -353,7 +392,7 @@ where
|
|||||||
let to_add = framing - (lip % framing) - 16;
|
let to_add = framing - (lip % framing) - 16;
|
||||||
let mut chunk = self.chunk(ELM1_SIG)?;
|
let mut chunk = self.chunk(ELM1_SIG)?;
|
||||||
let buf = vec![0u8; to_add as usize];
|
let buf = vec![0u8; to_add as usize];
|
||||||
chunk.write(&buf)?;
|
chunk.write_all(&buf)?;
|
||||||
let closed = chunk.end()?;
|
let closed = chunk.end()?;
|
||||||
let inner = closed.chunk(DATA_SIG)?;
|
let inner = closed.chunk(DATA_SIG)?;
|
||||||
Ok(AudioFrameWriter::new(inner))
|
Ok(AudioFrameWriter::new(inner))
|
||||||
@@ -383,7 +422,7 @@ where
|
|||||||
|
|
||||||
/// Add `amount` to the RIFF/RF64 form length
|
/// Add `amount` to the RIFF/RF64 form length
|
||||||
fn increment_form_length(&mut self, amount: u64) -> Result<(), std::io::Error> {
|
fn increment_form_length(&mut self, amount: u64) -> Result<(), std::io::Error> {
|
||||||
self.form_length = self.form_length + amount;
|
self.form_length += amount;
|
||||||
if self.is_rf64 {
|
if self.is_rf64 {
|
||||||
self.inner.seek(SeekFrom::Start(8 + 4 + 8))?;
|
self.inner.seek(SeekFrom::Start(8 + 4 + 8))?;
|
||||||
self.inner.write_u64::<LittleEndian>(self.form_length)?;
|
self.inner.write_u64::<LittleEndian>(self.form_length)?;
|
||||||
@@ -436,9 +475,9 @@ fn test_write_audio() {
|
|||||||
|
|
||||||
let mut frame_writer = w.audio_frame_writer().unwrap();
|
let mut frame_writer = w.audio_frame_writer().unwrap();
|
||||||
|
|
||||||
frame_writer.write_integer_frames(&[0i32]).unwrap();
|
frame_writer.write_frames(&[0i32]).unwrap();
|
||||||
frame_writer.write_integer_frames(&[0i32]).unwrap();
|
frame_writer.write_frames(&[0i32]).unwrap();
|
||||||
frame_writer.write_integer_frames(&[0i32]).unwrap();
|
frame_writer.write_frames(&[0i32]).unwrap();
|
||||||
|
|
||||||
frame_writer.end().unwrap();
|
frame_writer.end().unwrap();
|
||||||
|
|
||||||
@@ -504,14 +543,14 @@ fn test_write_bext() {
|
|||||||
|
|
||||||
let mut frame_writer = w.audio_frame_writer().unwrap();
|
let mut frame_writer = w.audio_frame_writer().unwrap();
|
||||||
|
|
||||||
frame_writer.write_integer_frames(&[0i32]).unwrap();
|
frame_writer.write_frames(&[0i32]).unwrap();
|
||||||
frame_writer.write_integer_frames(&[0i32]).unwrap();
|
frame_writer.write_frames(&[0i32]).unwrap();
|
||||||
frame_writer.write_integer_frames(&[0i32]).unwrap();
|
frame_writer.write_frames(&[0i32]).unwrap();
|
||||||
|
|
||||||
frame_writer.end().unwrap();
|
frame_writer.end().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE! This test of RF64 writing takes several minutes to complete.
|
// NOTE! This test of RF64 writing takes several minutes to complete in debug builds
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_rf64() {
|
fn test_create_rf64() {
|
||||||
use super::fourcc::ReadFourCC;
|
use super::fourcc::ReadFourCC;
|
||||||
@@ -530,7 +569,7 @@ fn test_create_rf64() {
|
|||||||
let mut af = w.audio_frame_writer().unwrap();
|
let mut af = w.audio_frame_writer().unwrap();
|
||||||
|
|
||||||
for _ in 0..(four_and_a_half_hours_of_frames * format.channel_count as u64 / buflen) {
|
for _ in 0..(four_and_a_half_hours_of_frames * format.channel_count as u64 / buflen) {
|
||||||
af.write_integer_frames(&buf).unwrap();
|
af.write_frames(&buf).unwrap();
|
||||||
}
|
}
|
||||||
af.end().unwrap();
|
af.end().unwrap();
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ extern crate bwavfile;
|
|||||||
use bwavfile::ChannelMask;
|
use bwavfile::ChannelMask;
|
||||||
use bwavfile::Error;
|
use bwavfile::Error;
|
||||||
use bwavfile::WaveReader;
|
use bwavfile::WaveReader;
|
||||||
|
use bwavfile::I24;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_open() {
|
fn test_open() {
|
||||||
@@ -80,16 +81,16 @@ fn test_read() {
|
|||||||
let path = "tests/media/audacity_16bit.wav";
|
let path = "tests/media/audacity_16bit.wav";
|
||||||
|
|
||||||
let mut w = WaveReader::open(path).expect("Failure opening test file");
|
let mut w = WaveReader::open(path).expect("Failure opening test file");
|
||||||
let mut buffer = w.format().unwrap().create_frame_buffer(1);
|
let mut buffer = w.format().unwrap().create_frame_buffer::<i16>(1);
|
||||||
|
|
||||||
let mut reader = w.audio_frame_reader().unwrap();
|
let mut reader = w.audio_frame_reader().unwrap();
|
||||||
|
|
||||||
assert_eq!(reader.read_integer_frame(&mut buffer).unwrap(), 1);
|
assert_eq!(reader.read_frames(&mut buffer).unwrap(), 1);
|
||||||
assert_eq!(buffer[0], -2823_i32);
|
assert_eq!(buffer[0], -2823_i16);
|
||||||
assert_eq!(reader.read_integer_frame(&mut buffer).unwrap(), 1);
|
assert_eq!(reader.read_frames(&mut buffer).unwrap(), 1);
|
||||||
assert_eq!(buffer[0], 2012_i32);
|
assert_eq!(buffer[0], 2012_i16);
|
||||||
assert_eq!(reader.read_integer_frame(&mut buffer).unwrap(), 1);
|
assert_eq!(reader.read_frames(&mut buffer).unwrap(), 1);
|
||||||
assert_eq!(buffer[0], 4524_i32);
|
assert_eq!(buffer[0], 4524_i16);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -97,21 +98,21 @@ fn test_locate_multichannel_read() {
|
|||||||
let path = "tests/media/ff_pink.wav";
|
let path = "tests/media/ff_pink.wav";
|
||||||
|
|
||||||
let mut w = WaveReader::open(path).expect("Failure opening test file");
|
let mut w = WaveReader::open(path).expect("Failure opening test file");
|
||||||
let mut buffer = w.format().unwrap().create_frame_buffer(1);
|
let mut buffer = w.format().unwrap().create_frame_buffer::<I24>(1);
|
||||||
|
|
||||||
let mut reader = w.audio_frame_reader().unwrap();
|
let mut reader = w.audio_frame_reader().unwrap();
|
||||||
|
|
||||||
assert_eq!(reader.read_integer_frame(&mut buffer).unwrap(), 1);
|
assert_eq!(reader.read_frames(&mut buffer).unwrap(), 1);
|
||||||
assert_eq!(buffer[0], 332702_i32);
|
assert_eq!(buffer[0], I24::from(332702));
|
||||||
assert_eq!(buffer[1], 3258791_i32);
|
assert_eq!(buffer[1], I24::from(3258791));
|
||||||
assert_eq!(reader.read_integer_frame(&mut buffer).unwrap(), 1);
|
assert_eq!(reader.read_frames(&mut buffer).unwrap(), 1);
|
||||||
assert_eq!(buffer[0], -258742_i32); // 0x800000 = 8388608 // 8129866 - 8388608
|
assert_eq!(buffer[0], I24::from(-258742)); // 0x800000 = 8388608 // 8129866 - 8388608
|
||||||
assert_eq!(buffer[1], 0x0D7EF9_i32);
|
assert_eq!(buffer[1], I24::from(0x0D7EF9));
|
||||||
|
|
||||||
assert_eq!(reader.locate(100).unwrap(), 100);
|
assert_eq!(reader.locate(100).unwrap(), 100);
|
||||||
assert_eq!(reader.read_integer_frame(&mut buffer).unwrap(), 1);
|
assert_eq!(reader.read_frames(&mut buffer).unwrap(), 1);
|
||||||
assert_eq!(buffer[0], 0x109422_i32);
|
assert_eq!(buffer[0], I24::from(0x109422));
|
||||||
assert_eq!(buffer[1], -698901_i32); // 7689707 - 8388608
|
assert_eq!(buffer[1], I24::from(-698901)); // 7689707 - 8388608
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in New Issue
Block a user