sfml-modstream 0.3.0

Custom SFML SoundStream for playing modules
Documentation
extern crate openmpt_sys;
extern crate sfml;

use crate::string::OpenMptString;
use openmpt_sys::*;
use sfml::audio::SoundStream;
use sfml::system::Time;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::path::Path;
use std::ptr;

mod string;

pub struct ModStream {
    module: *mut openmpt_module,
    buffer: [i16; 2048],
}

impl ModStream {
    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, io::Error> {
        let mut file = File::open(path)?;
        let mut buf = Vec::new();
        file.read_to_end(&mut buf)?;
        Ok(Self::from_memory(&buf))
    }
    pub fn from_memory(data: &[u8]) -> Self {
        unsafe {
            let module = openmpt_module_create_from_memory(
                data.as_ptr() as *const _,
                data.len(),
                None,
                ptr::null_mut(),
                ptr::null(),
            );
            if module.is_null() {
                panic!("Failed load module");
            }
            Self {
                module,
                buffer: [0; 2048],
            }
        }
    }
    pub fn get_duration_seconds(&self) -> f64 {
        unsafe { openmpt_module_get_duration_seconds(self.module) }
    }
    pub fn metadata<'a, K: Key<'a>>(&self, key: K) -> Option<OpenMptString> {
        let key = std::ffi::CString::new(key.into_str()).unwrap();
        let ret = unsafe { openmpt_module_get_metadata(self.module, key.as_ptr()) };
        OpenMptString::new(ret)
    }
}

pub trait Key<'a> {
    fn into_str(self) -> &'a str;
}

impl<'a> Key<'a> for &'a str {
    fn into_str(self) -> &'a str {
        self
    }
}

pub enum Metadata {
    Tracker,
    Artist,
    Title,
    Date,
    Message,
}

impl Key<'static> for Metadata {
    fn into_str(self) -> &'static str {
        match self {
            Self::Tracker => "tracker",
            Self::Artist => "artist",
            Self::Title => "title",
            Self::Date => "date",
            Self::Message => "message",
        }
    }
}

impl SoundStream for ModStream {
    fn get_data(&mut self) -> (&mut [i16], bool) {
        unsafe {
            let keep_playing = openmpt_module_read_interleaved_stereo(
                self.module,
                44_100,
                1024,
                self.buffer.as_mut_ptr(),
            ) != 0;
            (&mut self.buffer[..], keep_playing)
        }
    }
    fn seek(&mut self, offset: Time) {
        unsafe {
            openmpt_module_set_position_seconds(self.module, f64::from(offset.as_seconds()));
        }
    }
    fn sample_rate(&self) -> u32 {
        44_100
    }
    fn channel_count(&self) -> u32 {
        2
    }
}

impl Drop for ModStream {
    fn drop(&mut self) {
        unsafe {
            openmpt_module_destroy(self.module);
        }
    }
}