1use std::fs;
2use std::path::PathBuf;
3use std::time::Duration;
4
5use libxm::*;
6use rodio::Source;
7
8pub const BUFFER_SIZE: usize = 2048;
9
10pub struct XMSource {
11 xm_context: XMContext,
12 buffer: [f32; BUFFER_SIZE],
13 buffer_index: usize,
14 sample_rate: u32,
15}
16
17impl XMSource {
18 pub fn new(xm_context: XMContext, sample_rate: u32) -> Self {
19 XMSource {
20 xm_context,
21 buffer: [0.0; BUFFER_SIZE],
22 buffer_index: 0,
23 sample_rate
24 }
25 }
26
27 pub fn from_bytes(bytes: &[u8], sample_rate: u32) -> Self {
28 let xm = libxm::XMContext::new(bytes, sample_rate)
29 .expect("failed to parse xm");
30
31 XMSource::new(xm, sample_rate)
32 }
33
34 pub fn from_file(path: PathBuf, sample_rate: u32) -> Self {
35 let file = fs::read(path).expect("couldn't read file");
36 let xm = libxm::XMContext::new(&file, sample_rate)
37 .expect("failed to parse xm");
38
39 XMSource::new(xm, sample_rate)
40 }
41}
42
43impl Source for XMSource {
44 fn current_frame_len(&self) -> Option<usize> {
45 Some(BUFFER_SIZE - self.buffer_index)
46 }
47
48 fn channels(&self) -> u16 { 2 }
49
50 fn sample_rate(&self) -> u32 { self.sample_rate }
51
52 fn total_duration(&self) -> Option<Duration> { None }
53}
54
55impl Iterator for XMSource {
56 type Item = f32;
57
58 fn next(&mut self) -> Option<Self::Item> {
59 self.buffer_index += 1;
60
61 if self.buffer_index >= BUFFER_SIZE {
62 self.xm_context.generate_samples(&mut self.buffer);
63 self.buffer_index = 0;
64 }
65
66 Some(self.buffer[self.buffer_index])
67 }
68}