Playing music

We'll create a new Player structure to wrap the event loop. The player will be usable from the main thread to control the music. Here's the structure itself:

pub struct Player {
    app_state: Arc<Mutex<super::State>>,
    event_loop: EventLoop,
}

And here's the start of its constructor:

impl Player {
    pub(crate) fn new(app_state: Arc<Mutex<super::State>>) -> Self {
        let event_loop = EventLoop::new();

        {
            let app_state = app_state.clone();
            let event_loop = event_loop.clone();
            thread::spawn(move || {
                // …
            });
        }

        Player {
            app_state,
            event_loop,
        }
    }
}

We start by creating a new event loop. Then, we start a new thread. We used a new scope to avoid having to rename the variables that will be sent to the thread because these variables are used in the initialization of the structure at the end of the constructor. Again, we need to use a move closure because we're sending a copy of the event loop and the application state to the thread.

Let's see the first part of the thread's closure:

thread::spawn(move || {
    let mut buffer = [[0; 2]; BUFFER_SIZE];
    let mut playback = Playback::new("MP3", "MP3 Playback", None,
DEFAULT_RATE); let mut source = None; loop { if let Some(action) = event_loop.queue.try_pop() { match action { Load(path) => { let file = File::open(path).unwrap(); source = Some(Mp3Decoder::new(BufReader::new(file)).unwrap()); let rate = source.as_ref().map(|source|
source.samples_rate()).unwrap_or(DEFAULT_RATE); playback = Playback::new("MP3", "MP3 Playback",
None, rate); app_state.lock().unwrap().stopped = false; }, Stop => {}, } } // … } });

We start by creating a buffer to contain the samples to be played. Then we'll create a Playback, which is an object that will allow us to play music on the hardware. We'll also create a source variable that will contain an Mp3Decoder. We then start an infinite loop and try to get the first element in the queue: if there's an element in the queue, Some(action) is returned. That's why we used if let to pattern match against the result of this method call. We then match against the action to see which action it is: if it is a Load action, we open the file with the specified path and create an Mp3Decoder with a buffered reader of this file. We then try to get the sample rate of the song and create a new Playback with this rate. We'll handle the Stop action later.

Finally, we see our first use of Mutex:

app_state.lock().unwrap().stopped = false;

Let's rewrite it in another way to see what's going on:

let mut guard = app_state.lock().unwrap();
guard.stopped = false;

We first call lock(), which returns a Result<MutexGuard<T>, PoisonError<MutexGuard<T>>>.