Piston SDL window with Sound

With the 0.7.0 release of the sdl2 crate, it is now much easier to manage subsystem initialization across libraries. This means it's now very easy to set up audio through SDL when using the pistoncore-sdl2_window crate as a backend for the Piston game engine; the SDL context is not locked behind the backend in such a way that lifetimes get in the way of initializing other desired subsystems.

This was a problem of mine for a long time. But now, you can clone around Sdl and the subsystem structs, VideoSubsystem, AudioSubsystem, etc, and they all hold internal reference counters that sit on top of SDL's internal reference count. So let's use this to initialize AudioSubsystem after we have already created the window.

First, make sure you have all the necessary crates in your Cargo.toml:

sdl2 = "*"  
piston = "*"  
pistoncore-sdl2_window = "*"  

You have two options for creating your window. The first, and original method, calls sdl2::init internally, meaning that it will fail if SDL is initialized already. The second option is to initialize SDL yourself, and pass in the initialized VideoSubsystem.

extern crate sdl2;  
extern crate sdl2_window;

use sdl2::{Sdl, VideoSubsystem};  
use sdl2_window::Sdl2Window;  
use piston::window::WindowSettings;

fn create_window() -> Result<Sdl2Window, String> {  
    let sdl: Sdl = try!(sdl2::init());
    let video: VideoSubsystem = try!(sdl.video());
    try!(Sdl2Window::with_subsystem(video, WindowSettings::new("Hello world!", (640, 480))))

Calling that function, we'll get the Sdl2Window but not the Sdl just like if we called ::new, but thankfully now it's super easy to grab the Sdl and just make the AudioSubsystem in the calling code.

use piston::event_loop::WindowEvents;  
use piston::input::Event;

fn main() {  
    let window = Rc::new(RefCell::new(create_window().unwrap()));

    let audio = window.sdl_context.audio().unwrap();

    // create an audio device with the subsystem!

    for e in WindowEvents::new(window) {
        if let Some(r) = e.render_args() {}
        if let Some(u) = e.update_args() {}

    // It doesn't actually matter what order these are dropped
    // the internal refcount will safely quit dynamically

Fairly simple now! Before, you had to jump through absurd lifetime hurdles to grab the context out and make a new Builder and it was just a total mess. But this is much easier, and better preserves the lifetime expectations of the various structures in SDL2 (like SDL2_Window, which before the Rust wrapper around it was allowed to live longer than the video subsystem! Scary stuff...)

Enjoy producing sound alongside your Piston application!