#![allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum AudioChannel {
FrontLeft,
FrontRight,
FrontCenter,
LowFrequency,
BackLeft,
BackRight,
SideLeft,
SideRight,
TopFrontLeft,
TopFrontRight,
}
impl AudioChannel {
#[must_use]
pub fn label(self) -> &'static str {
match self {
Self::FrontLeft => "FL",
Self::FrontRight => "FR",
Self::FrontCenter => "FC",
Self::LowFrequency => "LFE",
Self::BackLeft => "BL",
Self::BackRight => "BR",
Self::SideLeft => "SL",
Self::SideRight => "SR",
Self::TopFrontLeft => "TFL",
Self::TopFrontRight => "TFR",
}
}
#[must_use]
pub fn is_surround(self) -> bool {
matches!(
self,
Self::BackLeft
| Self::BackRight
| Self::SideLeft
| Self::SideRight
| Self::TopFrontLeft
| Self::TopFrontRight
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ChannelLayoutKind {
Mono,
Stereo,
Surround21,
Surround51,
Surround71,
Custom,
}
impl ChannelLayoutKind {
#[must_use]
pub fn channel_count(self) -> usize {
match self {
Self::Mono => 1,
Self::Stereo => 2,
Self::Surround21 => 3,
Self::Surround51 => 6,
Self::Surround71 => 8,
Self::Custom => 0,
}
}
#[must_use]
pub fn channels(self) -> &'static [AudioChannel] {
match self {
Self::Mono => &[AudioChannel::FrontCenter],
Self::Stereo => &[AudioChannel::FrontLeft, AudioChannel::FrontRight],
Self::Surround21 => &[
AudioChannel::FrontLeft,
AudioChannel::FrontRight,
AudioChannel::LowFrequency,
],
Self::Surround51 => &[
AudioChannel::FrontLeft,
AudioChannel::FrontRight,
AudioChannel::FrontCenter,
AudioChannel::LowFrequency,
AudioChannel::BackLeft,
AudioChannel::BackRight,
],
Self::Surround71 => &[
AudioChannel::FrontLeft,
AudioChannel::FrontRight,
AudioChannel::FrontCenter,
AudioChannel::LowFrequency,
AudioChannel::BackLeft,
AudioChannel::BackRight,
AudioChannel::SideLeft,
AudioChannel::SideRight,
],
Self::Custom => &[],
}
}
#[must_use]
pub fn has_lfe(self) -> bool {
self.channels().contains(&AudioChannel::LowFrequency)
}
}
#[derive(Debug, Clone)]
pub struct ChannelLayout {
pub kind: ChannelLayoutKind,
custom_channels: Vec<AudioChannel>,
}
impl ChannelLayout {
#[must_use]
pub fn standard(kind: ChannelLayoutKind) -> Self {
Self {
kind,
custom_channels: Vec::new(),
}
}
#[must_use]
pub fn custom(channels: Vec<AudioChannel>) -> Self {
Self {
kind: ChannelLayoutKind::Custom,
custom_channels: channels,
}
}
#[must_use]
pub fn channels(&self) -> &[AudioChannel] {
if self.kind == ChannelLayoutKind::Custom {
&self.custom_channels
} else {
self.kind.channels()
}
}
#[must_use]
pub fn channel_count(&self) -> usize {
self.channels().len()
}
#[must_use]
pub fn index_of(&self, ch: AudioChannel) -> Option<usize> {
self.channels().iter().position(|&c| c == ch)
}
#[must_use]
pub fn contains(&self, ch: AudioChannel) -> bool {
self.channels().contains(&ch)
}
#[must_use]
pub fn description(&self) -> String {
self.channels()
.iter()
.map(|c| c.label())
.collect::<Vec<_>>()
.join(", ")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_channel_labels() {
assert_eq!(AudioChannel::FrontLeft.label(), "FL");
assert_eq!(AudioChannel::LowFrequency.label(), "LFE");
assert_eq!(AudioChannel::SideRight.label(), "SR");
}
#[test]
fn test_is_surround_true() {
assert!(AudioChannel::BackLeft.is_surround());
assert!(AudioChannel::SideRight.is_surround());
assert!(AudioChannel::TopFrontLeft.is_surround());
}
#[test]
fn test_is_surround_false() {
assert!(!AudioChannel::FrontLeft.is_surround());
assert!(!AudioChannel::LowFrequency.is_surround());
}
#[test]
fn test_layout_kind_channel_count() {
assert_eq!(ChannelLayoutKind::Mono.channel_count(), 1);
assert_eq!(ChannelLayoutKind::Stereo.channel_count(), 2);
assert_eq!(ChannelLayoutKind::Surround51.channel_count(), 6);
assert_eq!(ChannelLayoutKind::Surround71.channel_count(), 8);
assert_eq!(ChannelLayoutKind::Custom.channel_count(), 0);
}
#[test]
fn test_has_lfe() {
assert!(ChannelLayoutKind::Surround51.has_lfe());
assert!(ChannelLayoutKind::Surround71.has_lfe());
assert!(!ChannelLayoutKind::Stereo.has_lfe());
assert!(!ChannelLayoutKind::Mono.has_lfe());
}
#[test]
fn test_standard_layout_count() {
let layout = ChannelLayout::standard(ChannelLayoutKind::Surround51);
assert_eq!(layout.channel_count(), 6);
}
#[test]
fn test_standard_layout_contains() {
let layout = ChannelLayout::standard(ChannelLayoutKind::Stereo);
assert!(layout.contains(AudioChannel::FrontLeft));
assert!(layout.contains(AudioChannel::FrontRight));
assert!(!layout.contains(AudioChannel::FrontCenter));
}
#[test]
fn test_index_of_found() {
let layout = ChannelLayout::standard(ChannelLayoutKind::Surround51);
assert_eq!(layout.index_of(AudioChannel::FrontLeft), Some(0));
assert_eq!(layout.index_of(AudioChannel::LowFrequency), Some(3));
assert_eq!(layout.index_of(AudioChannel::BackRight), Some(5));
}
#[test]
fn test_index_of_not_found() {
let layout = ChannelLayout::standard(ChannelLayoutKind::Stereo);
assert_eq!(layout.index_of(AudioChannel::FrontCenter), None);
}
#[test]
fn test_custom_layout() {
let layout = ChannelLayout::custom(vec![
AudioChannel::FrontLeft,
AudioChannel::FrontRight,
AudioChannel::TopFrontLeft,
AudioChannel::TopFrontRight,
]);
assert_eq!(layout.channel_count(), 4);
assert_eq!(layout.kind, ChannelLayoutKind::Custom);
assert!(layout.contains(AudioChannel::TopFrontLeft));
}
#[test]
fn test_description_stereo() {
let layout = ChannelLayout::standard(ChannelLayoutKind::Stereo);
assert_eq!(layout.description(), "FL, FR");
}
#[test]
fn test_description_mono() {
let layout = ChannelLayout::standard(ChannelLayoutKind::Mono);
assert_eq!(layout.description(), "FC");
}
#[test]
fn test_surround71_side_channels() {
let layout = ChannelLayout::standard(ChannelLayoutKind::Surround71);
assert!(layout.contains(AudioChannel::SideLeft));
assert!(layout.contains(AudioChannel::SideRight));
}
#[test]
fn test_surround21_has_lfe() {
let layout = ChannelLayout::standard(ChannelLayoutKind::Surround21);
assert!(layout.contains(AudioChannel::LowFrequency));
assert_eq!(layout.channel_count(), 3);
}
#[test]
fn test_custom_index_of() {
let layout = ChannelLayout::custom(vec![AudioChannel::SideLeft, AudioChannel::SideRight]);
assert_eq!(layout.index_of(AudioChannel::SideRight), Some(1));
assert_eq!(layout.index_of(AudioChannel::FrontLeft), None);
}
}