flacx 0.11.0

Convert supported PCM containers to FLAC, decode FLAC back to PCM containers, and recompress existing FLAC streams.
Documentation
use crate::{
    error::{Error, Result},
    model::ChannelAssignment,
};

pub(crate) fn unfold_residual(folded: u32) -> i32 {
    if folded & 1 == 0 {
        (folded >> 1) as i32
    } else {
        -(((folded >> 1) as i32) + 1)
    }
}

#[cfg(test)]
pub(crate) fn restore_fixed(order: u8, warmup: Vec<i32>, residuals: Vec<i32>) -> Result<Vec<i32>> {
    if warmup.len() != usize::from(order) {
        return Err(Error::Decode(
            "warmup length does not match predictor order".into(),
        ));
    }

    let mut samples = warmup;
    samples.reserve(residuals.len());
    for residual in residuals {
        append_fixed_residual(&mut samples, order, residual)?;
    }
    Ok(samples)
}

#[cfg(test)]
pub(crate) fn restore_lpc(
    order: u8,
    warmup: Vec<i32>,
    shift: i8,
    coefficients: &[i16],
    residuals: Vec<i32>,
) -> Result<Vec<i32>> {
    if warmup.len() != usize::from(order) || coefficients.len() != usize::from(order) {
        return Err(Error::Decode(
            "LPC coefficient/warmup length does not match predictor order".into(),
        ));
    }

    let mut samples = warmup;
    samples.reserve(residuals.len());
    for residual in residuals {
        append_lpc_residual(&mut samples, shift, coefficients, residual)?;
    }
    Ok(samples)
}

pub(crate) fn append_fixed_residual(
    samples: &mut Vec<i32>,
    order: u8,
    residual: i32,
) -> Result<()> {
    let order = usize::from(order);
    if samples.len() < order {
        return Err(Error::Decode(
            "warmup length does not match predictor order".into(),
        ));
    }

    let index = samples.len();
    let predicted = match order {
        0 => 0,
        1 => i64::from(samples[index - 1]),
        2 => 2 * i64::from(samples[index - 1]) - i64::from(samples[index - 2]),
        3 => {
            3 * i64::from(samples[index - 1]) - 3 * i64::from(samples[index - 2])
                + i64::from(samples[index - 3])
        }
        4 => {
            4 * i64::from(samples[index - 1]) - 6 * i64::from(samples[index - 2])
                + 4 * i64::from(samples[index - 3])
                - i64::from(samples[index - 4])
        }
        _ => {
            return Err(Error::UnsupportedFlac(format!(
                "fixed predictor order {order} is out of scope"
            )));
        }
    };
    let sample = i32::try_from(predicted + i64::from(residual))
        .map_err(|_| Error::Decode("fixed predictor overflowed".into()))?;
    samples.push(sample);
    Ok(())
}

pub(crate) fn append_lpc_residual(
    samples: &mut Vec<i32>,
    shift: i8,
    coefficients: &[i16],
    residual: i32,
) -> Result<()> {
    if samples.len() < coefficients.len() {
        return Err(Error::Decode(
            "LPC coefficient/warmup length does not match predictor order".into(),
        ));
    }

    let index = samples.len();
    let mut predicted = 0i64;
    for (offset, &coefficient) in coefficients.iter().enumerate() {
        predicted += i64::from(coefficient) * i64::from(samples[index - offset - 1]);
    }
    if shift >= 0 {
        predicted >>= i32::from(shift);
    } else {
        predicted <<= i32::from(-shift);
    }
    let sample = i32::try_from(predicted + i64::from(residual))
        .map_err(|_| Error::Decode("LPC predictor overflowed".into()))?;
    samples.push(sample);
    Ok(())
}

#[cfg(test)]
pub(crate) fn interleave_channels(
    assignment: ChannelAssignment,
    channels: &[Vec<i32>],
) -> Result<Vec<i32>> {
    let mut interleaved = Vec::with_capacity(interleaved_sample_count(assignment, channels)?);
    interleave_channels_into(assignment, channels, &mut interleaved)?;
    Ok(interleaved)
}

pub(crate) fn interleave_channels_into(
    assignment: ChannelAssignment,
    channels: &[Vec<i32>],
    output: &mut Vec<i32>,
) -> Result<()> {
    output.reserve(interleaved_sample_count(assignment, channels)?);
    match assignment {
        ChannelAssignment::Independent(channels_count) => {
            interleave_independent(usize::from(channels_count), channels, output)
        }
        ChannelAssignment::LeftSide => {
            let left = channels
                .first()
                .ok_or_else(|| Error::Decode("left+side frame is missing left samples".into()))?;
            let side = channels
                .get(1)
                .ok_or_else(|| Error::Decode("left+side frame is missing side samples".into()))?;
            if left.len() != side.len() {
                return Err(Error::Decode(
                    "left and side channels differ in length".into(),
                ));
            }
            for (&left_sample, &side_sample) in left.iter().zip(side) {
                let right_sample =
                    i32::try_from(i64::from(left_sample) - i64::from(side_sample))
                        .map_err(|_| Error::Decode("left+side reconstruction overflowed".into()))?;
                output.push(left_sample);
                output.push(right_sample);
            }
            Ok(())
        }
        ChannelAssignment::SideRight => {
            let side = channels
                .first()
                .ok_or_else(|| Error::Decode("side+right frame is missing side samples".into()))?;
            let right = channels
                .get(1)
                .ok_or_else(|| Error::Decode("side+right frame is missing right samples".into()))?;
            if side.len() != right.len() {
                return Err(Error::Decode(
                    "side and right channels differ in length".into(),
                ));
            }
            for (&side_sample, &right_sample) in side.iter().zip(right) {
                let left_sample = i32::try_from(i64::from(side_sample) + i64::from(right_sample))
                    .map_err(|_| {
                    Error::Decode("side+right reconstruction overflowed".into())
                })?;
                output.push(left_sample);
                output.push(right_sample);
            }
            Ok(())
        }
        ChannelAssignment::MidSide => {
            let mid = channels
                .first()
                .ok_or_else(|| Error::Decode("mid+side frame is missing mid samples".into()))?;
            let side = channels
                .get(1)
                .ok_or_else(|| Error::Decode("mid+side frame is missing side samples".into()))?;
            if mid.len() != side.len() {
                return Err(Error::Decode(
                    "mid and side channels differ in length".into(),
                ));
            }
            for (&mid_sample, &side_sample) in mid.iter().zip(side) {
                let left_sample = i32::try_from(
                    i64::from(mid_sample)
                        + ((i64::from(side_sample) + i64::from(side_sample & 1)) >> 1),
                )
                .map_err(|_| Error::Decode("mid+side reconstruction overflowed".into()))?;
                let right_sample =
                    i32::try_from(i64::from(left_sample) - i64::from(side_sample))
                        .map_err(|_| Error::Decode("mid+side reconstruction overflowed".into()))?;
                output.push(left_sample);
                output.push(right_sample);
            }
            Ok(())
        }
    }
}

fn interleaved_sample_count(assignment: ChannelAssignment, channels: &[Vec<i32>]) -> Result<usize> {
    let channel_count = match assignment {
        ChannelAssignment::Independent(expected_channels) => usize::from(expected_channels),
        _ => assignment.channel_count(),
    };
    let Some(first) = channels.first() else {
        return Err(Error::Decode("independent frame is missing samples".into()));
    };
    Ok(first.len() * channel_count)
}

fn interleave_independent(
    expected_channels: usize,
    channels: &[Vec<i32>],
    output: &mut Vec<i32>,
) -> Result<()> {
    if channels.len() != expected_channels {
        return Err(Error::Decode(format!(
            "independent frame expected {expected_channels} channels, found {}",
            channels.len()
        )));
    }

    let Some(first) = channels.first() else {
        return Err(Error::Decode("independent frame is missing samples".into()));
    };
    if channels
        .iter()
        .skip(1)
        .any(|channel| channel.len() != first.len())
    {
        return Err(Error::Decode(
            "independent channels differ in length".into(),
        ));
    }

    if expected_channels == 2 {
        let left = &channels[0];
        let right = &channels[1];
        for (&left_sample, &right_sample) in left.iter().zip(right) {
            output.push(left_sample);
            output.push(right_sample);
        }
        return Ok(());
    }

    for frame_index in 0..first.len() {
        for channel in channels {
            output.push(channel[frame_index]);
        }
    }
    Ok(())
}

#[cfg(test)]
mod tests {
    use crate::model::ChannelAssignment;

    use super::{
        interleave_channels, interleave_channels_into, restore_fixed, restore_lpc, unfold_residual,
    };

    #[test]
    fn unfold_residual_matches_rice_mapping() {
        assert_eq!(unfold_residual(0), 0);
        assert_eq!(unfold_residual(1), -1);
        assert_eq!(unfold_residual(2), 1);
        assert_eq!(unfold_residual(3), -2);
    }

    #[test]
    fn reconstructs_mid_side_channels() {
        let interleaved =
            interleave_channels(ChannelAssignment::MidSide, &[vec![10, 11], vec![2, -1]]).unwrap();

        assert_eq!(interleaved, vec![11, 9, 11, 12]);
    }

    #[test]
    fn reconstructs_independent_multichannel_frames() {
        let interleaved = interleave_channels(
            ChannelAssignment::Independent(3),
            &[vec![1, 2], vec![3, 4], vec![5, 6]],
        )
        .unwrap();

        assert_eq!(interleaved, vec![1, 3, 5, 2, 4, 6]);
    }

    #[test]
    fn interleave_channels_into_appends_samples_to_existing_output() {
        let mut interleaved = vec![-1, -2];
        interleave_channels_into(
            ChannelAssignment::Independent(2),
            &[vec![1, 2], vec![3, 4]],
            &mut interleaved,
        )
        .unwrap();

        assert_eq!(interleaved, vec![-1, -2, 1, 3, 2, 4]);
    }

    #[test]
    fn restores_fixed_predictor_samples() {
        let samples = restore_fixed(2, vec![10, 12], vec![3, -2]).unwrap();

        assert_eq!(samples, vec![10, 12, 17, 20]);
    }

    #[test]
    fn restores_lpc_predictor_samples() {
        let samples = restore_lpc(1, vec![5], 0, &[1], vec![2, -1]).unwrap();

        assert_eq!(samples, vec![5, 7, 6]);
    }
}