rust-portaudio icon indicating copy to clipboard operation
rust-portaudio copied to clipboard

No way to create a non-interleaved non-blocking stream

Open eeeeeta opened this issue 8 years ago • 2 comments

As far as I can see (although I may have missed something); there's no way to create a non-interleaved non-blocking stream with safe code (i.e. not transmute()ing the callback arg to &mut [&mut [f32]]). Am I correct here, and what could be done to resolve this problem?

I will add that providing data in interleaved form, in my use case, requires significant extra processing in the audio thread to do (loop through non-interleaved buffers, which must be preallocated & write to interleaved buffer).

eeeeeta avatar Jul 05 '16 10:07 eeeeeta

Workaround, for anyone else (warning: untested, probably very unsafe):

const CHANNELS: usize = 2;
let callback = move |pa::stream::OutputCallbackArgs { buffer, frames, .. }| {
    let buffr: &mut [*mut f32];
    unsafe {
        let buffer: *mut *mut f32 = ::std::mem::transmute(buffer.get_unchecked_mut(0));
        buffr = ::std::slice::from_raw_parts_mut(buffer, CHANNELS);
        let first_channel_buffer = ::std::slice::from_raw_parts_mut(buffr[0], frames);
        let second_channel_buffer = ::std::slice::from_raw_parts_mut(buffr[1], frames);
    }
    pa::Continue
};

eeeeeta avatar Jul 05 '16 10:07 eeeeeta

You're right, support for writing to non-interleaved buffers hasn't yet been added, though it would be a welcome addition!

Currently StreamParameters takes an is_interleaved bool at runtime (which is unhandled at the callback end as you've discovered). It would be nicer to have this moved to the type level so that the user receives errors about mis-use at compile time instead of runtime. I've been meaning to add something along the lines of a Buffer trait but haven't had time.

pub<S> trait Buffer<S> {
    fn is_interleaved() -> bool;
    // Other buffer handling methods.
}

impl<'a, S> Buffer<S> for [&'a [S]] {
    fn is_interleaved() -> bool {
        false
    }
    ...
}

impl<S> Buffer<S> for [S]  {
    fn is_interleaved() -> bool {
        true
    }
    ...
}

Something like this might allow the compiler to infer whether or not the buffer is interleaved by examining the buffer type used in the callback args at compile time. This way we might be able to get away with removing the need for a is_interleaved bool param altogether.

mitchmindtree avatar Jul 05 '16 10:07 mitchmindtree