Initial commit, very broken
This commit is contained in:
		
							
								
								
									
										16
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/main.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| mod note; | ||||
| mod tracker; | ||||
| mod sampler; | ||||
|  | ||||
| use std::fs::File; | ||||
| use std::io::BufReader; | ||||
| use std::time::Duration; | ||||
| use rodio::{Decoder, OutputStream, Sink, Source}; | ||||
| use rodio::source::SineWave; | ||||
| use crate::tracker::Tracker; | ||||
|  | ||||
| fn main() { | ||||
|     let mut tracker = Tracker::new(4, 64); | ||||
|     tracker.load_file("./3266CHIP.MOD"); | ||||
|     tracker.play(); | ||||
| } | ||||
							
								
								
									
										22
									
								
								src/note.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/note.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| use std::fmt::{Display, Formatter}; | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct Note { | ||||
|     pub frequency: f32, | ||||
|     pub sample: u8, | ||||
|     pub effect: u16, | ||||
| } | ||||
|  | ||||
| impl Display for Note { | ||||
|     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||||
|         write!(f, "freq {}, sample: {}, effect: {}", self.frequency, self.sample, self.effect) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod test { | ||||
|     #[test] | ||||
|     pub fn note_to_frequency() { | ||||
|  | ||||
|     } | ||||
| } | ||||
							
								
								
									
										57
									
								
								src/sampler.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/sampler.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| use std::time::Duration; | ||||
| use modfile::ptmf::SampleInfo; | ||||
| use rodio::Source; | ||||
|  | ||||
| #[derive(Clone, Debug)] | ||||
| pub struct Sample { | ||||
|     num_sample: usize, | ||||
|     _sample_rate: u32, | ||||
|     sample: SampleInfo, | ||||
| } | ||||
|  | ||||
| impl Sample { | ||||
|     pub fn new(sample_rate: u32, sample: SampleInfo) -> Self { | ||||
|         Self { | ||||
|             num_sample: 0, | ||||
|             _sample_rate: sample_rate, | ||||
|             sample, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn set_sample_rate(&mut self, sample_rate: u32) { | ||||
|         self._sample_rate = sample_rate; | ||||
|         self.num_sample = 0; | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Iterator for Sample { | ||||
|     type Item = f32; | ||||
|  | ||||
|     fn next(&mut self) -> Option<Self::Item> { | ||||
|         if self.sample.data.len() == 0 { | ||||
|             return Some(0.0); | ||||
|         } | ||||
|         if self.num_sample >= self.sample.data.len() { | ||||
|             self.num_sample = 0; | ||||
|         } | ||||
|         let value = self.sample.data[self.num_sample]; | ||||
|         self.num_sample = self.num_sample.wrapping_add(1); | ||||
|         Some((value as i8 as f32)/(i8::MAX as f32)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Source for Sample { | ||||
|     fn current_frame_len(&self) -> Option<usize> { | ||||
|         Some(self.sample.data.len()-self.num_sample) | ||||
|     } | ||||
|  | ||||
|     fn channels(&self) -> u16 { 1 } | ||||
|  | ||||
|     fn sample_rate(&self) -> u32 { | ||||
|         self._sample_rate | ||||
|     } | ||||
|  | ||||
|     fn total_duration(&self) -> Option<Duration> { | ||||
|         Some(Duration::new(0, (self.sample.data.len() * 1_000_000_000 / self._sample_rate as usize) as u32)) | ||||
|     } | ||||
| } | ||||
							
								
								
									
										131
									
								
								src/tracker.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								src/tracker.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,131 @@ | ||||
| use std::fs::File; | ||||
| use std::io::BufReader; | ||||
| use std::time::Duration; | ||||
| use modfile::ptmf; | ||||
| use rodio::{OutputStream, OutputStreamHandle, Sink, Source}; | ||||
| use rodio::source::SineWave; | ||||
| use crate::note::Note; | ||||
| use crate::sampler::Sample; | ||||
|  | ||||
| pub struct Tracker { | ||||
|     stream: OutputStream, | ||||
|     stream_handle: OutputStreamHandle, | ||||
|     sinks: Vec<Sink>, | ||||
|     patterns: Vec<Vec<Vec<Option<Note>>>>, | ||||
|     positions: Vec<u8>, | ||||
|     samples: Vec<Sample>, | ||||
| } | ||||
|  | ||||
| impl Tracker { | ||||
|     pub fn new(tracks: u8, lines: u8) -> Self { | ||||
|         let (stream, stream_handle) = OutputStream::try_default().unwrap(); | ||||
|         let mut _sinks = vec![]; | ||||
|         let mut _tracks = vec![]; | ||||
|  | ||||
|         for _ in 0..tracks { | ||||
|             _sinks.push(Sink::try_new(&stream_handle).unwrap()); | ||||
|             _tracks.push(vec![None; lines as usize]) | ||||
|         } | ||||
|  | ||||
|  | ||||
|         Self { | ||||
|             stream, stream_handle, | ||||
|             sinks: _sinks, patterns: vec![_tracks.clone()], | ||||
|             positions: vec![], | ||||
|             samples: vec![] | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn play(&mut self) { | ||||
|         for sink in &self.sinks { | ||||
|             sink.pause(); | ||||
|         } | ||||
|  | ||||
|         let lines = self.patterns[0][0].len(); | ||||
|         println!("Playing {} lines", lines); | ||||
|         for &position in &self.positions { | ||||
|             let pattern = &self.patterns[position as usize]; | ||||
|             for line in 0..lines { | ||||
|                 for (track, notes) in pattern.iter().enumerate() { | ||||
|                     let note = notes[line].as_ref(); | ||||
|                     if note.is_some() { | ||||
|                         //println!("Playing note: {}", note.as_ref().unwrap()); | ||||
|                         let note = note.unwrap(); | ||||
|                         let frequency = note.frequency; | ||||
|                         let sample = note.sample; | ||||
|                         self.samples[sample as usize].set_sample_rate(frequency as u32); | ||||
|                         self.sinks[track].append( | ||||
|                             self.samples[sample as usize].clone() | ||||
|                                 .take_duration(Duration::from_secs_f64(60.0/375.0)) | ||||
|                                 .amplify(0.20) | ||||
|                         ) | ||||
|                     } else { | ||||
|                         //println!("Playing note: ---"); | ||||
|                         self.sinks[track].append( | ||||
|                             SineWave::new(0.0) | ||||
|                                 .take_duration(Duration::from_secs_f64(60.0/375.0)) | ||||
|                                 .amplify(0.20) | ||||
|                         ) | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         for sink in &self.sinks { | ||||
|             sink.play(); | ||||
|         } | ||||
|  | ||||
|         for sink in &self.sinks { | ||||
|             sink.sleep_until_end(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn load_file(&mut self, file: &str) { | ||||
|         let mut reader = BufReader::new(File::open(file).unwrap()); | ||||
|         let mut module = ptmf::read_mod(&mut reader, false).unwrap(); | ||||
|  | ||||
|         println!("Loading: {}", module.name); | ||||
|         println!("\tLength: {:?}", module.length); | ||||
|         //println!("\tPatterns: {:?}", module.patterns); | ||||
|         println!("\tPositions: {:?}", module.positions); | ||||
|         println!("\tSamples: {:?}", module.sample_info.len()); | ||||
|         //println!("\tSamples: {:?}", module.sample_info); | ||||
|  | ||||
|         for i in 0..module.length { | ||||
|             self.positions.push(module.positions.data[i as usize]); | ||||
|         } | ||||
|  | ||||
|         for sample in module.sample_info { | ||||
|             self.samples.push(Sample::new(44100, sample.clone())); | ||||
|         } | ||||
|  | ||||
|         let mut prev_freqs = vec![1.0; module.length as usize]; | ||||
|  | ||||
|         for _ in 0..(module.patterns.len()-1) { | ||||
|             self.patterns.push(self.patterns[0].clone()); | ||||
|         } | ||||
|  | ||||
|         for (patern_index, pattern) in module.patterns.iter().enumerate() { | ||||
|             for (line, row) in pattern.rows.iter().enumerate() { | ||||
|                 for (track, channel) in row.channels.iter().enumerate() { | ||||
|                     let frequency = if channel.period == 0 { | ||||
|                         prev_freqs[track] | ||||
|                     } else { | ||||
|                         7093789.2/(2.0*channel.period as f32) | ||||
|                     }; | ||||
|  | ||||
|                     prev_freqs[track] = frequency; | ||||
|  | ||||
|                     println!("{patern_index}, {track}, {line}"); | ||||
|  | ||||
|                     self.patterns[patern_index][track][line] = Some(Note { | ||||
|                         frequency, | ||||
|                         sample: channel.sample_number, | ||||
|                         effect: channel.effect | ||||
|                     }); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user