Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
MOZGIII opened this issue Sep 6, 2019 · 2 comments

Comments

@MOZGIII
Copy link

MOZGIII commented Sep 6, 2019

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
Copy link
Author

MOZGIII commented Sep 8, 2019

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
Copy link
Author

MOZGIII commented Sep 8, 2019

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant