RAII

But how does it work behind the scene? Rust uses the idiom of Resource Acquisition Is Initialization(RAII) for short. With this idiom, a resource is allocated in the constructor and released in its destructor. In Rust, destructors are implemented by the Drop trait. So, to get back to mutex guards, the mutex is unlocked when the destructor of MutexGuard is called, so, as in the previous example, when the guard variable goes out of scope.

Let's get back to our infinite loop:

loop {
    if let Some(action) = event_loop.queue.try_pop() {
        // …
    } else if *event_loop.playing.lock().unwrap() {
        let mut written = false;
        if let Some(ref mut source) = source {
            let size = iter_to_buffer(source, &mut buffer);
            if size > 0 {
                playback.write(&buffer[..size]);
                written = true;
            }
        }

        if !written {
            app_state.lock().unwrap().stopped = true;
            *event_loop.playing.lock().unwrap() = false;
            source = None;
        }
    }
}

Here, we check whether the playing value is true (again using the lock().unwrap() trick). We must use a * to access the value of a MutexGuard because it implements Deref. That means we don't have direct access to the underlying value. But since it implements the Deref trait, we can access it by dereferencing the guard (with a *). We didn't need this trick before because we accessed a field and Rust automatically dereferences fields.

We then create a written variable that will be true if the player was able to play a sample. If it was unable to play one, this means the song came to an end. In this case, we set the stopped value to true and playing to false.

To play the samples, we call iter_to_buffer, which will take the value from the decoder (which is an Iterator) and write them to the buffer. Afterward, it will write the buffer to the playback in order to play the samples on your sound card.

Let's look at this iter_to_buffer function:

fn iter_to_buffer<I: Iterator<Item=i16>>(iter: &mut I, buffer: &mut [[i16; 2]; BUFFER_SIZE]) -> usize {
    let mut iter = iter.take(BUFFER_SIZE);
    let mut index = 0;
    while let Some(sample1) = iter.next() {
        if let Some(sample2) = iter.next() {
            buffer[index][0] = sample1;
            buffer[index][1] = sample2;
        }
        index += 1;
    }
    index
}

We start by taking BUFFER_SIZE elements from the iterator and add them to the buffer two at a time (for two channels). We then return the number of elements written to the buffer.