dasp
dasp copied to clipboard
Add an example for working with signals with channels amount determined at runtime
I'm having serious difficulties with making my code work with frames with dynamic amount of channels (i.e. determined at runtime). When audio engine is initialized in my app, the amount of channels I have to work with is reported by the system. I then want to resample the signal, and when I'm trying to work with interleaved samples buffer I have to specify the frame type.
Surely, in theory, it's not required for the amount of channels to be known at compile type as a type parameter - it's enough for the Frame::zip_map
to just function.
I'd like to know how I'm supposed to use the currently present API to handle my case. This problem must be very wide-spread, since in the real world we almost never hardcode the apps to work with just a predetermined amount of channels, and I must me missing something really simple.
Figured out that samples
create should provide a macro to do the compiler job for now and implement runtime branching function (to switch based on the channels amount at runtime) and to provide some fn impls for all the 32 supported frame sizes. It's ugly and manual work, though it seems to be the most logical and less-intrusive way.
#[macro_export]
macro_rules! match_channels_explicit {
($frame:ident => [$channels:ident] => [$($N:expr)*] => $body:expr) => {
match $channels {
$(
$N => {
type $frame<S> = [S; $N];
$body
}
)*
_ => panic!("unsupported amount of channels"),
}
};
}
/// Go from channels number to Frame form at runtime.
///
/// Example:
///
/// ```
/// let channels = 1;
///
/// let val = match_channels! {
/// F => [channels] => {
/// format!("{:?}", F::<f32>::equilibrium())
/// }
/// };
///
/// assert_eq!(val, "[0.0]");
/// ```
#[macro_export]
macro_rules! match_channels {
($frame:ident => [$channels:ident] => $body:expr) => {
crate::match_channels_explicit! { $frame => [$channels] => [
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
27 28 29 30 31 32
] => $body }
};
}
#[cfg(test)]
mod tests {
use sample::Frame;
#[test]
fn test_1() {
let channels = 1;
let val = match_channels! {
F => [channels] => {
format!("{:?}", F::<f32>::equilibrium())
}
};
assert_eq!(val, "[0.0]");
}
#[test]
fn test_2() {
let channels = 2;
let val = match_channels! {
F => [channels] => {
format!("{:?}", F::<f32>::equilibrium())
}
};
assert_eq!(val, "[0.0, 0.0]");
}
#[test]
fn test_3() {
let channels = 3;
let val = match_channels! {
F => [channels] => {
format!("{:?}", F::<i16>::equilibrium())
}
};
assert_eq!(val, "[0, 0, 0]");
}
mod generic {
use sample::Sample;
use std::marker::PhantomData;
struct MyGenericType<S: Sample> {
s: PhantomData<S>,
channels: usize,
}
impl<S: Sample + std::fmt::Debug> MyGenericType<S> {
pub fn new(channels: usize) -> Self {
Self {
s: PhantomData,
channels,
}
}
pub fn frame_string(&mut self) -> String {
let channels = self.channels;
match_channels! {
F => [channels] => {
use sample::Frame;
format!("{:?}", F::<S>::equilibrium())
}
}
}
}
#[test]
fn test_generic_1() {
let mut s = MyGenericType::<f32>::new(1);
assert_eq!(s.frame_string(), "[0.0]");
}
#[test]
fn test_generic_2() {
let mut s = MyGenericType::<f32>::new(2);
assert_eq!(s.frame_string(), "[0.0, 0.0]");
}
#[test]
fn test_generic_3() {
let mut s = MyGenericType::<i8>::new(3);
assert_eq!(s.frame_string(), "[0, 0, 0]");
}
}
}