use alloc::vec::Vec;
use super::{AudioBuffer, SampleFormat, Sink, StreamInfo};
const FORMAT_PCM: u16 = 1;
const FORMAT_FLOAT: u16 = 3;
const HEADER_BYTES: usize = 44;
const fn tag_and_bits(format: SampleFormat) -> (u16, u16) {
match format {
SampleFormat::F32 => (FORMAT_FLOAT, 32),
SampleFormat::U8 => (FORMAT_PCM, 8),
_ => (FORMAT_PCM, 16),
}
}
#[must_use]
pub fn encode(info: StreamInfo, buffer: &AudioBuffer) -> Vec<u8> {
let mut out = Vec::with_capacity(HEADER_BYTES + buffer.bytes().len());
write_header(
&mut out,
info.rate_hz(),
buffer.channels(),
buffer.format(),
buffer.len(),
);
out.extend_from_slice(buffer.bytes());
out
}
fn write_header(out: &mut Vec<u8>, rate: u32, channels: u16, format: SampleFormat, data: u64) {
let (tag, bits) = tag_and_bits(format);
let block_align = u32::from(channels) * u32::from(bits) / 8;
let byte_rate = rate.saturating_mul(block_align);
let data = u32::try_from(data).unwrap_or(u32::MAX);
out.extend_from_slice(b"RIFF");
out.extend_from_slice(&data.saturating_add(36).to_le_bytes());
out.extend_from_slice(b"WAVE");
out.extend_from_slice(b"fmt ");
out.extend_from_slice(&16u32.to_le_bytes());
out.extend_from_slice(&tag.to_le_bytes());
out.extend_from_slice(&channels.to_le_bytes());
out.extend_from_slice(&rate.to_le_bytes());
out.extend_from_slice(&byte_rate.to_le_bytes());
out.extend_from_slice(&(u16::try_from(block_align).unwrap_or(u16::MAX)).to_le_bytes());
out.extend_from_slice(&bits.to_le_bytes());
out.extend_from_slice(b"data");
out.extend_from_slice(&data.to_le_bytes());
}
#[derive(Debug, Clone)]
pub struct Writer {
info: StreamInfo,
format: SampleFormat,
channels: u16,
samples: Vec<u8>,
}
impl Writer {
#[must_use]
pub fn new(info: StreamInfo, format: SampleFormat) -> Writer {
Writer {
info,
format,
channels: info.channels,
samples: Vec::new(),
}
}
#[must_use]
pub fn frames(&self) -> u64 {
let stride = self.format.bytes_per_sample() * u64::from(self.channels);
(self.samples.len() as u64).checked_div(stride).unwrap_or(0)
}
#[must_use]
pub fn duration_ms(&self) -> u64 {
let rate = u64::from(self.info.rate_hz().max(1));
self.frames().saturating_mul(1000) / rate
}
#[must_use]
pub fn finish(&self) -> Vec<u8> {
let mut out = Vec::with_capacity(HEADER_BYTES + self.samples.len());
write_header(
&mut out,
self.info.rate_hz(),
self.channels,
self.format,
self.samples.len() as u64,
);
out.extend_from_slice(&self.samples);
out
}
}
impl Sink for Writer {
fn info(&self) -> StreamInfo {
StreamInfo::new(
self.info.rate_num,
self.info.rate_den,
self.channels,
self.format,
)
}
fn write(&mut self, buffer: &AudioBuffer) -> u64 {
if buffer.format() != self.format || buffer.channels() != self.channels {
return 0;
}
self.samples.extend_from_slice(buffer.bytes());
buffer.frames()
}
}