blob: 585206ad2c2101f83d72a8bed89b16969c725243 [file] [log] [blame] [edit]
// Hound -- A wav encoding and decoding library in Rust
// Copyright (C) 2015 Ruud van Asseldonk
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// A copy of the License has been included in the root of the repository.
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::fs;
use std::io;
use std::mem;
use std::io::{Seek, Write};
use std::mem::MaybeUninit;
use std::path;
use super::{Error, Result, Sample, SampleFormat, WavSpec, WavSpecEx};
use ::read;
/// Extends the functionality of `io::Write` with additional methods.
///
/// The methods may be used on any type that implements `io::Write`.
pub trait WriteExt: io::Write {
/// Writes an unsigned 8-bit integer.
fn write_u8(&mut self, x: u8) -> io::Result<()>;
/// Writes a signed 16-bit integer in little endian format.
fn write_le_i16(&mut self, x: i16) -> io::Result<()>;
/// Writes an unsigned 16-bit integer in little endian format.
fn write_le_u16(&mut self, x: u16) -> io::Result<()>;
/// Writes a signed 24-bit integer in little endian format.
///
/// The most significant byte of the `i32` is ignored.
fn write_le_i24(&mut self, x: i32) -> io::Result<()>;
/// Writes a signed 24-bit integer in 4-byte little endian format.
///
/// The most significant byte of the `i32` is replaced with zeroes.
fn write_le_i24_4(&mut self, x: i32) -> io::Result<()>;
/// Writes an unsigned 24-bit integer in little endian format.
///
/// The most significant byte of the `u32` is ignored.
fn write_le_u24(&mut self, x: u32) -> io::Result<()>;
/// Writes a signed 32-bit integer in little endian format.
fn write_le_i32(&mut self, x: i32) -> io::Result<()>;
/// Writes an unsigned 32-bit integer in little endian format.
fn write_le_u32(&mut self, x: u32) -> io::Result<()>;
/// Writes an IEEE float in little endian format.
fn write_le_f32(&mut self, x: f32) -> io::Result<()>;
}
impl<W> WriteExt for W
where W: io::Write
{
#[inline(always)]
fn write_u8(&mut self, x: u8) -> io::Result<()> {
let buf = [x];
self.write_all(&buf)
}
#[inline(always)]
fn write_le_i16(&mut self, x: i16) -> io::Result<()> {
self.write_le_u16(x as u16)
}
#[inline(always)]
fn write_le_u16(&mut self, x: u16) -> io::Result<()> {
let mut buf = [0u8; 2];
buf[0] = (x & 0xff) as u8;
buf[1] = (x >> 8) as u8;
self.write_all(&buf)
}
#[inline(always)]
fn write_le_i24(&mut self, x: i32) -> io::Result<()> {
self.write_le_u24(x as u32)
}
#[inline(always)]
fn write_le_i24_4(&mut self, x: i32) -> io::Result<()> {
self.write_le_u32((x as u32) & 0x00_ff_ff_ff)
}
#[inline(always)]
fn write_le_u24(&mut self, x: u32) -> io::Result<()> {
let mut buf = [0u8; 3];
buf[0] = ((x >> 00) & 0xff) as u8;
buf[1] = ((x >> 08) & 0xff) as u8;
buf[2] = ((x >> 16) & 0xff) as u8;
self.write_all(&buf)
}
#[inline(always)]
fn write_le_i32(&mut self, x: i32) -> io::Result<()> {
self.write_le_u32(x as u32)
}
#[inline(always)]
fn write_le_u32(&mut self, x: u32) -> io::Result<()> {
let mut buf = [0u8; 4];
buf[0] = ((x >> 00) & 0xff) as u8;
buf[1] = ((x >> 08) & 0xff) as u8;
buf[2] = ((x >> 16) & 0xff) as u8;
buf[3] = ((x >> 24) & 0xff) as u8;
self.write_all(&buf)
}
#[inline(always)]
fn write_le_f32(&mut self, x: f32) -> io::Result<()> {
let u = unsafe { mem::transmute::<f32, u32>(x) };
self.write_le_u32(u)
}
}
/// Generates a bitmask with `channels` ones in the least significant bits.
///
/// According to the [spec](https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/ksmedia/ns-ksmedia-waveformatextensible#remarks),
/// if `channels` is greater than the number of bits in the channel mask, 18 non-reserved bits,
/// extra channels are not assigned to any physical speaker location. In this scenario, this
/// function will return a filled channel mask.
fn channel_mask(channels: u16) -> u32 {
// Clamp to 0-18 to stay within reserved bits.
let channels = if channels > 18 { 18 } else { channels };
(0..channels as u32).map(|c| 1 << c).fold(0, |a, c| a | c)
}
#[test]
fn verify_channel_mask() {
assert_eq!(channel_mask(0), 0);
assert_eq!(channel_mask(1), 1);
assert_eq!(channel_mask(2), 3);
assert_eq!(channel_mask(3), 7);
assert_eq!(channel_mask(4), 0xF);
assert_eq!(channel_mask(8), 0xFF);
assert_eq!(channel_mask(16), 0xFFFF);
// expect channels >= 18 to yield the same mask
assert_eq!(channel_mask(18), 0x3FFFF);
assert_eq!(channel_mask(32), 0x3FFFF);
assert_eq!(channel_mask(64), 0x3FFFF);
assert_eq!(channel_mask(129), 0x3FFFF);
}
/// A writer that accepts samples and writes the WAVE format.
///
/// The writer needs a `WavSpec` or `WavSpecEx` that describes the audio
/// properties. Then samples can be written with `write_sample`. Channel data is
/// interleaved. The number of samples written must be a multiple of the number
/// of channels. After all samples have been written, the file must be
/// finalized. This can be done by calling `finalize`. If `finalize` is not
/// called, the file will be finalized upon drop. However, finalization may
/// fail, and without calling `finalize`, such a failure cannot be observed.
pub struct WavWriter<W>
where W: io::Write + io::Seek
{
/// Specifies properties of the audio data.
spec: WavSpec,
/// The (container) bytes per sample. This is the bit rate / 8 rounded up.
bytes_per_sample: u16,
/// The writer that will be written to.
writer: W,
/// The number of bytes written to the data section.
///
/// This is an `u32` because WAVE cannot accomodate more data.
data_bytes_written: u32,
/// Whether the header has been finalized.
finalized: bool,
/// The buffer for the sample writer, which is recycled throughout calls to
/// avoid allocating frequently.
sample_writer_buffer: Vec<MaybeUninit<u8>>,
/// The offset of the length field of the data chunk.
///
/// This field needs to be overwritten after all data has been written. To
/// support different size fmt chunks, and other chunks interspersed, the
/// offset is flexible.
data_len_offset: u32,
}
enum FmtKind {
PcmWaveFormat,
WaveFormatExtensible,
}
impl<W> WavWriter<W>
where W: io::Write + io::Seek
{
/// Creates a writer that writes the WAVE format to the underlying writer.
///
/// The underlying writer is assumed to be at offset 0. `WavWriter` employs
/// *no* buffering internally. It is recommended to wrap the writer in a
/// `BufWriter` to avoid too many `write` calls. The `create()` constructor
/// does this automatically.
///
/// This writes parts of the header immediately, hence a `Result` is
/// returned.
pub fn new(writer: W, spec: WavSpec) -> Result<WavWriter<W>> {
let spec_ex = WavSpecEx {
spec: spec,
bytes_per_sample: (spec.bits_per_sample + 7) / 8,
};
WavWriter::new_with_spec_ex(writer, spec_ex)
}
/// Creates a writer that writes the WAVE format to the underlying writer.
///
/// The underlying writer is assumed to be at offset 0. `WavWriter` employs
/// *no* buffering internally. It is recommended to wrap the writer in a
/// `BufWriter` to avoid too many `write` calls. The `create()` constructor
/// does this automatically.
///
/// This writes parts of the header immediately, hence a `Result` is
/// returned.
pub fn new_with_spec_ex(writer: W, spec_ex: WavSpecEx) -> Result<WavWriter<W>> {
let spec = spec_ex.spec;
// Write the older PCMWAVEFORMAT structure if possible, because it is
// more widely supported. For more than two channels or more than 16
// bits per sample, the newer WAVEFORMATEXTENSIBLE is required. See also
// https://msdn.microsoft.com/en-us/library/ms713497.aspx.
let fmt_kind = if spec.channels > 2 || spec.bits_per_sample > 16 {
FmtKind::WaveFormatExtensible
} else {
FmtKind::PcmWaveFormat
};
let mut writer = WavWriter {
spec: spec,
bytes_per_sample: spec_ex.bytes_per_sample,
writer: writer,
data_bytes_written: 0,
sample_writer_buffer: Vec::new(),
finalized: false,
data_len_offset: match fmt_kind {
FmtKind::WaveFormatExtensible => 64,
FmtKind::PcmWaveFormat => 40,
},
};
// Hound can only write those bit depths. If something else was
// requested, fail early, rather than writing a header but then failing
// at the first sample.
let supported = match spec.bits_per_sample {
8 => true,
16 => true,
24 => true,
32 => true,
_ => false,
};
if !supported {
return Err(Error::Unsupported)
}
// Write headers, up to the point where data should be written.
try!(writer.write_headers(fmt_kind));
Ok(writer)
}
/// Writes the RIFF WAVE header, fmt chunk, and data chunk header.
fn write_headers(&mut self, fmt_kind: FmtKind) -> io::Result<()> {
// Write to an in-memory buffer before writing to the underlying writer.
let mut header = [0u8; 68];
{
let mut buffer = io::Cursor::new(&mut header[..]);
// Write the headers for the RIFF WAVE format.
try!(buffer.write_all("RIFF".as_bytes()));
// Skip 4 bytes that will be filled with the file size afterwards.
try!(buffer.write_le_u32(0));
try!(buffer.write_all("WAVE".as_bytes()));
try!(buffer.write_all("fmt ".as_bytes()));
match fmt_kind {
FmtKind::PcmWaveFormat => {
try!(self.write_pcmwaveformat(&mut buffer));
}
FmtKind::WaveFormatExtensible => {
try!(self.write_waveformatextensible(&mut buffer));
}
}
// Finally the header of the "data" chunk. The number of bytes
// that this will take is not known at this point. The 0 will
// be overwritten later.
try!(buffer.write_all("data".as_bytes()));
try!(buffer.write_le_u32(0));
}
// The data length field are the last 4 bytes of the header.
let header_len = self.data_len_offset as usize + 4;
self.writer.write_all(&header[..header_len])
}
/// Writes the spec as a WAVEFORMAT structure.
///
/// The `WAVEFORMAT` struct is a subset of both `WAVEFORMATEX` and
/// `WAVEFORMATEXTENSIBLE`. This does not write the `wFormatTag` member.
fn write_waveformat(&self, buffer: &mut io::Cursor<&mut [u8]>) -> io::Result<()> {
let spec = &self.spec;
// The field nChannels.
try!(buffer.write_le_u16(spec.channels));
// The field nSamplesPerSec.
try!(buffer.write_le_u32(spec.sample_rate));
let bytes_per_sec = spec.sample_rate
* self.bytes_per_sample as u32
* spec.channels as u32;
// The field nAvgBytesPerSec;
try!(buffer.write_le_u32(bytes_per_sec));
// The field nBlockAlign. Block align * sample rate = bytes per sec.
try!(buffer.write_le_u16((bytes_per_sec / spec.sample_rate) as u16));
Ok(())
}
/// Writes the content of the fmt chunk as PCMWAVEFORMAT struct.
fn write_pcmwaveformat(&mut self, buffer: &mut io::Cursor<&mut [u8]>) -> io::Result<()> {
// Write the size of the WAVE header chunk.
try!(buffer.write_le_u32(16));
// The following is based on the PCMWAVEFORMAT struct as documented at
// https://msdn.microsoft.com/en-us/library/ms712832.aspx. See also
// http://soundfile.sapp.org/doc/WaveFormat/.
// The field wFormatTag
match self.spec.sample_format {
// WAVE_FORMAT_PCM
SampleFormat::Int => {
try!(buffer.write_le_u16(1));
},
// WAVE_FORMAT_IEEE_FLOAT
SampleFormat::Float => {
if self.spec.bits_per_sample == 32 {
try!(buffer.write_le_u16(3));
} else {
panic!("Invalid number of bits per sample. \
When writing SampleFormat::Float, \
bits_per_sample must be 32.");
}
},
};
try!(self.write_waveformat(buffer));
// The field wBitsPerSample, the real number of bits per sample.
try!(buffer.write_le_u16(self.spec.bits_per_sample));
// Note: for WAVEFORMATEX, there would be another 16-byte field `cbSize`
// here that should be set to zero. And the header size would be 18
// rather than 16.
Ok(())
}
/// Writes the contents of the fmt chunk as WAVEFORMATEXTENSIBLE struct.
fn write_waveformatextensible(&mut self, buffer: &mut io::Cursor<&mut [u8]>) -> io::Result<()> {
// Write the size of the WAVE header chunk.
try!(buffer.write_le_u32(40));
// The following is based on the WAVEFORMATEXTENSIBLE struct, documented
// at https://msdn.microsoft.com/en-us/library/ms713496.aspx and
// https://msdn.microsoft.com/en-us/library/ms713462.aspx.
// The field wFormatTag, value 1 means WAVE_FORMAT_PCM, but we use
// the slightly more sophisticated WAVE_FORMAT_EXTENSIBLE.
try!(buffer.write_le_u16(0xfffe));
try!(self.write_waveformat(buffer));
// The field wBitsPerSample. This is actually the size of the
// container, so this is a multiple of 8.
try!(buffer.write_le_u16(self.bytes_per_sample as u16 * 8));
// The field cbSize, the number of remaining bytes in the struct.
try!(buffer.write_le_u16(22));
// The field wValidBitsPerSample, the real number of bits per sample.
try!(buffer.write_le_u16(self.spec.bits_per_sample));
// The field dwChannelMask.
// TODO: add the option to specify the channel mask. For now, use
// the default assignment.
try!(buffer.write_le_u32(channel_mask(self.spec.channels)));
// The field SubFormat.
let subformat_guid = match self.spec.sample_format {
// PCM audio with integer samples.
SampleFormat::Int => super::KSDATAFORMAT_SUBTYPE_PCM,
// PCM audio with 32-bit IEEE float samples.
SampleFormat::Float => {
if self.spec.bits_per_sample == 32 {
super::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
} else {
panic!("Invalid number of bits per sample. \
When writing SampleFormat::Float, \
bits_per_sample must be 32.");
}
}
};
try!(buffer.write_all(&subformat_guid));
Ok(())
}
/// Writes a single sample for one channel.
///
/// WAVE interleaves channel data, so the channel that this writes the
/// sample to depends on previous writes. This will return an error if the
/// sample does not fit in the number of bits specified in the `WavSpec`.
#[inline]
pub fn write_sample<S: Sample>(&mut self, sample: S) -> Result<()> {
try!(sample.write_padded(
&mut self.writer,
self.spec.bits_per_sample,
self.bytes_per_sample,
));
self.data_bytes_written += self.bytes_per_sample as u32;
Ok(())
}
/// Create an efficient writer that writes 16-bit integer samples only.
///
/// When it is known what the kind of samples will be, many dynamic checks
/// can be omitted. Furthermore, this writer employs buffering internally,
/// which allows omitting return value checks except on flush. The internal
/// buffer will be sized such that exactly `num_samples` samples can be
/// written to it, and the buffer is recycled across calls to
/// `get_i16_writer()` if the previous buffer was sufficiently large.
///
/// # Panics
///
/// Panics if the spec does not match a 16 bits per sample integer format.
///
/// Attempting to write more than `num_samples` samples to the writer will
/// panic too.
pub fn get_i16_writer<'s>(&'s mut self,
num_samples: u32)
-> SampleWriter16<'s, W> {
if self.spec.sample_format != SampleFormat::Int {
panic!("When calling get_i16_writer, the sample format must be int.");
}
if self.spec.bits_per_sample != 16 {
panic!("When calling get_i16_writer, the number of bits per sample must be 16.");
}
let num_bytes = num_samples as usize * 2;
if self.sample_writer_buffer.len() < num_bytes {
// We need a bigger buffer. There is no point in growing the old
// one, as we are going to overwrite the samples anyway, so just
// allocate a new one.
let mut new_buffer = Vec::<MaybeUninit<u8>>::with_capacity(num_bytes);
// The potentially garbage memory here will not be exposed: the
// buffer is only exposed when flushing, but `flush()` asserts that
// all samples have been written.
unsafe { new_buffer.set_len(num_bytes); }
self.sample_writer_buffer = new_buffer;
}
SampleWriter16 {
writer: &mut self.writer,
buffer: &mut self.sample_writer_buffer[..num_bytes],
data_bytes_written: &mut self.data_bytes_written,
index: 0,
}
}
fn update_header(&mut self) -> Result<()> {
// The header size minus magic and 32-bit filesize (8 bytes).
// The data chunk length (4 bytes) is the last part of the header.
let header_size = self.data_len_offset + 4 - 8;
let file_size = self.data_bytes_written + header_size;
try!(self.writer.seek(io::SeekFrom::Start(4)));
try!(self.writer.write_le_u32(file_size));
try!(self.writer.seek(io::SeekFrom::Start(self.data_len_offset as u64)));
try!(self.writer.write_le_u32(self.data_bytes_written));
// Signal error if the last sample was not finished, but do so after
// everything has been written, so that no data is lost, even though
// the file is now ill-formed.
if (self.data_bytes_written / self.bytes_per_sample as u32)
% self.spec.channels as u32 != 0 {
Err(Error::UnfinishedSample)
} else {
Ok(())
}
}
/// Updates the WAVE header and flushes the underlying writer.
///
/// Flush writes the WAVE header to the underlying writer to make the
/// written bytes a valid wav file, and then flushes the writer. It is still
/// possible to write more samples after flushing.
///
/// Flush can be used for “checkpointing”. Even if after the flush there is
/// an IO error or the writing process dies, the file can still be read by a
/// compliant decoder up to the last flush.
///
/// Note that if the number of samples written is not a multiple of the
/// channel count, the intermediate wav file will not be valid. In that case
/// `flush()` will still flush the data and write the (invalid) wav file,
/// but `Error::UnfinishedSample` will be returned afterwards.
///
/// It is not necessary to call `finalize()` directly after `flush()`, if no
/// samples have been written after flushing.
pub fn flush(&mut self) -> Result<()> {
let current_pos = try!(self.writer.seek(io::SeekFrom::Current(0)));
try!(self.update_header());
try!(self.writer.flush());
try!(self.writer.seek(io::SeekFrom::Start(current_pos)));
Ok(())
}
/// Updates the WAVE header (which requires knowing all samples).
///
/// This method must be called after all samples have been written. If it
/// is not called, the destructor will finalize the file, but any errors
/// that occur in the process cannot be observed in that manner.
pub fn finalize(mut self) -> Result<()> {
self.finalized = true;
try!(self.update_header());
// We need to perform a flush here to truly capture all errors before
// the writer is dropped: for a buffered writer, the write to the buffer
// may succeed, but the write to the underlying writer may fail. So
// flush explicitly.
try!(self.writer.flush());
Ok(())
}
/// Returns information about the WAVE file being written.
///
/// This is the same spec that was passed to `WavWriter::new()`. For a
/// writer constructed with `WavWriter::new_append()` or
/// `WavWriter::append()`, this method returns the spec of the file being
/// appended to.
pub fn spec(&self) -> WavSpec {
self.spec
}
/// Returns the duration of the file written so far, in samples.
///
/// The duration is independent of the number of channels. It is expressed
/// in units of samples. The duration in seconds can be obtained by
/// dividing this number by the sample rate.
pub fn duration(&self) -> u32 {
self.data_bytes_written / (self.bytes_per_sample as u32 * self.spec.channels as u32)
}
/// Returns the number of samples in the file written so far.
///
/// The length of the file is its duration (in samples) times the number of
/// channels.
pub fn len(&self) -> u32 {
self.data_bytes_written / self.bytes_per_sample as u32
}
}
impl<W> Drop for WavWriter<W>
where W: io::Write + io::Seek
{
fn drop(&mut self) {
// If the file was not explicitly finalized (to update the headers), do
// it in the drop. This can fail, but drop should not panic, so a
// failure is ignored silently here.
if !self.finalized {
let _r = self.update_header();
}
}
}
/// Reads the relevant parts of the header required to support append.
///
/// Returns (spec_ex, data_len, data_len_offset).
fn read_append<W: io::Read + io::Seek>(mut reader: &mut W) -> Result<(WavSpecEx, u32, u32)> {
let (spec_ex, data_len) = {
try!(read::read_wave_header(&mut reader));
try!(read::read_until_data(&mut reader))
};
// Record the position of the data chunk length, so we can overwrite it
// later.
let data_len_offset = try!(reader.seek(io::SeekFrom::Current(0))) as u32 - 4;
let spec = spec_ex.spec;
let num_samples = data_len / spec_ex.bytes_per_sample as u32;
// There must not be trailing bytes in the data chunk, otherwise the
// bytes we write will be off.
if num_samples * spec_ex.bytes_per_sample as u32 != data_len {
let msg = "data chunk length is not a multiple of sample size";
return Err(Error::FormatError(msg));
}
// Hound cannot read or write other bit depths than those, so rather
// than refusing to write later, fail early.
let supported = match (spec_ex.bytes_per_sample, spec.bits_per_sample) {
(1, 8) => true,
(2, 16) => true,
(3, 24) => true,
(4, 32) => true,
_ => false,
};
if !supported {
return Err(Error::Unsupported);
}
// The number of samples must be a multiple of the number of channels,
// otherwise the last inter-channel sample would not have data for all
// channels.
if num_samples % spec_ex.spec.channels as u32 != 0 {
return Err(Error::FormatError("invalid data chunk length"));
}
Ok((spec_ex, data_len, data_len_offset))
}
impl WavWriter<io::BufWriter<fs::File>> {
/// Creates a writer that writes the WAVE format to a file.
///
/// This is a convenience constructor that creates the file, wraps it in a
/// `BufWriter`, and then constructs a `WavWriter` from it. The file will
/// be overwritten if it exists.
pub fn create<P: AsRef<path::Path>>(filename: P,
spec: WavSpec)
-> Result<WavWriter<io::BufWriter<fs::File>>> {
let file = try!(fs::File::create(filename));
let buf_writer = io::BufWriter::new(file);
WavWriter::new(buf_writer, spec)
}
/// Creates a writer that appends samples to an existing file.
///
/// This is a convenience constructor that opens the file in append mode,
/// reads its header using a buffered reader, and then constructs an
/// appending `WavWriter` that writes to the file using a `BufWriter`.
///
/// See `WavWriter::new_append()` for more details about append behavior.
pub fn append<P: AsRef<path::Path>>(filename: P) -> Result<WavWriter<io::BufWriter<fs::File>>> {
// Open the file in append mode, start reading from the start.
let mut file = try!(fs::OpenOptions::new().read(true).write(true).open(filename));
try!(file.seek(io::SeekFrom::Start(0)));
// Read the header using a buffered reader.
let mut buf_reader = io::BufReader::new(file);
let (spec_ex, data_len, data_len_offset) = try!(read_append(&mut buf_reader));
let mut file = buf_reader.into_inner();
// Seek to the data position, and from now on, write using a buffered
// writer.
try!(file.seek(io::SeekFrom::Current(data_len as i64)));
let buf_writer = io::BufWriter::new(file);
let writer = WavWriter {
spec: spec_ex.spec,
bytes_per_sample: spec_ex.bytes_per_sample,
writer: buf_writer,
data_bytes_written: data_len,
sample_writer_buffer: Vec::new(),
finalized: false,
data_len_offset: data_len_offset,
};
Ok(writer)
}
}
impl<W> WavWriter<W> where W: io::Read + io::Write + io::Seek {
/// Creates a writer that appends samples to an existing file stream.
///
/// This first reads the existing header to obtain the spec, then seeks to
/// the end of the writer. The writer then appends new samples to the end of
/// the stream.
///
/// The underlying writer is assumed to be at offset 0.
///
/// If the existing file includes a fact chunk, it will not be updated after
/// appending, and hence become outdated. For files produced by Hound this
/// is not an issue, because Hound never writes a fact chunk. For all the
/// formats that Hound can write, the fact chunk is redundant.
pub fn new_append(mut writer: W) -> Result<WavWriter<W>> {
let (spec_ex, data_len, data_len_offset) = try!(read_append(&mut writer));
try!(writer.seek(io::SeekFrom::Current(data_len as i64)));
let writer = WavWriter {
spec: spec_ex.spec,
bytes_per_sample: spec_ex.bytes_per_sample,
writer: writer,
data_bytes_written: data_len,
sample_writer_buffer: Vec::new(),
finalized: false,
data_len_offset: data_len_offset,
};
Ok(writer)
}
}
/// A writer that specifically only writes integer samples of 16 bits per sample.
///
/// The writer buffers written samples internally so they can be written in a
/// single batch later on. This has two advantages when performance is
/// important:
///
/// * There is no need for error handling during writing, only on flush. This
/// eliminates a lot of branches.
/// * The buffer can be written once, which reduces the overhead of the write
/// call. Because writing to an `io::BufWriter` is implemented with a
/// `memcpy` (even for single bytes), there is a large overhead to writing
/// small amounts of data such as a 16-bit sample. By writing large blocks
/// (or by not using `BufWriter`) this overhead can be avoided.
///
/// A `SampleWriter16` can be obtained by calling [`WavWriter::get_i16_writer`](
/// struct.WavWriter.html#method.get_i16_writer).
pub struct SampleWriter16<'parent, W> where W: io::Write + io::Seek + 'parent {
/// The writer borrowed from the wrapped WavWriter.
writer: &'parent mut W,
/// The internal buffer that samples are written to before they are flushed.
buffer: &'parent mut [MaybeUninit<u8>],
/// Reference to the `data_bytes_written` field of the writer.
data_bytes_written: &'parent mut u32,
/// The index into the buffer where the next bytes will be written.
index: u32,
}
impl<'parent, W: io::Write + io::Seek> SampleWriter16<'parent, W> {
/// Writes a single sample for one channel.
///
/// WAVE interleaves channel data, so the channel that this writes the
/// sample to depends on previous writes.
///
/// Unlike `WavWriter::write_sample()`, no range check is performed. Only
/// the least significant 16 bits are considered, everything else is
/// discarded. Apart from that check, this method is more efficient than
/// `WavWriter::write_sample()`, because it can avoid dispatching on the
/// number of bits. That was done already when the `SampleWriter16` was
/// constructed.
///
/// Note that nothing is actually written until `flush()` is called.
#[inline(always)]
pub fn write_sample<S: Sample>(&mut self, sample: S) {
assert!((self.index as usize) + 2 <= self.buffer.len(),
"Trying to write more samples than reserved for the sample writer.");
// SAFETY: We performed the bounds check in the above assertion.
unsafe { self.write_sample_unchecked(sample) };
}
unsafe fn write_u16_le_unchecked(&mut self, value: u16) {
// On little endian machines the compiler produces assembly code
// that merges the following two lines into a single instruction.
*self.buffer.get_unchecked_mut(self.index as usize) = MaybeUninit::new(value as u8);
self.buffer.get_unchecked_mut(self.index as usize).assume_init();
*self.buffer.get_unchecked_mut(self.index as usize + 1) = MaybeUninit::new((value >> 8) as u8);
self.buffer.get_unchecked_mut(self.index as usize + 1).assume_init();
}
/// Like `write_sample()`, but does not perform a bounds check when writing
/// to the internal buffer.
///
/// It is the responsibility of the programmer to ensure that no more
/// samples are written than allocated when the writer was created.
#[inline(always)]
pub unsafe fn write_sample_unchecked<S: Sample>(&mut self, sample: S) {
self.write_u16_le_unchecked(sample.as_i16() as u16);
self.index += 2;
}
/// Flush the internal buffer to the underlying writer.
///
/// # Panics
///
/// Panics if insufficient samples (less than specified when the writer was
/// constructed) have been written with `write_sample()`.
pub fn flush(self) -> Result<()> {
if self.index as usize != self.buffer.len() {
panic!("Insufficient samples written to the sample writer.");
}
// SAFETY: casting `self.buffer` to a `*const [MaybeUninit<u8>]` is safe
// since the caller guarantees that `self.buffer` is initialized, and
// `MaybeUninit<u8>` is guaranteed to have the same layout as `u8`. The
// pointer obtained is valid since it refers to memory owned by
// `self.buffer` which is a reference and thus guaranteed to be valid
// for reads. This is copied from the nightly implementation for
// slice_assume_init_ref.
let slice = unsafe { &*(self.buffer as *const [MaybeUninit<u8>] as *const [u8]) };
try!(self.writer.write_all(slice));
*self.data_bytes_written += self.buffer.len() as u32;
Ok(())
}
}
#[test]
fn short_write_should_signal_error() {
use SampleFormat;
let mut buffer = io::Cursor::new(Vec::new());
let write_spec = WavSpec {
channels: 17,
sample_rate: 48000,
bits_per_sample: 8,
sample_format: SampleFormat::Int,
};
// Deliberately write one sample less than 17 * 5.
let mut writer = WavWriter::new(&mut buffer, write_spec).unwrap();
for s in 0..17 * 5 - 1 {
writer.write_sample(s as i16).unwrap();
}
let error = writer.finalize().err().unwrap();
match error {
Error::UnfinishedSample => {}
_ => panic!("UnfinishedSample error should have been returned."),
}
}
#[test]
fn wide_write_should_signal_error() {
let mut buffer = io::Cursor::new(Vec::new());
let spec8 = WavSpec {
channels: 1,
sample_rate: 44100,
bits_per_sample: 8,
sample_format: SampleFormat::Int,
};
{
let mut writer = WavWriter::new(&mut buffer, spec8).unwrap();
assert!(writer.write_sample(127_i8).is_ok());
assert!(writer.write_sample(127_i16).is_ok());
assert!(writer.write_sample(127_i32).is_ok());
assert!(writer.write_sample(128_i16).is_err());
assert!(writer.write_sample(128_i32).is_err());
}
let spec16 = WavSpec { bits_per_sample: 16, ..spec8 };
{
let mut writer = WavWriter::new(&mut buffer, spec16).unwrap();
assert!(writer.write_sample(32767_i16).is_ok());
assert!(writer.write_sample(32767_i32).is_ok());
assert!(writer.write_sample(32768_i32).is_err());
}
let spec24 = WavSpec { bits_per_sample: 24, ..spec8 };
{
let mut writer = WavWriter::new(&mut buffer, spec24).unwrap();
assert!(writer.write_sample(8_388_607_i32).is_ok());
assert!(writer.write_sample(8_388_608_i32).is_err());
}
}
#[test]
fn s24_wav_write() {
use std::fs::File;
use std::io::Read;
let mut buffer = io::Cursor::new(Vec::new());
let spec = WavSpecEx {
spec: WavSpec {
channels: 2,
sample_rate: 48000,
bits_per_sample: 24,
sample_format: SampleFormat::Int,
},
bytes_per_sample: 4,
};
{
let mut writer = WavWriter::new_with_spec_ex(&mut buffer, spec).unwrap();
assert!(writer.write_sample(-96_i32).is_ok());
assert!(writer.write_sample(23_052_i32).is_ok());
assert!(writer.write_sample(8_388_607_i32).is_ok());
assert!(writer.write_sample(-8_360_672_i32).is_ok());
}
let mut expected = Vec::new();
File::open("testsamples/waveformatextensible-24bit-4byte-48kHz-stereo.wav")
.unwrap()
.read_to_end(&mut expected)
.unwrap();
assert_eq!(buffer.into_inner(), expected);
}