use crate::{PcmContainer, level::Level};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EncoderConfig {
level: Level,
threads: usize,
block_size: u16,
block_schedule: Option<Vec<u16>>,
capture_fxmd: bool,
strict_fxmd_validation: bool,
}
impl Default for EncoderConfig {
fn default() -> Self {
let level = Level::Level8;
let profile = level.profile();
Self {
level,
threads: std::thread::available_parallelism()
.map(usize::from)
.unwrap_or(1),
block_size: profile.block_size,
block_schedule: None,
capture_fxmd: true,
strict_fxmd_validation: true,
}
}
}
impl EncoderConfig {
#[must_use]
pub fn builder() -> EncoderBuilder {
EncoderBuilder::default()
}
#[must_use]
pub fn level(&self) -> Level {
self.level
}
#[must_use]
pub fn threads(&self) -> usize {
self.threads
}
#[must_use]
pub fn block_size(&self) -> u16 {
self.block_size
}
#[must_use]
pub fn block_schedule(&self) -> Option<&[u16]> {
self.block_schedule.as_deref()
}
#[must_use]
pub fn capture_fxmd(&self) -> bool {
self.capture_fxmd
}
#[must_use]
pub fn strict_fxmd_validation(&self) -> bool {
self.strict_fxmd_validation
}
#[must_use]
pub fn with_level(mut self, level: Level) -> Self {
let profile = level.profile();
self.level = level;
self.block_size = profile.block_size;
self
}
#[must_use]
pub fn with_threads(mut self, threads: usize) -> Self {
self.threads = threads.max(1);
self
}
#[must_use]
pub fn with_block_size(mut self, block_size: u16) -> Self {
self.block_size = block_size;
self.block_schedule = None;
self
}
#[must_use]
pub fn with_block_schedule(mut self, block_schedule: Vec<u16>) -> Self {
self.block_schedule = Some(block_schedule);
self
}
#[must_use]
pub fn with_capture_fxmd(mut self, capture: bool) -> Self {
self.capture_fxmd = capture;
self
}
#[must_use]
pub fn with_strict_fxmd_validation(mut self, strict: bool) -> Self {
self.strict_fxmd_validation = strict;
self
}
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct EncoderBuilder {
config: EncoderConfig,
}
impl EncoderBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn level(mut self, level: Level) -> Self {
self.config = self.config.with_level(level);
self
}
#[must_use]
pub fn threads(mut self, threads: usize) -> Self {
self.config = self.config.with_threads(threads);
self
}
#[must_use]
pub fn block_size(mut self, block_size: u16) -> Self {
self.config = self.config.with_block_size(block_size);
self
}
#[must_use]
pub fn block_schedule(mut self, block_schedule: Vec<u16>) -> Self {
self.config = self.config.with_block_schedule(block_schedule);
self
}
#[must_use]
pub fn capture_fxmd(mut self, capture: bool) -> Self {
self.config = self.config.with_capture_fxmd(capture);
self
}
#[must_use]
pub fn strict_fxmd_validation(mut self, strict: bool) -> Self {
self.config = self.config.with_strict_fxmd_validation(strict);
self
}
#[must_use]
pub fn build(self) -> EncoderConfig {
self.config
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DecodeConfig {
threads: usize,
emit_fxmd: bool,
output_container: PcmContainer,
strict_channel_mask_provenance: bool,
strict_seektable_validation: bool,
}
impl Default for DecodeConfig {
fn default() -> Self {
Self {
threads: std::thread::available_parallelism()
.map(usize::from)
.unwrap_or(1),
emit_fxmd: true,
output_container: PcmContainer::Auto,
strict_channel_mask_provenance: false,
strict_seektable_validation: false,
}
}
}
impl DecodeConfig {
#[must_use]
pub fn builder() -> DecodeBuilder {
DecodeBuilder::default()
}
#[must_use]
pub fn threads(&self) -> usize {
self.threads
}
#[must_use]
pub fn emit_fxmd(&self) -> bool {
self.emit_fxmd
}
#[must_use]
pub fn output_container(&self) -> PcmContainer {
self.output_container
}
#[must_use]
pub fn strict_channel_mask_provenance(&self) -> bool {
self.strict_channel_mask_provenance
}
#[must_use]
pub fn strict_seektable_validation(&self) -> bool {
self.strict_seektable_validation
}
#[must_use]
pub fn with_threads(mut self, threads: usize) -> Self {
self.threads = threads.max(1);
self
}
#[must_use]
pub fn with_emit_fxmd(mut self, emit: bool) -> Self {
self.emit_fxmd = emit;
self
}
#[must_use]
pub fn with_output_container(mut self, output_container: PcmContainer) -> Self {
self.output_container = output_container;
self
}
#[must_use]
pub fn with_strict_channel_mask_provenance(mut self, strict: bool) -> Self {
self.strict_channel_mask_provenance = strict;
self
}
#[must_use]
pub fn with_strict_seektable_validation(mut self, strict: bool) -> Self {
self.strict_seektable_validation = strict;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct DecodeBuilder {
config: DecodeConfig,
}
impl DecodeBuilder {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn threads(mut self, threads: usize) -> Self {
self.config = self.config.with_threads(threads);
self
}
#[must_use]
pub fn emit_fxmd(mut self, emit: bool) -> Self {
self.config = self.config.with_emit_fxmd(emit);
self
}
#[must_use]
pub fn output_container(mut self, output_container: PcmContainer) -> Self {
self.config = self.config.with_output_container(output_container);
self
}
#[must_use]
pub fn strict_channel_mask_provenance(mut self, strict: bool) -> Self {
self.config = self.config.with_strict_channel_mask_provenance(strict);
self
}
#[must_use]
pub fn strict_seektable_validation(mut self, strict: bool) -> Self {
self.config = self.config.with_strict_seektable_validation(strict);
self
}
#[must_use]
pub fn build(self) -> DecodeConfig {
self.config
}
}
#[cfg(test)]
mod tests {
use super::{DecodeConfig, EncoderConfig};
use crate::{PcmContainer, level::Level};
#[test]
fn with_threads_clamps_to_one() {
assert_eq!(EncoderConfig::default().with_threads(0).threads(), 1);
}
#[test]
fn with_level_resets_block_size_to_level_default() {
let config = EncoderConfig::default()
.with_block_size(576)
.with_level(Level::Level6);
assert_eq!(config.block_size(), Level::Level6.profile().block_size);
}
#[test]
fn builder_matches_fluent_config() {
let built = EncoderConfig::builder()
.level(Level::Level4)
.threads(2)
.block_size(1024)
.capture_fxmd(false)
.strict_fxmd_validation(false)
.build();
assert_eq!(
built,
EncoderConfig::default()
.with_level(Level::Level4)
.with_threads(2)
.with_block_size(1024)
.with_capture_fxmd(false)
.with_strict_fxmd_validation(false)
);
}
#[test]
fn with_block_schedule_enables_variable_mode() {
let schedule = vec![576, 1152, 576];
let config = EncoderConfig::default().with_block_schedule(schedule.clone());
assert_eq!(config.block_schedule(), Some(schedule.as_slice()));
}
#[test]
fn with_block_size_clears_block_schedule() {
let config = EncoderConfig::default()
.with_block_schedule(vec![576, 1152])
.with_block_size(1024);
assert_eq!(config.block_schedule(), None);
assert_eq!(config.block_size(), 1024);
}
#[test]
fn builder_supports_block_schedule() {
let schedule = vec![576, 1152, 576];
let built = EncoderConfig::builder()
.level(Level::Level4)
.threads(2)
.block_schedule(schedule.clone())
.build();
assert_eq!(
built,
EncoderConfig::default()
.with_level(Level::Level4)
.with_threads(2)
.with_block_schedule(schedule)
);
}
#[test]
fn decode_with_threads_clamps_to_one() {
assert_eq!(DecodeConfig::default().with_threads(0).threads(), 1);
}
#[test]
fn decode_builder_matches_fluent_config() {
let built = DecodeConfig::builder()
.threads(4)
.emit_fxmd(false)
.output_container(PcmContainer::Wave64)
.strict_channel_mask_provenance(true)
.strict_seektable_validation(true)
.build();
assert_eq!(built.threads(), 4);
assert!(!built.emit_fxmd());
assert_eq!(built.output_container(), PcmContainer::Wave64);
assert!(built.strict_channel_mask_provenance());
assert!(built.strict_seektable_validation());
}
#[test]
fn encoder_default_preserves_fxmd_with_strict_validation() {
let config = EncoderConfig::default();
assert!(config.capture_fxmd());
assert!(config.strict_fxmd_validation());
}
#[test]
fn decode_default_emits_fxmd_without_extra_validation() {
let config = DecodeConfig::default();
assert!(config.emit_fxmd());
assert_eq!(config.output_container(), PcmContainer::Auto);
assert!(!config.strict_channel_mask_provenance());
assert!(!config.strict_seektable_validation());
}
}