1use std::{cell::RefCell, collections::HashMap, rc::Rc};
2
3use nom::{
4 combinator,
5 error::context,
6 multi,
7 number::complete::{le_f64, u8},
8 IResult,
9};
10
11use crate::{
12 chunk_structs::{SamplesChunk, StreamHeaderChunkInfo},
13 Format, Sample,
14};
15
16use super::{chunk_content, chunk_length::length, chunk_tags::samples_tag, stream_id, values};
17
18fn optional_timestamp(input: &[u8]) -> IResult<&[u8], Option<f64>> {
19 let (input, timestamp_bytes) = u8(input)?;
20 match timestamp_bytes {
21 0 => Ok((input, None)),
22 8 => {
23 let (input, timestamp) = le_f64(input)?;
24 Ok((input, Some(timestamp)))
25 }
26 _ => Err(nom::Err::Failure(nom::error::Error::new(
27 input,
28 nom::error::ErrorKind::Char, ))),
30 }
31}
32
33fn sample(input: &[u8], num_channels: usize, format: Format) -> IResult<&[u8], Sample> {
39 let (input, timestamp) = context("sample optional_timestamp", optional_timestamp)(input)?;
40 let (input, values) = context("sample values", |i| values(i, format, num_channels))(input)?;
41
42 Ok((input, Sample { timestamp, values }))
43}
44
45#[allow(clippy::needless_pass_by_value)]
46pub(super) fn samples(
47 input: &[u8],
48 stream_info: Rc<RefCell<HashMap<u32, StreamHeaderChunkInfo>>>,
50) -> IResult<&[u8], SamplesChunk> {
51 let stream_info = stream_info.borrow();
52 let (input, chunk_content) = context("samples chunk_content", chunk_content)(input)?;
53 let (chunk_content, _tag) = context("samples tag", samples_tag)(chunk_content)?; let (chunk_content, stream_id) = context("samples stream_id", stream_id)(chunk_content)?; let (chunk_content, num_samples) = context("samples num_samples", length)(chunk_content)?;
56
57 let Some(stream_info) = stream_info.get(&stream_id) else {
58 return context("samples get(&stream_id), missing a stream header", combinator::fail)(&[0]);
59 };
60 let num_channels = stream_info.channel_count as usize;
61 let format = stream_info.channel_format;
62
63 let (_chunk_content, samples) = multi::count(|i| sample(i, num_channels, format), num_samples)(chunk_content)?;
64
65 Ok((input, SamplesChunk { stream_id, samples }))
66}