old school music tracker audio backend
1//! I currently use generics. This hinges on the idea that in every program only one type of file reader is used
2//! so dynamic dispatch would offer no advantages. Maybe i will / should change this at some point.
3//!
4//! I would probably use BufReader<dyn Read + Seek> as the reader to have the buffer below the vtable.
5//! would need my own trait to have Read and Seek together in one vtable
6//!
7//! I currently only support the schism tracker file format.
8use err::LoadErr;
9
10use crate::project::song::Song;
11
12pub mod err;
13pub mod header;
14pub mod instrument;
15pub mod pattern;
16pub mod sample;
17
18#[derive(Debug, Clone, Copy)]
19pub struct InFilePtr(pub(crate) std::num::NonZeroU32);
20
21impl InFilePtr {
22 /// Move the Read Cursor to the value of the file ptr
23 pub fn move_to_self<S: std::io::Seek>(self, seeker: &mut S) -> Result<(), std::io::Error> {
24 seeker
25 .seek(std::io::SeekFrom::Start(self.0.get().into()))
26 .map(|_| ())
27 }
28}
29
30/// Default parsing of a song. Should be fine for most usecases. If you want more customization use the different parsing functions directly.
31///
32/// R should be buffered in some way and not do a syscall on every read.
33/// If you ever find yourself using multiple different reader and/or handlers please open an issue on Github, i will change this to take &dyn.
34pub fn parse_song<R: std::io::Read + std::io::Seek>(reader: &mut R) -> Result<Song, LoadErr> {
35 let header = header::ImpulseHeader::parse(reader)?;
36 let mut song = Song::default();
37 header.copy_values_into_song(&mut song);
38
39 // parse patterns
40 for (idx, ptr) in header
41 .pattern_offsets
42 .iter()
43 // number the pointer slots
44 .enumerate()
45 // filter out empty pointers. Keep the old slot numbering
46 .flat_map(|(idx, ptr)| ptr.map(|ptr| (idx, ptr)))
47 {
48 ptr.move_to_self(reader)?;
49 let pattern = pattern::parse_pattern(reader)?;
50 song.patterns[idx] = pattern;
51 }
52
53 println!("sample_count: {:?}", header.sample_offsets);
54 // let mut sample_header_buf = [0; ImpulseSampleHeader::SIZE];
55 for (idx, ptr) in header
56 .sample_offsets
57 .iter()
58 .enumerate()
59 .flat_map(|(idx, ptr)| ptr.map(|ptr| (idx, ptr)))
60 {
61 println!("header {idx} at {ptr:?}");
62 // ptr.move_to_self(reader)?;
63 // reader.read_exact(&mut sample_header_buf)?;
64 // println!("header {idx} read");
65 // let sample_header = ImpulseSampleHeader::parse(&sample_header_buf);
66 // println!("sample_header {idx}: {sample_header:?}");
67 }
68
69 Ok(song)
70}