use crate::Error;
pub const SILENT_CHANNEL: u8 = 255;
pub const MAX_CHANNELS: usize = 255;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum MappingType {
None,
Surround,
Ambisonics,
}
#[derive(Clone, PartialEq, Eq)]
pub struct ChannelLayout {
nb_channels: u8,
nb_streams: u8,
nb_coupled_streams: u8,
mapping: [u8; MAX_CHANNELS],
}
static VORBIS_MAPPINGS: [(u8, u8, [u8; 8]); 8] = [
(1, 0, [0, 0, 0, 0, 0, 0, 0, 0]), (1, 1, [0, 1, 0, 0, 0, 0, 0, 0]), (2, 1, [0, 2, 1, 0, 0, 0, 0, 0]), (2, 2, [0, 1, 2, 3, 0, 0, 0, 0]), (3, 2, [0, 4, 1, 2, 3, 0, 0, 0]), (4, 2, [0, 4, 1, 2, 3, 5, 0, 0]), (4, 3, [0, 4, 1, 2, 3, 5, 6, 0]), (5, 3, [0, 6, 1, 2, 3, 4, 5, 7]), ];
pub fn validate_ambisonics(channels: usize) -> Option<(usize, usize)> {
if !(1..=227).contains(&channels) {
return None;
}
let mut order_plus_one = 1;
while (order_plus_one + 1) * (order_plus_one + 1) <= channels {
order_plus_one += 1;
}
let acn_channels = order_plus_one * order_plus_one;
let nondiegetic_channels = channels - acn_channels;
if nondiegetic_channels != 0 && nondiegetic_channels != 2 {
return None;
}
let coupled_streams = (nondiegetic_channels != 0) as usize;
Some((acn_channels + coupled_streams, coupled_streams))
}
impl ChannelLayout {
pub fn new(
channels: usize,
streams: usize,
coupled_streams: usize,
mapping: &[u8],
) -> Result<Self, Error> {
if !(1..=MAX_CHANNELS).contains(&channels) {
return Err(Error::bad_arg("multistream layout: the channel count must be 1 to 255"));
}
if coupled_streams > streams {
return Err(Error::bad_arg("multistream layout: more coupled streams than streams"));
}
if streams < 1 {
return Err(Error::bad_arg("multistream layout: at least one stream is needed"));
}
let max_channel = streams.saturating_add(coupled_streams);
if max_channel > MAX_CHANNELS {
return Err(Error::bad_arg(
"multistream layout: streams plus coupled streams must be at most 255",
));
}
if mapping.len() < channels {
return Err(Error::bad_arg(
"multistream layout: the mapping has fewer entries than channels",
));
}
let mapping = &mapping[..channels];
if mapping
.iter()
.any(|&m| m as usize >= max_channel && m != SILENT_CHANNEL)
{
return Err(Error::bad_arg(
"multistream layout: a mapping entry names a stream channel that does not exist",
));
}
let mut stored = [0u8; MAX_CHANNELS];
stored[..channels].copy_from_slice(mapping);
Ok(Self {
nb_channels: channels as u8,
nb_streams: streams as u8,
nb_coupled_streams: coupled_streams as u8,
mapping: stored,
})
}
pub fn validate_for_encoder(&self) -> Result<(), Error> {
if self.streams() + self.coupled_streams() > self.channels() {
return Err(Error::bad_arg(
"multistream layout: an encoder cannot code more channels than it is given",
));
}
for s in 0..self.streams() {
if s < self.coupled_streams() {
if self.left_channel(s, None).is_none() {
return Err(Error::bad_arg(
"multistream layout: a coupled stream has no left input channel",
));
}
if self.right_channel(s, None).is_none() {
return Err(Error::bad_arg(
"multistream layout: a coupled stream has no right input channel",
));
}
} else if self.mono_channel(s, None).is_none() {
return Err(Error::bad_arg(
"multistream layout: a mono stream has no input channel",
));
}
}
Ok(())
}
pub fn surround(
channels: usize,
mapping_family: u8,
) -> Result<(Self, MappingType, Option<usize>), Error> {
if !(1..=MAX_CHANNELS).contains(&channels) {
return Err(Error::bad_arg("multistream layout: the channel count must be 1 to 255"));
}
let mut mapping = [0u8; MAX_CHANNELS];
let mut lfe_stream = None;
let (streams, coupled_streams) = match mapping_family {
0 => match channels {
1 => {
mapping[0] = 0;
(1, 0)
}
2 => {
mapping[0] = 0;
mapping[1] = 1;
(1, 1)
}
_ => {
return Err(Error::bad_arg(
"multistream layout: mapping family 0 has 1 or 2 channels",
));
}
},
1 if channels <= 8 => {
let (nb_streams, nb_coupled_streams, table) = VORBIS_MAPPINGS[channels - 1];
mapping[..channels].copy_from_slice(&table[..channels]);
if channels >= 6 {
lfe_stream = Some(nb_streams as usize - 1);
}
(nb_streams as usize, nb_coupled_streams as usize)
}
255 => {
for (i, m) in mapping[..channels].iter_mut().enumerate() {
*m = i as u8;
}
(channels, 0)
}
2 => {
let (streams, coupled_streams) = validate_ambisonics(channels)
.ok_or(Error::bad_arg("multistream layout: not an ambisonics channel count"))?;
for i in 0..streams - coupled_streams {
mapping[i] = (i + coupled_streams * 2) as u8;
}
for i in 0..coupled_streams * 2 {
mapping[i + streams - coupled_streams] = i as u8;
}
(streams, coupled_streams)
}
_ => {
return Err(Error::bad_arg(
"multistream layout: unsupported mapping family or channel count",
));
}
};
let mapping_type = if channels > 2 && mapping_family == 1 {
MappingType::Surround
} else if mapping_family == 2 {
MappingType::Ambisonics
} else {
MappingType::None
};
let layout = Self::new(channels, streams, coupled_streams, &mapping[..channels])?;
layout.validate_for_encoder()?;
Ok((layout, mapping_type, lfe_stream))
}
#[inline]
pub fn channels(&self) -> usize {
self.nb_channels as usize
}
#[inline]
pub fn streams(&self) -> usize {
self.nb_streams as usize
}
#[inline]
pub fn coupled_streams(&self) -> usize {
self.nb_coupled_streams as usize
}
#[inline]
pub fn mapping(&self) -> &[u8] {
&self.mapping[..self.nb_channels as usize]
}
#[inline]
fn find_channel(&self, index: usize, prev: Option<usize>) -> Option<usize> {
let start = match prev {
None => 0,
Some(p) => p.checked_add(1)?,
};
let mapping = self.mapping();
mapping
.get(start..)?
.iter()
.position(|&m| m as usize == index)
.map(|i| i + start)
}
#[inline]
pub fn left_channel(&self, stream: usize, prev: Option<usize>) -> Option<usize> {
if stream >= self.coupled_streams() {
return None;
}
self.find_channel(stream * 2, prev)
}
#[inline]
pub fn right_channel(&self, stream: usize, prev: Option<usize>) -> Option<usize> {
if stream >= self.coupled_streams() {
return None;
}
self.find_channel(stream * 2 + 1, prev)
}
#[inline]
pub fn mono_channel(&self, stream: usize, prev: Option<usize>) -> Option<usize> {
if stream < self.coupled_streams() || stream >= self.streams() {
return None;
}
self.find_channel(stream + self.coupled_streams(), prev)
}
}
#[derive(Clone)]
pub(crate) struct ChannelsByEntry {
channels: [u8; MAX_CHANNELS],
start: [u8; 257],
}
impl ChannelsByEntry {
pub(crate) fn new(layout: &ChannelLayout) -> Self {
let mapping = layout.mapping();
let mut start = [0u8; 257];
for &m in mapping {
start[m as usize + 1] += 1;
}
for m in 0..256 {
start[m + 1] += start[m];
}
let mut next = start;
let mut channels = [0u8; MAX_CHANNELS];
for (c, &m) in mapping.iter().enumerate() {
channels[next[m as usize] as usize] = c as u8;
next[m as usize] += 1;
}
Self { channels, start }
}
#[inline]
pub(crate) fn with_entry(&self, entry: usize) -> &[u8] {
match self.start.get(entry..).and_then(|s| s.get(..2)) {
Some(&[first, end]) => &self.channels[first as usize..end as usize],
_ => &[],
}
}
}
impl core::fmt::Debug for ChannelLayout {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("ChannelLayout")
.field("channels", &self.nb_channels)
.field("streams", &self.nb_streams)
.field("coupled_streams", &self.nb_coupled_streams)
.field("mapping", &self.mapping())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
fn surround(channels: usize, family: u8) -> (ChannelLayout, MappingType, Option<usize>) {
ChannelLayout::surround(channels, family).unwrap()
}
#[test]
fn family_1_table_matches_vorbis_mappings() {
#[allow(clippy::type_complexity)]
let rows: [(usize, usize, usize, &[u8], Option<usize>); 8] = [
(1, 1, 0, &[0], None),
(2, 1, 1, &[0, 1], None),
(3, 2, 1, &[0, 2, 1], None),
(4, 2, 2, &[0, 1, 2, 3], None),
(5, 3, 2, &[0, 4, 1, 2, 3], None),
(6, 4, 2, &[0, 4, 1, 2, 3, 5], Some(3)),
(7, 4, 3, &[0, 4, 1, 2, 3, 5, 6], Some(3)),
(8, 5, 3, &[0, 6, 1, 2, 3, 4, 5, 7], Some(4)),
];
for (channels, streams, coupled, mapping, lfe) in rows {
let (layout, mapping_type, lfe_stream) = surround(channels, 1);
assert_eq!(layout.channels(), channels);
assert_eq!(layout.streams(), streams, "{channels} channels");
assert_eq!(layout.coupled_streams(), coupled, "{channels} channels");
assert_eq!(layout.mapping(), mapping, "{channels} channels");
assert_eq!(lfe_stream, lfe, "{channels} channels");
let want_type = if channels > 2 {
MappingType::Surround
} else {
MappingType::None
};
assert_eq!(mapping_type, want_type, "{channels} channels");
assert_eq!(streams + coupled, channels);
}
assert!(ChannelLayout::surround(9, 1).is_err());
assert!(ChannelLayout::surround(0, 1).is_err());
}
#[test]
fn family_0_is_mono_or_stereo() {
let (mono, t, lfe) = surround(1, 0);
assert_eq!(
(mono.streams(), mono.coupled_streams(), mono.mapping()),
(1, 0, &[0u8][..])
);
assert_eq!((t, lfe), (MappingType::None, None));
let (stereo, t, lfe) = surround(2, 0);
assert_eq!(
(stereo.streams(), stereo.coupled_streams(), stereo.mapping()),
(1, 1, &[0u8, 1][..])
);
assert_eq!((t, lfe), (MappingType::None, None));
for channels in [0, 3, 4, 255, 256] {
assert!(
ChannelLayout::surround(channels, 0).is_err(),
"{channels} channels"
);
}
}
#[test]
fn family_255_is_one_mono_stream_per_channel() {
for channels in 1..=255usize {
let (layout, t, lfe) = surround(channels, 255);
assert_eq!(layout.streams(), channels);
assert_eq!(layout.coupled_streams(), 0);
assert!(
layout
.mapping()
.iter()
.enumerate()
.all(|(i, &m)| m as usize == i)
);
assert_eq!((t, lfe), (MappingType::None, None));
}
assert!(ChannelLayout::surround(0, 255).is_err());
assert!(ChannelLayout::surround(256, 255).is_err());
}
#[test]
fn family_2_ambisonics_layouts() {
let valid: [(usize, usize, usize, &[u8]); 8] = [
(1, 1, 0, &[0]),
(3, 2, 1, &[2, 0, 1]),
(4, 4, 0, &[0, 1, 2, 3]),
(6, 5, 1, &[2, 3, 4, 5, 0, 1]),
(9, 9, 0, &[0, 1, 2, 3, 4, 5, 6, 7, 8]),
(11, 10, 1, &[2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 1]),
(
16,
16,
0,
&[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
),
(
18,
17,
1,
&[2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 0, 1],
),
];
for (channels, streams, coupled, mapping) in valid {
let (layout, t, lfe) = surround(channels, 2);
assert_eq!(layout.streams(), streams, "{channels} channels");
assert_eq!(layout.coupled_streams(), coupled, "{channels} channels");
assert_eq!(layout.mapping(), mapping, "{channels} channels");
assert_eq!((t, lfe), (MappingType::Ambisonics, None));
}
for channels in [0, 2, 5, 7, 8, 10, 12, 15, 17, 228, 229, 255, 256] {
assert!(
ChannelLayout::surround(channels, 2).is_err(),
"{channels} channels"
);
}
assert_eq!(validate_ambisonics(225), Some((225, 0)));
assert_eq!(validate_ambisonics(227), Some((226, 1)));
assert_eq!(validate_ambisonics(226), None);
}
#[test]
fn validate_ambisonics_accepts_squares_and_squares_plus_two() {
let mut want = [None; 301];
for order_plus_one in 1..=17usize {
let acn = order_plus_one * order_plus_one;
if acn <= 227 {
want[acn] = Some((acn, 0));
}
if acn + 2 <= 227 {
want[acn + 2] = Some((acn + 1, 1));
}
}
for (channels, want) in want.iter().enumerate() {
assert_eq!(validate_ambisonics(channels), *want, "{channels} channels");
}
assert_eq!(want.iter().filter(|w| w.is_some()).count(), 30);
assert_eq!(validate_ambisonics(usize::MAX), None);
}
#[test]
fn other_families_are_rejected() {
for family in [3u8, 4, 100, 254] {
for channels in 1..=20 {
assert!(ChannelLayout::surround(channels, family).is_err());
}
}
}
#[test]
fn new_rejects_each_invalid_argument() {
let map = [0u8; 300];
assert!(ChannelLayout::new(0, 1, 0, &map).is_err());
assert!(ChannelLayout::new(256, 1, 0, &map).is_err());
assert!(ChannelLayout::new(255, 1, 0, &map).is_ok());
assert!(ChannelLayout::new(1, 0, 0, &map).is_err());
assert!(ChannelLayout::new(2, 1, 2, &map).is_err());
assert!(ChannelLayout::new(2, 1, 1, &map).is_ok());
assert!(ChannelLayout::new(2, 128, 128, &map).is_err());
assert!(ChannelLayout::new(2, 128, 127, &map).is_ok());
assert!(ChannelLayout::new(2, 255, 0, &map).is_ok());
assert!(ChannelLayout::new(2, 256, 0, &map).is_err());
assert!(ChannelLayout::new(2, usize::MAX, usize::MAX, &map).is_err());
assert!(ChannelLayout::new(3, 2, 1, &[0, 1]).is_err());
assert!(ChannelLayout::new(3, 2, 1, &[0, 1, 2]).is_ok());
assert!(ChannelLayout::new(3, 2, 1, &[0, 1, 3]).is_err());
assert!(ChannelLayout::new(3, 2, 1, &[0, 1, 254]).is_err());
assert!(ChannelLayout::new(3, 2, 1, &[0, 1, 255]).is_ok());
assert!(ChannelLayout::new(1, 1, 0, &[5]).is_err());
}
#[test]
fn extra_mapping_entries_are_ignored() {
let a = ChannelLayout::new(2, 1, 1, &[0, 1, 77, 78]).unwrap();
let b = ChannelLayout::new(2, 1, 1, &[0, 1]).unwrap();
assert_eq!(a, b);
assert_eq!(a.mapping(), &[0, 1]);
}
#[test]
fn encoder_checks() {
let l = ChannelLayout::new(2, 2, 1, &[0, 1]).unwrap();
assert!(l.validate_for_encoder().is_err());
let l = ChannelLayout::new(3, 2, 1, &[0, 1, 0]).unwrap();
assert!(l.validate_for_encoder().is_err());
let l = ChannelLayout::new(3, 2, 1, &[0, 2, 2]).unwrap();
assert!(l.validate_for_encoder().is_err());
let l = ChannelLayout::new(3, 2, 1, &[1, 2, 2]).unwrap();
assert!(l.validate_for_encoder().is_err());
let l = ChannelLayout::new(5, 2, 1, &[0, 1, 2, 255, 2]).unwrap();
assert!(l.validate_for_encoder().is_ok());
}
#[test]
fn routing_follows_the_mapping() {
let (l, _, _) = surround(6, 1);
assert_eq!(l.left_channel(0, None), Some(0));
assert_eq!(l.right_channel(0, None), Some(2));
assert_eq!(l.left_channel(1, None), Some(3));
assert_eq!(l.right_channel(1, None), Some(4));
assert_eq!(l.mono_channel(2, None), Some(1));
assert_eq!(l.mono_channel(3, None), Some(5));
assert_eq!(l.left_channel(0, Some(0)), None);
assert_eq!(l.mono_channel(3, Some(5)), None);
assert_eq!(l.left_channel(2, None), None);
assert_eq!(l.right_channel(3, None), None);
assert_eq!(l.mono_channel(0, None), None);
assert_eq!(l.mono_channel(4, None), None);
assert_eq!(l.left_channel(usize::MAX, None), None);
assert_eq!(l.mono_channel(usize::MAX, None), None);
}
#[test]
fn routing_with_repeated_mapping_values() {
let l = ChannelLayout::new(8, 2, 1, &[2, 0, 1, 0, 255, 2, 1, 2]).unwrap();
let all = |first: Option<usize>, next: &dyn Fn(Option<usize>) -> Option<usize>| {
let mut found = [usize::MAX; 8];
let mut n = 0;
let mut cur = first;
while let Some(c) = cur {
found[n] = c;
n += 1;
cur = next(Some(c));
}
(found, n)
};
let (left, n) = all(l.left_channel(0, None), &|p| l.left_channel(0, p));
assert_eq!(&left[..n], &[1, 3]);
let (right, n) = all(l.right_channel(0, None), &|p| l.right_channel(0, p));
assert_eq!(&right[..n], &[2, 6]);
let (mono, n) = all(l.mono_channel(1, None), &|p| l.mono_channel(1, p));
assert_eq!(&mono[..n], &[0, 5, 7]);
assert_eq!(l.mono_channel(1, Some(7)), None);
assert_eq!(l.mono_channel(1, Some(1000)), None);
assert_eq!(l.mono_channel(1, Some(usize::MAX)), None);
}
struct Rng(u32);
impl Rng {
fn below(&mut self, n: usize) -> usize {
self.0 = self.0.wrapping_mul(1664525).wrapping_add(1013904223);
(self.0 >> 8) as usize % n
}
}
fn c_lookup(mapping: &[u8], index: usize, prev: i32) -> i32 {
let mut i = if prev < 0 { 0 } else { prev as usize + 1 };
while i < mapping.len() {
if mapping[i] as usize == index {
return i as i32;
}
i += 1;
}
-1
}
#[test]
fn lookups_follow_the_c_loops_on_random_layouts() {
let as_c = |r: Option<usize>| r.map_or(-1, |c| c as i32);
let mut rng = Rng(0x5EED);
let mut layouts = 0;
let mut encoder_layouts = 0;
while layouts < 3000 {
let channels = if rng.below(20) == 0 {
1 + rng.below(255)
} else {
1 + rng.below(12)
};
let streams = 1 + rng.below(6);
let coupled = rng.below(streams + 1);
let decoded = streams + coupled;
let mut mapping = [0u8; 255];
for m in mapping[..channels].iter_mut() {
*m = if rng.below(8) == 0 {
SILENT_CHANNEL
} else {
rng.below(decoded) as u8
};
}
let mapping = &mapping[..channels];
let l = ChannelLayout::new(channels, streams, coupled, mapping).unwrap();
layouts += 1;
for s in 0..streams + 3 {
for prev in (0..channels + 2).map(Some).chain([None]) {
let c_prev = as_c(prev);
let (left, right, mono) = (
as_c(l.left_channel(s, prev)),
as_c(l.right_channel(s, prev)),
as_c(l.mono_channel(s, prev)),
);
if s < coupled {
assert_eq!(left, c_lookup(mapping, 2 * s, c_prev));
assert_eq!(right, c_lookup(mapping, 2 * s + 1, c_prev));
assert_eq!(mono, -1);
} else if s < streams {
assert_eq!(mono, c_lookup(mapping, s + coupled, c_prev));
assert_eq!((left, right), (-1, -1));
} else {
assert_eq!((left, right, mono), (-1, -1, -1));
}
}
}
let has_inputs = (0..streams).all(|s| {
if s < coupled {
c_lookup(mapping, 2 * s, -1) != -1 && c_lookup(mapping, 2 * s + 1, -1) != -1
} else {
c_lookup(mapping, s + coupled, -1) != -1
}
});
let want = decoded <= channels && has_inputs;
assert_eq!(l.validate_for_encoder().is_ok(), want, "{l:?}");
encoder_layouts += want as usize;
}
assert!((200..2800).contains(&encoder_layouts), "{encoder_layouts}");
}
#[test]
fn channels_by_entry_lists_what_the_lookups_find() {
let mut rng = Rng(0xC0FFEE);
for round in 0..2000 {
let channels = if round % 10 == 0 { 255 } else { 1 + rng.below(12) };
let streams = 1 + rng.below(6);
let coupled = rng.below(streams + 1);
let mut mapping = [0u8; 255];
for m in mapping[..channels].iter_mut() {
*m = if rng.below(8) == 0 {
SILENT_CHANNEL
} else {
rng.below(streams + coupled) as u8
};
}
let l = ChannelLayout::new(channels, streams, coupled, &mapping[..channels]).unwrap();
let by_entry = ChannelsByEntry::new(&l);
let mut total = 0;
for entry in 0..=255usize {
let mut want = Vec::new();
let mut prev = -1;
loop {
prev = c_lookup(l.mapping(), entry, prev);
if prev < 0 {
break;
}
want.push(prev as u8);
}
assert_eq!(by_entry.with_entry(entry), &want[..], "{l:?}, entry {entry}");
total += want.len();
}
assert_eq!(total, channels);
assert!(by_entry.with_entry(256).is_empty());
assert!(by_entry.with_entry(usize::MAX).is_empty());
}
}
#[test]
fn no_argument_makes_a_function_panic() {
const SIZES: [usize; 16] = [
0,
1,
2,
3,
8,
9,
127,
128,
227,
228,
254,
255,
256,
u32::MAX as usize,
usize::MAX - 1,
usize::MAX,
];
let mut rng = Rng(99);
let mut mapping = [0u8; 300];
let mut accepted = 0;
for _ in 0..100_000 {
let (channels, streams, coupled, other) = (
SIZES[rng.below(16)],
SIZES[rng.below(16)],
SIZES[rng.below(16)],
SIZES[rng.below(16)],
);
let len = [0usize, 1, 2, 254, 255, 256, 300][rng.below(7)];
for m in mapping[..len].iter_mut() {
*m = [0u8, 1, 2, 127, 253, 254, 255][rng.below(7)];
}
if let Ok(l) = ChannelLayout::new(channels, streams, coupled, &mapping[..len]) {
accepted += 1;
assert_eq!(
(l.channels(), l.streams(), l.coupled_streams()),
(channels, streams, coupled)
);
assert_eq!(l.mapping(), &mapping[..channels]);
let _ = l.validate_for_encoder();
let prev = [None, Some(other), Some(rng.below(300))][rng.below(3)];
for found in [
l.left_channel(other, prev),
l.right_channel(other, prev),
l.mono_channel(other, prev),
l.left_channel(rng.below(streams), prev),
l.right_channel(rng.below(streams), prev),
l.mono_channel(rng.below(streams), prev),
]
.into_iter()
.flatten()
{
assert!(found < channels && prev.is_none_or(|p| found > p));
assert_ne!(l.mapping()[found], SILENT_CHANNEL);
}
}
if let Ok((l, _, lfe)) = ChannelLayout::surround(channels, rng.below(256) as u8) {
assert_eq!(l.channels(), channels);
assert!(l.validate_for_encoder().is_ok());
assert!(lfe.is_none_or(|s| s >= l.coupled_streams() && s < l.streams()));
}
let _ = validate_ambisonics(channels);
}
assert!(accepted > 1000, "{accepted}");
}
#[test]
fn silent_channel_is_never_a_stream_channel() {
let mut map = [SILENT_CHANNEL; 255];
map[0] = 254;
let l = ChannelLayout::new(255, 128, 127, &map).unwrap();
assert_eq!(l.right_channel(127, None), None);
assert_eq!(l.mono_channel(127, None), Some(0));
assert_eq!(l.left_channel(126, None), None);
}
}