libxm 1.1.1

A binding of libxm for Rust. A small XM (FastTracker II Extended Module) player library. Designed for easy integration in demos and such, and provides timing functions for easy sync against specific instruments, samples or channels.
Documentation
use getopts::Options;
use libxm::XMContext;
use sdl2::audio::{AudioCallback, AudioDevice, AudioSpecDesired};
use std::fs::File;
use std::io::Read;
use std::env;
use std::sync::mpsc::Sender;

struct MyCallback {
    xm: XMContext,
    last_loop_count: u8,
    loop_tx: Sender<()>
}

impl AudioCallback for MyCallback {
    type Channel = f32;

    fn callback(&mut self, out: &mut [f32]) {
        self.xm.generate_samples(out);

        let loop_count = self.xm.loop_count();

        if loop_count != self.last_loop_count {
            self.last_loop_count = loop_count;

            // Signal the driver that a loop has occured
            self.loop_tx.send(()).unwrap();
        }
    }
}

fn play_audio(contents: &[u8], rate: u32, max_loops: u8) {
    use std::sync::mpsc::channel;

    let sdl = sdl2::init().unwrap();
    let audio = sdl.audio().unwrap();

    let (loop_tx, loop_rx) = channel();

    let desired_spec = AudioSpecDesired {
        freq: Some(rate as i32),
        channels: Some(2),
        samples: None
    };

    let device = AudioDevice::open_playback(&audio, None, &desired_spec, |spec| {
        let mut xm = XMContext::new(&contents, spec.freq as u32).unwrap();
        xm.set_max_loop_count(max_loops);

        if let Some(module_name) = xm.module_name() {
            println!("Module name: {}", String::from_utf8_lossy(module_name));
        }
        if let Some(tracker_name) = xm.tracker_name() {
            println!("Tracker: {}", String::from_utf8_lossy(tracker_name));
        }
        println!("Channels: {}", xm.number_of_channels());
        println!("Module length: {}", xm.module_length());
        println!("Patterns: {}", xm.number_of_patterns());
        println!("Instruments: {}", xm.number_of_instruments());

        MyCallback {
            xm: xm,
            last_loop_count: 0,
            loop_tx: loop_tx
        }
    }).unwrap();

    device.resume();

    for _ in 0..max_loops {
        // Block until the song has looped
        loop_rx.recv().unwrap();
    }
}


fn print_usage(program: &str, opts: Options) {
    let brief = format!("Usage: {} [options] FILE", program);
    print!("{}", opts.usage(&brief));
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let program = args[0].clone();

    let mut opts = Options::new();
    opts.optopt("r", "rate", "Set the output rate", "RATE");
    opts.optopt("l", "loops", "Set the maximum number of loops", "LOOPS");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => { m }
        Err(f) => { panic!("{:?}", f) }
    };

    let input = if !matches.free.is_empty() {
        matches.free[0].clone()
    } else {
        print_usage(&program, opts);
        return;
    };

    let rate = match matches.opt_str("r") {
        Some(s) => s.parse().unwrap(),
        None => 48000
    };

    let max_loops = match matches.opt_str("l") {
        Some(s) => s.parse().unwrap(),
        None => 1
    };

    let mut contents = Vec::new();
    File::open(&input).unwrap().read_to_end(&mut contents).unwrap();

    play_audio(&contents, rate, max_loops);
}