Chunks, lifetime not long enough, and closure


#1

Hi,

I’m trying to play an audio file (which I open with sndfile) with portaudio.

I first load the whole audio file as a vec of samples.
Then, I have to create an audio callback for portaudio that periodically writes audio samples into the output buffer.
Hence, I use chunks() on the input samples and then copy the samples into the output buffer (with clone_from_slice).

Here is a minimal example (without the dependencies to portaudio, sndfile) https://play.rust-lang.org/?gist=96b38d86a2b6db15329212ebaa1c78fc&version=stable&backtrace=0

use std::thread;

fn main() {
    let data = vec![1, 2, 3, 4, 5, 6, 8, 9, 10, 34, 56];
    
    let mut it = data.chunks(2);
    
    let mut audio_callback = move || {
         if let Some(mut v) = it.next() {
            println!("{:?}", v);
            Some(v)
        }
        else {
            None
        }
    };

    //In the actual code, I use  open_non_blocking_stream
   // which creates an audio thread and calls the audio callback regurlarly
    let handle = thread::spawn( move || {
        while let Some(v) = audio_callback() {
        }
    });
    //No join on a portaudio stream but I can close the stream to terminate it
    handle.join().unwrap();
}

with the following errors:

<anon>:7:18: 7:22 error: `data` does not live long enough
<anon>:7     let mut it = data.chunks(2);
                          ^~~~
note: reference must be valid for the static lifetime...
<anon>:5:57: 28:2 note: ...but borrowed value is only valid for the block suffix following statement 0 at 5:56

I think I understand the error: chunks takes &self as argument and so only borrows data and data only lives until the end of the main function, whereas the thread could live longer (it’s not the case in practice because of the join). The code would work with something like into_iter but there is no version of chunks that would take ownership.
I’m still quite a beginner in rust and I can’t find a solution…


#2

the thread could live longer (it’s not the case in practice because of the join

Your analysis is correct and the solution would be to use a thread which is guaranteed to finish before main. There is no such function in the standard library, so you need to use corssbeam crate to do this.


#3

Yes, but here, I can’t use that because I have no control over the creation of the thread : portaudio creates itself a thread and then use the callback I provide to it.

The code to create the audiostream with portaudio usually looks like that (official example):

In my case, I also load the audio file with sndfile before and use this chunks method.


#4

Ah, sorry for not reading your question properly. It’s an interesting one! I don’t know how to solve it with chunks :slight_smile: I would just roll my own chunks then, like this: https://is.gd/A1CDKR.

Another problematic piece of code is Some(v), because it returns a reference from a callback. However, the open_non_blocking_stream has a 'static bound on the result type anyway…


#5

The handmade chunks work very well, indeed. Thank you!