#![allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ChannelLabel {
FrontLeft,
FrontRight,
Center,
Lfe,
RearLeft,
RearRight,
SideLeft,
SideRight,
TopCenter,
Mono,
Unknown,
}
impl ChannelLabel {
pub fn is_lfe(self) -> bool {
self == ChannelLabel::Lfe
}
pub fn short_name(self) -> &'static str {
match self {
ChannelLabel::FrontLeft => "FL",
ChannelLabel::FrontRight => "FR",
ChannelLabel::Center => "C",
ChannelLabel::Lfe => "LFE",
ChannelLabel::RearLeft => "RL",
ChannelLabel::RearRight => "RR",
ChannelLabel::SideLeft => "SL",
ChannelLabel::SideRight => "SR",
ChannelLabel::TopCenter => "TC",
ChannelLabel::Mono => "M",
ChannelLabel::Unknown => "?",
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AudioChannelLayout {
labels: Vec<ChannelLabel>,
}
impl AudioChannelLayout {
pub fn new(labels: Vec<ChannelLabel>) -> Self {
Self { labels }
}
pub fn mono() -> Self {
Self::new(vec![ChannelLabel::Mono])
}
pub fn stereo() -> Self {
Self::new(vec![ChannelLabel::FrontLeft, ChannelLabel::FrontRight])
}
pub fn surround_5_1() -> Self {
Self::new(vec![
ChannelLabel::FrontLeft,
ChannelLabel::FrontRight,
ChannelLabel::Center,
ChannelLabel::Lfe,
ChannelLabel::RearLeft,
ChannelLabel::RearRight,
])
}
pub fn surround_7_1() -> Self {
Self::new(vec![
ChannelLabel::FrontLeft,
ChannelLabel::FrontRight,
ChannelLabel::Center,
ChannelLabel::Lfe,
ChannelLabel::RearLeft,
ChannelLabel::RearRight,
ChannelLabel::SideLeft,
ChannelLabel::SideRight,
])
}
pub fn channel_count(&self) -> usize {
self.labels.len()
}
pub fn has_lfe(&self) -> bool {
self.labels.iter().any(|l| l.is_lfe())
}
pub fn labels(&self) -> &[ChannelLabel] {
&self.labels
}
pub fn index_of(&self, label: ChannelLabel) -> Option<usize> {
self.labels.iter().position(|&l| l == label)
}
}
pub struct ChannelConverter {
src: AudioChannelLayout,
dst: AudioChannelLayout,
}
impl ChannelConverter {
pub fn new(src: AudioChannelLayout, dst: AudioChannelLayout) -> Self {
Self { src, dst }
}
pub fn remap(&self, input: &[f32]) -> Vec<f32> {
let src_ch = self.src.channel_count();
if src_ch == 0 {
return Vec::new();
}
let frames = input.len() / src_ch;
let dst_ch = self.dst.channel_count();
let mut output = vec![0.0_f32; frames * dst_ch];
for (dst_idx, dst_label) in self.dst.labels().iter().enumerate() {
if let Some(src_idx) = self.src.index_of(*dst_label) {
for fr in 0..frames {
output[fr * dst_ch + dst_idx] = input[fr * src_ch + src_idx];
}
}
}
output
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_channel_label_is_lfe_true() {
assert!(ChannelLabel::Lfe.is_lfe());
}
#[test]
fn test_channel_label_is_lfe_false() {
assert!(!ChannelLabel::FrontLeft.is_lfe());
assert!(!ChannelLabel::Center.is_lfe());
}
#[test]
fn test_channel_label_short_name() {
assert_eq!(ChannelLabel::FrontLeft.short_name(), "FL");
assert_eq!(ChannelLabel::Lfe.short_name(), "LFE");
assert_eq!(ChannelLabel::Center.short_name(), "C");
}
#[test]
fn test_mono_channel_count() {
assert_eq!(AudioChannelLayout::mono().channel_count(), 1);
}
#[test]
fn test_stereo_channel_count() {
assert_eq!(AudioChannelLayout::stereo().channel_count(), 2);
}
#[test]
fn test_5_1_channel_count() {
assert_eq!(AudioChannelLayout::surround_5_1().channel_count(), 6);
}
#[test]
fn test_7_1_channel_count() {
assert_eq!(AudioChannelLayout::surround_7_1().channel_count(), 8);
}
#[test]
fn test_stereo_has_no_lfe() {
assert!(!AudioChannelLayout::stereo().has_lfe());
}
#[test]
fn test_5_1_has_lfe() {
assert!(AudioChannelLayout::surround_5_1().has_lfe());
}
#[test]
fn test_index_of_existing_label() {
let layout = AudioChannelLayout::stereo();
assert_eq!(layout.index_of(ChannelLabel::FrontLeft), Some(0));
assert_eq!(layout.index_of(ChannelLabel::FrontRight), Some(1));
}
#[test]
fn test_index_of_missing_label() {
let layout = AudioChannelLayout::stereo();
assert_eq!(layout.index_of(ChannelLabel::Lfe), None);
}
#[test]
fn test_converter_stereo_passthrough() {
let src = AudioChannelLayout::stereo();
let dst = AudioChannelLayout::stereo();
let conv = ChannelConverter::new(src, dst);
let input = vec![1.0_f32, 2.0, 3.0, 4.0]; let output = conv.remap(&input);
assert_eq!(output, input);
}
#[test]
fn test_converter_stereo_to_mono_silences_center() {
let src = AudioChannelLayout::stereo();
let dst = AudioChannelLayout::mono();
let conv = ChannelConverter::new(src, dst);
let input = vec![0.5_f32, 0.5, 0.5, 0.5];
let output = conv.remap(&input);
assert_eq!(output.len(), 2); for s in &output {
assert!((*s).abs() < 1e-6);
}
}
#[test]
fn test_converter_5_1_extracts_center() {
let src = AudioChannelLayout::surround_5_1();
let dst = AudioChannelLayout::new(vec![ChannelLabel::Center]);
let conv = ChannelConverter::new(src, dst);
let input = vec![0.0_f32, 0.0, 0.9, 0.0, 0.0, 0.0];
let output = conv.remap(&input);
assert_eq!(output.len(), 1);
assert!((output[0] - 0.9).abs() < 1e-6);
}
}