opus-codec 0.2.0

Safe Rust bindings for the Opus audio codec
Documentation
#![cfg(not(opus_codec_system_lib))]

use opus_codec::multistream::{Mapping, MultistreamDecoder, MultistreamEncoder};
use opus_codec::projection::ProjectionEncoder;
use opus_codec::{
    Application, Bandwidth, Bitrate, Channels, Complexity, Decoder, Encoder, Error,
    ExpertFrameDuration, Result, SampleRate, Signal,
};

type CtlCase<T> = (&'static str, fn(&mut T) -> Result<()>);

fn run_ctl_cases<T>(target: &mut T, cases: &[CtlCase<T>]) {
    for (name, case) in cases {
        case(target).unwrap_or_else(|err| panic!("bundled Opus CTL {name} failed: {err:?}"));
    }
}

#[test]
fn every_single_stream_ctl_is_supported_by_bundled_opus() {
    let mut encoder =
        Encoder::new(SampleRate::Hz48000, Channels::Stereo, Application::Audio).unwrap();
    let encoder_cases: &[CtlCase<Encoder>] = &[
        ("bitrate", |encoder| {
            encoder.set_bitrate(Bitrate::Custom(96_000))?;
            let _ = encoder.bitrate()?;
            Ok(())
        }),
        ("complexity", |encoder| {
            encoder.set_complexity(Complexity::new(5))?;
            let _ = encoder.complexity()?;
            Ok(())
        }),
        ("vbr", |encoder| {
            encoder.set_vbr(true)?;
            let _ = encoder.vbr()?;
            Ok(())
        }),
        ("vbr_constraint", |encoder| {
            encoder.set_vbr_constraint(true)?;
            let _ = encoder.vbr_constraint()?;
            Ok(())
        }),
        ("inband_fec", |encoder| {
            encoder.set_inband_fec(true)?;
            let _ = encoder.inband_fec()?;
            Ok(())
        }),
        ("packet_loss", |encoder| {
            encoder.set_packet_loss_perc(10)?;
            let _ = encoder.packet_loss_perc()?;
            Ok(())
        }),
        ("dtx", |encoder| {
            encoder.set_dtx(true)?;
            let _ = encoder.dtx()?;
            let _ = encoder.in_dtx()?;
            Ok(())
        }),
        ("max_bandwidth", |encoder| {
            encoder.set_max_bandwidth(Bandwidth::Wideband)?;
            let _ = encoder.max_bandwidth()?;
            Ok(())
        }),
        ("bandwidth", |encoder| {
            encoder.set_bandwidth(Bandwidth::Wideband)?;
            let _ = encoder.bandwidth()?;
            Ok(())
        }),
        ("force_channels", |encoder| {
            encoder.set_force_channels(Some(Channels::Mono))?;
            let _ = encoder.force_channels()?;
            Ok(())
        }),
        ("signal", |encoder| {
            encoder.set_signal(Signal::Music)?;
            let _ = encoder.signal()?;
            Ok(())
        }),
        ("lookahead", |encoder| {
            let _ = encoder.lookahead()?;
            Ok(())
        }),
        ("final_range", |encoder| {
            let _ = encoder.final_range()?;
            Ok(())
        }),
        ("lsb_depth", |encoder| {
            assert_eq!(encoder.set_lsb_depth(7), Err(Error::BadArg));
            encoder.set_lsb_depth(16)?;
            assert_eq!(encoder.lsb_depth()?, 16);
            Ok(())
        }),
        ("expert_frame_duration", |encoder| {
            encoder.set_expert_frame_duration(ExpertFrameDuration::Ms20)?;
            let _ = encoder.expert_frame_duration()?;
            Ok(())
        }),
        ("prediction_disabled", |encoder| {
            encoder.set_prediction_disabled(true)?;
            let _ = encoder.prediction_disabled()?;
            Ok(())
        }),
        ("phase_inversion", |encoder| {
            encoder.set_phase_inversion_disabled(true)?;
            let _ = encoder.phase_inversion_disabled()?;
            Ok(())
        }),
        ("reset", Encoder::reset),
    ];
    run_ctl_cases(&mut encoder, encoder_cases);

    let mut decoder = Decoder::new(SampleRate::Hz48000, Channels::Stereo).unwrap();
    let decoder_cases: &[CtlCase<Decoder>] = &[
        ("sample_rate", |decoder| {
            let _ = decoder.get_sample_rate()?;
            Ok(())
        }),
        ("pitch", |decoder| {
            let _ = decoder.get_pitch()?;
            Ok(())
        }),
        ("last_packet_duration", |decoder| {
            let _ = decoder.get_last_packet_duration()?;
            Ok(())
        }),
        ("final_range", |decoder| {
            let _ = decoder.final_range()?;
            Ok(())
        }),
        ("gain", |decoder| {
            decoder.set_gain(128)?;
            let _ = decoder.gain()?;
            Ok(())
        }),
        ("phase_inversion", |decoder| {
            decoder.set_phase_inversion_disabled(true)?;
            let _ = decoder.phase_inversion_disabled()?;
            Ok(())
        }),
        ("reset", Decoder::reset),
    ];
    run_ctl_cases(&mut decoder, decoder_cases);
}

#[test]
fn every_multistream_ctl_is_supported_by_bundled_opus() {
    let mapping_table = [0u8, 1u8];
    let mapping = Mapping {
        channels: 2,
        streams: 1,
        coupled_streams: 1,
        mapping: &mapping_table,
    };
    let mut encoder =
        MultistreamEncoder::new(SampleRate::Hz48000, Application::Audio, mapping).unwrap();
    let encoder_cases: &[CtlCase<MultistreamEncoder>] = &[
        ("final_range", |encoder| {
            let _ = encoder.final_range()?;
            Ok(())
        }),
        ("bitrate", |encoder| {
            encoder.set_bitrate(Bitrate::Custom(96_000))?;
            let _ = encoder.bitrate()?;
            Ok(())
        }),
        ("complexity", |encoder| {
            encoder.set_complexity(Complexity::new(5))?;
            let _ = encoder.complexity()?;
            Ok(())
        }),
        ("lsb_depth", |encoder| {
            encoder.set_lsb_depth(16)?;
            let _ = encoder.lsb_depth()?;
            Ok(())
        }),
        ("dtx", |encoder| {
            encoder.set_dtx(true)?;
            let _ = encoder.dtx()?;
            let _ = encoder.in_dtx()?;
            Ok(())
        }),
        ("inband_fec", |encoder| {
            encoder.set_inband_fec(true)?;
            let _ = encoder.inband_fec()?;
            Ok(())
        }),
        ("packet_loss", |encoder| {
            encoder.set_packet_loss_perc(10)?;
            let _ = encoder.packet_loss_perc()?;
            Ok(())
        }),
        ("vbr", |encoder| {
            encoder.set_vbr(true)?;
            let _ = encoder.vbr()?;
            Ok(())
        }),
        ("vbr_constraint", |encoder| {
            encoder.set_vbr_constraint(true)?;
            let _ = encoder.vbr_constraint()?;
            Ok(())
        }),
        ("max_bandwidth", |encoder| {
            encoder.set_max_bandwidth(Bandwidth::Wideband)?;
            let _ = encoder.max_bandwidth()?;
            Ok(())
        }),
        ("bandwidth", |encoder| {
            encoder.set_bandwidth(Bandwidth::Wideband)?;
            let _ = encoder.bandwidth()?;
            Ok(())
        }),
        ("force_channels", |encoder| {
            encoder.set_force_channels(Some(Channels::Mono))?;
            let _ = encoder.force_channels()?;
            Ok(())
        }),
        ("signal", |encoder| {
            encoder.set_signal(Signal::Music)?;
            let _ = encoder.signal()?;
            Ok(())
        }),
        ("lookahead", |encoder| {
            let _ = encoder.lookahead()?;
            Ok(())
        }),
        ("encoder_state", |encoder| {
            let ptr = unsafe { encoder.encoder_state_ptr(0)? };
            if ptr.is_null() {
                Err(Error::InternalError)
            } else {
                Ok(())
            }
        }),
        ("reset", MultistreamEncoder::reset),
    ];
    run_ctl_cases(&mut encoder, encoder_cases);

    let mut decoder = MultistreamDecoder::new(SampleRate::Hz48000, mapping).unwrap();
    let decoder_cases: &[CtlCase<MultistreamDecoder>] = &[
        ("final_range", |decoder| {
            let _ = decoder.final_range()?;
            Ok(())
        }),
        ("gain", |decoder| {
            decoder.set_gain(128)?;
            let _ = decoder.gain()?;
            Ok(())
        }),
        ("phase_inversion", |decoder| {
            decoder.set_phase_inversion_disabled(true)?;
            let _ = decoder.phase_inversion_disabled()?;
            Ok(())
        }),
        ("sample_rate", |decoder| {
            let _ = decoder.get_sample_rate()?;
            Ok(())
        }),
        ("pitch", |decoder| {
            let _ = decoder.get_pitch()?;
            Ok(())
        }),
        ("last_packet_duration", |decoder| {
            let _ = decoder.get_last_packet_duration()?;
            Ok(())
        }),
        ("decoder_state", |decoder| {
            let ptr = unsafe { decoder.decoder_state_ptr(0)? };
            if ptr.is_null() {
                Err(Error::InternalError)
            } else {
                Ok(())
            }
        }),
        ("reset", MultistreamDecoder::reset),
    ];
    run_ctl_cases(&mut decoder, decoder_cases);
}

#[test]
fn every_projection_encoder_ctl_is_supported_by_bundled_opus() {
    let mut encoder =
        ProjectionEncoder::new(SampleRate::Hz48000, 4, 3, Application::Audio).unwrap();
    let cases: &[CtlCase<ProjectionEncoder>] = &[
        ("bitrate", |encoder| {
            encoder.set_bitrate(Bitrate::Custom(128_000))?;
            let _ = encoder.bitrate()?;
            Ok(())
        }),
        ("demixing_matrix_size", |encoder| {
            let _ = encoder.demixing_matrix_size()?;
            Ok(())
        }),
        ("demixing_matrix_gain", |encoder| {
            let _ = encoder.demixing_matrix_gain()?;
            Ok(())
        }),
        ("demixing_matrix", |encoder| {
            let _ = encoder.demixing_matrix_bytes()?;
            Ok(())
        }),
    ];
    run_ctl_cases(&mut encoder, cases);
}

#[cfg(feature = "dred")]
#[test]
fn every_dred_ctl_uses_the_bundled_opus_signature() {
    use opus_codec::DredDecoder;

    let mut encoder =
        Encoder::new(SampleRate::Hz48000, Channels::Mono, Application::Audio).unwrap();
    encoder.set_dred_duration(2).unwrap();
    assert_eq!(encoder.dred_duration().unwrap(), 2);
    assert_eq!(
        unsafe { encoder.set_dnn_blob(std::ptr::null(), 0) },
        Err(Error::BadArg)
    );

    let mut decoder = Decoder::new(SampleRate::Hz48000, Channels::Mono).unwrap();
    assert_eq!(
        unsafe { decoder.set_dnn_blob(std::ptr::null(), 0) },
        Err(Error::BadArg)
    );

    let mut dred_decoder = DredDecoder::new().unwrap();
    assert_eq!(
        unsafe { dred_decoder.set_dnn_blob(&[]) },
        Err(Error::BadArg)
    );
}