mirror of
https://github.com/iluvcapra/bwavfile.git
synced 2025-12-31 17:00:44 +00:00
Writer impl
This commit is contained in:
@@ -102,7 +102,7 @@ pub const DATA_SIG: FourCC = FourCC::make(b"data");
|
|||||||
pub const FMT__SIG: FourCC = FourCC::make(b"fmt ");
|
pub const FMT__SIG: FourCC = FourCC::make(b"fmt ");
|
||||||
|
|
||||||
pub const BEXT_SIG: FourCC = FourCC::make(b"bext");
|
pub const BEXT_SIG: FourCC = FourCC::make(b"bext");
|
||||||
pub const FACT_SIG: FourCC = FourCC::make(b"fact");
|
//pub const FACT_SIG: FourCC = FourCC::make(b"fact");
|
||||||
|
|
||||||
pub const JUNK_SIG: FourCC = FourCC::make(b"JUNK");
|
pub const JUNK_SIG: FourCC = FourCC::make(b"JUNK");
|
||||||
pub const FLLR_SIG: FourCC = FourCC::make(b"FLLR");
|
pub const FLLR_SIG: FourCC = FourCC::make(b"FLLR");
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ use std::fs::File;
|
|||||||
use super::parser::Parser;
|
use super::parser::Parser;
|
||||||
use super::fourcc::{FourCC, ReadFourCC, FMT__SIG,DATA_SIG, BEXT_SIG, LIST_SIG, JUNK_SIG, FLLR_SIG, CUE__SIG, ADTL_SIG};
|
use super::fourcc::{FourCC, ReadFourCC, FMT__SIG,DATA_SIG, BEXT_SIG, LIST_SIG, JUNK_SIG, FLLR_SIG, CUE__SIG, ADTL_SIG};
|
||||||
use super::errors::Error as ParserError;
|
use super::errors::Error as ParserError;
|
||||||
//use super::raw_chunk_reader::RawChunkReader;
|
|
||||||
use super::fmt::{WaveFmt, ChannelDescriptor, ChannelMask};
|
use super::fmt::{WaveFmt, ChannelDescriptor, ChannelMask};
|
||||||
use super::bext::Bext;
|
use super::bext::Bext;
|
||||||
use super::audio_frame_reader::AudioFrameReader;
|
use super::audio_frame_reader::AudioFrameReader;
|
||||||
|
|||||||
@@ -2,15 +2,48 @@ use std::fs::File;
|
|||||||
use std::io::{Write,Seek,SeekFrom};
|
use std::io::{Write,Seek,SeekFrom};
|
||||||
|
|
||||||
use super::Error;
|
use super::Error;
|
||||||
use super::fourcc::{FourCC, WriteFourCC, RIFF_SIG, WAVE_SIG, FMT__SIG,FACT_SIG};
|
use super::fourcc::{FourCC, WriteFourCC, RIFF_SIG, WAVE_SIG, FMT__SIG, DATA_SIG, ELM1_SIG};
|
||||||
use super::fmt::WaveFmt;
|
use super::fmt::WaveFmt;
|
||||||
use super::common_format::CommonFormat;
|
//use super::common_format::CommonFormat;
|
||||||
use super::chunks::WriteBWaveChunks;
|
use super::chunks::WriteBWaveChunks;
|
||||||
|
|
||||||
use byteorder::LittleEndian;
|
use byteorder::LittleEndian;
|
||||||
use byteorder::WriteBytesExt;
|
use byteorder::WriteBytesExt;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct AudioFrameWriter<W> where W: Write + Seek {
|
||||||
|
inner : WaveChunkWriter<W>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<W> AudioFrameWriter<W> where W: Write + Seek {
|
||||||
|
pub fn write_integer_frame(&mut self, buffer: &[i32]) -> Result<u64,Error> {
|
||||||
|
let format = self.inner.inner.format;
|
||||||
|
assert!(buffer.len() as u16 == format.channel_count,
|
||||||
|
"read_integer_frame was called with a mis-sized buffer, expected {}, was {}",
|
||||||
|
format.channel_count, buffer.len());
|
||||||
|
|
||||||
|
let framed_bits_per_sample = format.block_alignment * 8 / format.channel_count;
|
||||||
|
|
||||||
|
for n in 0..(format.channel_count as usize) {
|
||||||
|
match (format.bits_per_sample, framed_bits_per_sample) {
|
||||||
|
(0..=8,8) => self.inner.write_u8((buffer[n] + 0x80) as u8 )?, // EBU 3285 §A2.2
|
||||||
|
(9..=16,16) => self.inner.write_i16::<LittleEndian>(buffer[n] as i16)?,
|
||||||
|
(10..=24,24) => self.inner.write_i24::<LittleEndian>(buffer[n])?,
|
||||||
|
(25..=32,32) => self.inner.write_i32::<LittleEndian>(buffer[n])?,
|
||||||
|
(b,_)=> panic!("Unrecognized integer format, bits per sample {}, channels {}, block_alignment {}",
|
||||||
|
b, format.channel_count, format.block_alignment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end(self) -> Result<WaveWriter<W>, Error> {
|
||||||
|
self.inner.end()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct WaveChunkWriter<W> where W: Write + Seek {
|
pub struct WaveChunkWriter<W> where W: Write + Seek {
|
||||||
inner : WaveWriter<W>,
|
inner : WaveWriter<W>,
|
||||||
content_start_pos : u64,
|
content_start_pos : u64,
|
||||||
@@ -28,8 +61,13 @@ impl<W> WaveChunkWriter<W> where W: Write + Seek {
|
|||||||
Ok( WaveChunkWriter { inner , content_start_pos, length } )
|
Ok( WaveChunkWriter { inner , content_start_pos, length } )
|
||||||
}
|
}
|
||||||
|
|
||||||
fn end(self) -> WaveWriter<W> {
|
fn end(mut self) -> Result<WaveWriter<W>, Error> {
|
||||||
self.inner
|
if self.length % 2 == 1 {
|
||||||
|
self.inner.inner.seek(SeekFrom::End(0))?;
|
||||||
|
self.inner.inner.write(&[0u8])?;
|
||||||
|
self.inner.increment_form_length(1)?;
|
||||||
|
}
|
||||||
|
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> {
|
||||||
@@ -98,16 +136,9 @@ impl<W> WaveWriter<W> where W: Write + Seek {
|
|||||||
|
|
||||||
let mut chunk = retval.begin_chunk(FMT__SIG)?;
|
let mut chunk = retval.begin_chunk(FMT__SIG)?;
|
||||||
chunk.write_wave_fmt(&format)?;
|
chunk.write_wave_fmt(&format)?;
|
||||||
let retval = chunk.end();
|
let retval = chunk.end()?;
|
||||||
|
|
||||||
if format.common_format() != CommonFormat::IntegerPCM {
|
Ok( retval )
|
||||||
let mut chunk = retval.begin_chunk(FACT_SIG)?;
|
|
||||||
chunk.write_u32::<LittleEndian>(0)?;
|
|
||||||
let retval = chunk.end();
|
|
||||||
Ok( retval )
|
|
||||||
} else {
|
|
||||||
Ok( retval )
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a new chunk writer, which takes posession of the `WaveWriter`.
|
/// Create a new chunk writer, which takes posession of the `WaveWriter`.
|
||||||
@@ -119,12 +150,29 @@ impl<W> WaveWriter<W> where W: Write + Seek {
|
|||||||
WaveChunkWriter::begin(self, ident)
|
WaveChunkWriter::begin(self, ident)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn audio_frame_writer(mut self) -> Result<AudioFrameWriter<W>, Error> {
|
||||||
|
// append elm1 chunk
|
||||||
|
|
||||||
|
let framing = 0x4000;
|
||||||
|
|
||||||
|
let lip = self.inner.seek(SeekFrom::End(0))?;
|
||||||
|
let to_add = framing - (lip % framing) - 16;
|
||||||
|
let mut chunk = self.begin_chunk(ELM1_SIG)?;
|
||||||
|
let buf = vec![0u8; to_add as usize];
|
||||||
|
chunk.write(&buf)?;
|
||||||
|
let closed = chunk.end()?;
|
||||||
|
|
||||||
|
let inner = closed.begin_chunk(DATA_SIG)?;
|
||||||
|
Ok( AudioFrameWriter { inner } )
|
||||||
|
}
|
||||||
|
|
||||||
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 = self.form_length + amount;
|
||||||
self.inner.seek(SeekFrom::Start(4))?;
|
self.inner.seek(SeekFrom::Start(4))?;
|
||||||
self.inner.write_u32::<LittleEndian>(self.form_length as u32)?;
|
self.inner.write_u32::<LittleEndian>(self.form_length as u32)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -144,6 +192,46 @@ fn test_new() {
|
|||||||
assert_eq!(cursor.read_fourcc().unwrap(), FMT__SIG);
|
assert_eq!(cursor.read_fourcc().unwrap(), FMT__SIG);
|
||||||
let fmt_size = cursor.read_u32::<LittleEndian>().unwrap();
|
let fmt_size = cursor.read_u32::<LittleEndian>().unwrap();
|
||||||
assert_eq!(form_size, fmt_size + 8 + 4);
|
assert_eq!(form_size, fmt_size + 8 + 4);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_write_audio() {
|
||||||
|
use std::io::Cursor;
|
||||||
|
use super::fourcc::ReadFourCC;
|
||||||
|
use byteorder::ReadBytesExt;
|
||||||
|
|
||||||
|
let mut cursor = Cursor::new(vec![0u8;0]);
|
||||||
|
let format = WaveFmt::new_pcm(48000, 24, 1);
|
||||||
|
let w = WaveWriter::new(&mut cursor, format).unwrap();
|
||||||
|
|
||||||
|
let mut frame_writer = w.audio_frame_writer().unwrap();
|
||||||
|
|
||||||
|
frame_writer.write_integer_frame(&[0i32]).unwrap();
|
||||||
|
frame_writer.write_integer_frame(&[0i32]).unwrap();
|
||||||
|
frame_writer.write_integer_frame(&[0i32]).unwrap();
|
||||||
|
|
||||||
|
frame_writer.end().unwrap();
|
||||||
|
|
||||||
|
cursor.seek(SeekFrom::Start(0)).unwrap();
|
||||||
|
|
||||||
|
cursor.seek(SeekFrom::Start(0)).unwrap();
|
||||||
|
assert_eq!(cursor.read_fourcc().unwrap(), RIFF_SIG);
|
||||||
|
let _ = cursor.read_u32::<LittleEndian>().unwrap();
|
||||||
|
assert_eq!(cursor.read_fourcc().unwrap(), WAVE_SIG);
|
||||||
|
assert_eq!(cursor.read_fourcc().unwrap(), FMT__SIG);
|
||||||
|
let seek = cursor.read_u32::<LittleEndian>().unwrap();
|
||||||
|
cursor.seek(SeekFrom::Current(seek as i64)).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(cursor.read_fourcc().unwrap(), ELM1_SIG);
|
||||||
|
let seek = cursor.read_u32::<LittleEndian>().unwrap();
|
||||||
|
cursor.seek(SeekFrom::Current(seek as i64)).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(cursor.read_fourcc().unwrap(), DATA_SIG);
|
||||||
|
let data_size = cursor.read_u32::<LittleEndian>().unwrap();
|
||||||
|
assert_eq!(data_size, 9);
|
||||||
|
|
||||||
|
let tell = cursor.seek(SeekFrom::Current(0)).unwrap();
|
||||||
|
assert!(tell % 0x4000 == 0);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user