///|
/// Incremental integer PCM input events. Samples are interleaved, unshifted,
/// owned, and always contain complete channel groups.
pub(all) enum PcmInputEvent {
PcmFormatReady(PcmFormat, UInt32?)
PcmSamples(Array[Int32])
PcmNeedInput
PcmInputDone
}
///|
/// A bounded RIFF/RF64 or signed raw PCM reader. Unknown chunks are skipped
/// incrementally. RF64 size tables have an explicit 65536-entry resource limit.
pub struct PcmInput {
mut pending : BytesView
mut offset : Int64
mut ended : Bool
mut failed : FlacError?
mut stage : Int32
mut format : PcmFormat?
mut announced : Bool
mut mask : UInt32?
mut width : Int32
mut shift : Int32
raw : Bool
mut rf64 : Bool
mut riff_end : Int64?
mut chunk_left : Int64
mut pad : Bool
mut saw_data : Bool
mut saw_ds64 : Bool
mut data_size : Int64
mut data_size_used : Bool
mut expected_samples : Int64
mut samples : Int64
mut table_left : Int32
sizes : Map[UInt32, Array[Int64]]
size_positions : Map[UInt32, Int32]
}
///|
fn PcmInput::create(raw : Bool, format : PcmFormat?) -> PcmInput {
{
pending: b""[:],
offset: 0L,
ended: false,
failed: None,
stage: if raw {
9
} else {
0
},
format,
announced: false,
mask: None,
width: 0,
shift: 0,
raw,
rf64: false,
riff_end: None,
chunk_left: 0L,
pad: false,
saw_data: false,
saw_ds64: false,
data_size: 0L,
data_size_used: false,
expected_samples: 0L,
samples: 0L,
table_left: 0,
sizes: {},
size_positions: {},
}
}
///|
/// Read integer RIFF WAVE or RF64; floating-point PCM is explicitly rejected.
pub fn PcmInput::wav() -> PcmInput {
PcmInput::create(false, None)
}
///|
/// Raw PCM is signed little-endian, sign-extended and unshifted in ceil(bits/8)
/// bytes. Zero sample rate is representable; the encoder requires non_audio.
pub fn PcmInput::raw(format : PcmFormat) -> PcmInput raise FlacError {
pcm_input_validate(format, 0L)
let reader = PcmInput::create(true, Some(format))
reader.width = (format.bits_per_sample + 7) / 8
reader
}
///|
fn pcm_input_validate(
format : PcmFormat,
offset : Int64,
) -> Unit raise FlacError {
if format.channels < 1 ||
format.channels > 8 ||
format.bits_per_sample < 4 ||
format.bits_per_sample > 32 ||
format.sample_rate < 0 ||
format.sample_rate > 1048575 {
raise Invalid(offset, "PCM format outside FLAC dimensions")
}
}
///|
/// Copies at most the remaining 64 KiB staging capacity; drain next and retry
/// any unaccepted suffix. Calling after finish is an error.
pub fn PcmInput::push(self : PcmInput, input : Bytes) -> Int32 raise FlacError {
if self.failed is Some(error) {
raise error
}
if self.ended {
raise Invalid(self.offset, "PCM input after finish")
}
let room = 65536 - self.pending.length()
if room == 0 {
raise Limit("PCM input staging full; drain next")
}
let count = if input.length() < room { input.length() } else { room }
if count == 0 {
return 0
}
let old = self.pending
let storage = FixedArray::make(old.length() + count, (0 : Byte))
storage.blit_from_bytesview(0, old)
storage.blit_from_bytes(old.length(), input, 0, count)
// This allocation is exclusively owned and never mutated after freezing.
// Both copies are bounded by 64 KiB; no view can retain the caller's input.
self.pending = storage.unsafe_reinterpret_as_bytes()[:]
count
}
///|
pub fn PcmInput::finish(self : PcmInput) -> Unit raise FlacError {
if self.failed is Some(error) {
raise error
}
self.ended = true
}
///|
pub fn PcmInput::buffered_bytes(self : PcmInput) -> Int32 {
self.pending.length()
}
///|
fn PcmInput::consume(self : PcmInput, count : Int32) -> Unit {
self.pending = if count == self.pending.length() {
b""[:]
} else {
self.pending[count:]
}
self.offset += count.to_int64()
}
///|
fn pcm_le(data : BytesView, offset : Int32, width : Int32) -> Int64 {
let mut result = 0L
for i in 0.. Int64 raise FlacError {
if data[offset + 7].to_int() >= 128 {
raise Limit("WAV size exceeds signed 64-bit range")
}
let value = pcm_le(data, offset, 8)
if value > 9223372036854775799L {
raise Invalid(position, "WAV size cannot include RIFF header")
}
value
}
///|
fn PcmInput::incomplete(self : PcmInput) -> PcmInputEvent raise FlacError {
if self.ended {
raise Invalid(self.offset, "truncated PCM/WAV input")
}
PcmNeedInput
}
///|
fn PcmInput::format_body(self : PcmInput) -> Unit raise FlacError {
let p = self.pending
let tag = pcm_le(p, 0, 2).to_int()
let channels = pcm_le(p, 2, 2).to_int()
let rate = pcm_le(p, 4, 4)
let byte_rate = pcm_le(p, 8, 4)
let align = pcm_le(p, 12, 2).to_int()
let container_bits = pcm_le(p, 14, 2).to_int()
if tag != 1 && tag != 65534 {
raise Invalid(
self.offset,
"only integer PCM WAV is supported (not float or compressed audio)",
)
}
if container_bits != 8 &&
container_bits != 16 &&
container_bits != 24 &&
container_bits != 32 {
raise Invalid(self.offset, "WAV PCM container must be 8, 16, 24 or 32 bits")
}
if self.chunk_left > 16L &&
(self.chunk_left < 18L || pcm_le(p, 16, 2) > self.chunk_left - 18L) {
raise Invalid(self.offset, "WAV format extension exceeds its chunk")
}
let bits = if tag == 65534 {
if self.chunk_left < 40L || pcm_le(p, 16, 2) < 22L {
raise Invalid(self.offset, "short extensible WAV format")
}
if p[24:40].to_owned() !=
b"\x01\x00\x00\x00\x00\x00\x10\x00\x80\x00\x00\xaa\x00\x38\x9b\x71" {
raise Invalid(self.offset, "WAV extensible subformat is not integer PCM")
}
self.mask = Some(pcm_le(p, 20, 4).to_int().reinterpret_as_uint())
pcm_le(p, 18, 2).to_int()
} else {
container_bits
}
if rate > 1048575L ||
bits > container_bits ||
align != channels * (container_bits / 8) ||
byte_rate != rate * align.to_int64() {
raise Invalid(
self.offset,
"inconsistent WAV format dimensions or byte rate",
)
}
let format : PcmFormat = {
sample_rate: rate.to_int(),
channels,
bits_per_sample: bits,
}
pcm_input_validate(format, self.offset)
self.format = Some(format)
self.width = container_bits / 8
self.shift = container_bits - bits
}
///|
fn PcmInput::read_samples(
self : PcmInput,
format : PcmFormat,
count : Int32,
) -> Array[Int32] raise FlacError {
if self.shift == 0 && format.bits_per_sample == self.width * 8 {
// step() supplies count <= pending.length(), divisible by width*channels,
// with at most 4096 scalar samples. Validated raw/WAV widths are 1..4.
// For i
if self.raw {
Array::makei(length, i => input.unsafe_get(i).to_int() << 24 >> 24)
} else {
Array::makei(length, i => input.unsafe_get(i).to_int() - 128)
}
2 =>
Array::makei(length, i => {
input.unsafe_read_uint16_le(i * 2).to_int() << 16 >> 16
})
3 =>
Array::makei(length, i => {
let at = i * 3
let word = input.unsafe_read_uint16_le(at).to_int() |
(input.unsafe_get(at + 2).to_int() << 16)
word << 8 >> 8
})
4 =>
Array::makei(length, i => {
input.unsafe_read_uint32_le(i * 4).reinterpret_as_int()
})
_ => abort("validated PCM container width")
}
// Commit only after the complete owned output has been constructed.
self.samples += (count / (self.width * format.channels)).to_int64()
self.consume(count)
return out
}
let out : Array[Int32] = []
let minimum = -(1L << (format.bits_per_sample - 1))
let maximum = (1L << (format.bits_per_sample - 1)) - 1L
for i in 0..<(count / self.width) {
let unsigned = pcm_le(self.pending, i * self.width, self.width)
let value = if !self.raw && self.width == 1 {
unsigned - 128L
} else if (unsigned & (1L << (self.width * 8 - 1))) != 0L {
unsigned - (1L << (self.width * 8))
} else {
unsigned
}
if !self.raw && (value & ((1L << self.shift) - 1L)) != 0L {
raise Invalid(
self.offset + (i * self.width).to_int64(),
"nonzero WAV unused low bits",
)
}
let sample = value >> self.shift
if sample < minimum || sample > maximum {
raise Invalid(
self.offset + (i * self.width).to_int64(),
"raw PCM sample is not sign-extended to its declared bit depth",
)
}
out.push(sample.to_int())
}
self.samples += (count / (self.width * format.channels)).to_int64()
self.consume(count)
out
}
///|
fn PcmInput::step(self : PcmInput) -> PcmInputEvent raise FlacError {
if !self.announced && self.format is Some(format) {
self.announced = true
return PcmFormatReady(format, self.mask)
}
while true {
match self.stage {
0 => {
if self.pending.length() < 12 {
return self.incomplete()
}
let signature = self.pending[:4].to_owned()
if (signature != b"RIFF" && signature != b"RF64") ||
self.pending[8:12].to_owned() != b"WAVE" {
raise Invalid(0L, "input is not RIFF/RF64 WAVE")
}
let size = pcm_le(self.pending, 4, 4)
self.rf64 = signature == b"RF64"
if self.rf64 {
if size != 4294967295L {
raise Invalid(4L, "RF64 RIFF size must be 0xffffffff")
}
} else {
if size < 4L {
raise Invalid(4L, "short RIFF size")
}
self.riff_end = Some(size + 8L)
}
self.consume(12)
self.stage = 1
}
1 => {
if self.riff_end is Some(end) {
if self.offset == end {
self.stage = 8
continue
}
if end - self.offset < 8L {
raise Invalid(
self.offset,
"partial WAV chunk header within RIFF size",
)
}
}
if self.pending.length() < 8 {
return self.incomplete()
}
let tag = self.pending[:4].to_owned()
let id = pcm_le(self.pending, 0, 4).to_int().reinterpret_as_uint()
let mut size = pcm_le(self.pending, 4, 4)
if self.rf64 && !self.saw_ds64 && tag != b"ds64" {
raise Invalid(self.offset, "RF64 requires ds64 as its first chunk")
}
if self.rf64 {
if tag == b"data" && !self.data_size_used {
if size != 4294967295L && size != self.data_size {
raise Invalid(self.offset, "RF64 data size disagrees with ds64")
}
size = self.data_size
self.data_size_used = true
} else if size == 4294967295L {
let index = self.size_positions.get(id).unwrap_or(0)
match self.sizes.get(id) {
Some(sizes) if index < sizes.length() => {
size = sizes[index]
self.size_positions[id] = index + 1
}
_ =>
raise Invalid(
self.offset,
"missing RF64 chunk size table entry",
)
}
}
}
self.consume(8)
self.chunk_left = size
self.pad = (size & 1L) == 1L
if self.riff_end is Some(end) &&
(size > end - self.offset || (self.pad && size == end - self.offset)) {
raise Invalid(self.offset, "WAV chunk exceeds RIFF boundary")
}
if tag == b"fmt " {
if self.format is Some(_) || size < 16L {
raise Invalid(self.offset, "duplicate or short WAV format")
}
self.stage = 2
} else if tag == b"ds64" {
if !self.rf64 || self.saw_ds64 || size < 28L {
raise Invalid(self.offset, "unexpected or short RF64 ds64 chunk")
}
self.stage = 3
} else if tag == b"data" {
let format = match self.format {
Some(value) => value
None => raise Invalid(self.offset, "WAV data precedes format")
}
if size % (self.width * format.channels).to_int64() != 0L {
raise Invalid(
self.offset,
"WAV data size contains a partial sample frame",
)
}
self.saw_data = true
self.stage = 6
} else {
self.stage = 5
}
}
2 => {
if self.pending.length() < 16 {
return self.incomplete()
}
let needed = if pcm_le(self.pending, 0, 2) == 65534L {
40
} else if self.chunk_left > 16L {
18
} else {
16
}
if self.chunk_left < needed.to_int64() {
raise Invalid(self.offset, "short WAV format chunk")
}
if self.pending.length() < needed {
return self.incomplete()
}
self.format_body()
self.consume(needed)
self.chunk_left -= needed.to_int64()
self.stage = 5
self.announced = true
return PcmFormatReady(self.format.unwrap(), self.mask)
}
3 => {
if self.pending.length() < 28 {
return self.incomplete()
}
let riff_size = pcm_u64_size(self.pending, 0, self.offset)
let data_size = pcm_u64_size(self.pending, 8, self.offset + 8L)
let expected = pcm_u64_size(self.pending, 16, self.offset + 16L)
let entries = pcm_le(self.pending, 24, 4)
if entries > 65536L {
raise Limit("RF64 size table exceeds 65536 entries")
}
if 28L + entries * 12L > self.chunk_left ||
riff_size + 8L <
self.offset + self.chunk_left + (if self.pad { 1L } else { 0L }) {
raise Invalid(self.offset, "invalid RF64 ds64 sizes")
}
self.riff_end = Some(riff_size + 8L)
self.data_size = data_size
self.expected_samples = expected
self.table_left = entries.to_int()
self.saw_ds64 = true
self.consume(28)
self.chunk_left -= 28L
self.stage = 4
}
4 => {
if self.table_left == 0 {
self.stage = 5
continue
}
if self.pending.length() < 12 {
return self.incomplete()
}
let id = pcm_le(self.pending, 0, 4).to_int().reinterpret_as_uint()
let size = pcm_u64_size(self.pending, 4, self.offset + 4L)
match self.sizes.get(id) {
Some(values) => values.push(size)
None => self.sizes[id] = [size]
}
self.consume(12)
self.chunk_left -= 12L
self.table_left -= 1
}
5 => {
if self.chunk_left == 0L {
self.stage = 7
continue
}
if self.pending.is_empty() {
return self.incomplete()
}
let count = if self.chunk_left < self.pending.length().to_int64() {
self.chunk_left.to_int()
} else {
self.pending.length()
}
self.consume(count)
self.chunk_left -= count.to_int64()
}
6 | 9 => {
if self.stage == 6 && self.chunk_left == 0L {
self.stage = 7
continue
}
let format = self.format.unwrap()
let align = self.width * format.channels
let available = if self.stage == 6 &&
self.chunk_left < self.pending.length().to_int64() {
self.chunk_left.to_int()
} else {
self.pending.length()
}
let frames = available / align
let frame_limit = 4096 / format.channels
let count = (if frames < frame_limit { frames } else { frame_limit }) *
align
if count == 0 {
if self.stage == 9 && self.ended && self.pending.is_empty() {
self.stage = 8
continue
}
return self.incomplete()
}
let samples = self.read_samples(format, count)
if self.stage == 6 {
self.chunk_left -= count.to_int64()
}
return PcmSamples(samples)
}
7 => {
if self.pad {
if self.pending.is_empty() {
return self.incomplete()
}
self.consume(1)
}
self.stage = 1
}
8 => {
if !self.pending.is_empty() {
raise Invalid(self.offset, "trailing bytes beyond WAV container")
}
if !self.ended {
return PcmNeedInput
}
if !self.raw && !self.saw_data {
raise Invalid(self.offset, "WAV contains no data chunk")
}
if self.expected_samples != 0L && self.samples != self.expected_samples {
raise Invalid(
self.offset,
"RF64 sample count does not match PCM data",
)
}
return PcmInputDone
}
_ => abort("unreachable PCM reader stage")
}
} nobreak {
abort("unreachable PCM input state")
}
}
///|
/// Pull a format, complete interleaved PCM group, input request or completion.
/// Errors are sticky, and truncated input is diagnosed after finish.
pub fn PcmInput::next(self : PcmInput) -> PcmInputEvent raise FlacError {
if self.failed is Some(error) {
raise error
}
self.step() catch {
error => {
self.failed = Some(error)
raise error
}
}
}