RustAudio / cpal

Cross-platform audio I/O library in pure Rust
Apache License 2.0
2.53k stars 338 forks source link

Handling different audio data layouts (interleaved / non-interleaved) #367

Open mitchmindtree opened 4 years ago

mitchmindtree commented 4 years ago

Currently CPAL's stream data callback API allows the user to assume that audio data will be provided with channels interleaved in a single slice of memory. There are some issues with this:

We should update CPAL's API guided by the following broad goals:

I don't have a solid proposal in mind yet, though thought I'd open this so that we have a place to discuss.

Ralith commented 4 years ago

Straw proposal:

pub struct Format {
    pub channels: ChannelCount,
    pub sample_rate: SampleRate,
    pub data_type: SampleFormat,
    pub interleaved: bool,
}

impl Data {
    pub fn as_slice<T>(&self) -> Option<&[&[T]]> { ... }
}
mitchmindtree commented 4 years ago

I like this as a nice simple solution for Data and the build_input/output_stream_raw methods.

I wonder how we should handle this in the build_input/output_stream methods?

Perhaps rather than taking &[&[T]] and requiring the user must check the layout on every call, we take a similar approach to #119 and require specifying the layout as a type. I'm imagining |data: &Interleaved<T>| ... where Interleaved<T> derefs to &[T], and NonInterleaved<T> derefs to &[&[T]].

Ralith commented 4 years ago

I don't think that materially impacts the amount of checking the user has to do in the deinterleaved case. It's low-friction to access channel indices up to the number you requested, which are guaranteed to be there if the create call succeeded. It's definitely a little nicer for the interleaved case to not have to sprinkle [0] everywhere, though, and embedding that flag into the type system might improve readability. Conversely, having to look up what those wrappers are, learn about their Deref impls, and import/qualify them at every use, increases friction, so I'm not sure.

What I'd really like is [&[T]; N], but of course we can't have that until const generics (unless there's a modest upper bound on the number of channels? Supporting crazy professional DAW setups with boatloads of channels is cool and might preclude this, but there's always the dynamic API...).

thavlik commented 4 years ago

Has any additional conceptual progress been made on this? I'm looking to implement the functionality, so it make sense to do it as a PR.