dasp icon indicating copy to clipboard operation
dasp copied to clipboard

Add an example for working with signals with channels amount determined at runtime

Open MOZGIII opened this issue 5 years ago • 2 comments

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.

MOZGIII avatar Sep 06 '19 05:09 MOZGIII

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.

MOZGIII avatar Sep 08 '19 06:09 MOZGIII

#[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]");
        }
    }
}

MOZGIII avatar Sep 08 '19 07:09 MOZGIII