old school music tracker audio backend
at main 123 lines 3.1 kB view raw
1use std::{ 2 error::Error, 3 fmt::{Display, Write}, 4}; 5 6use crate::project::event_command::NoteCommand; 7 8#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] 9pub struct Note(u8); 10 11impl Note { 12 pub fn new(value: u8) -> Result<Note, u8> { 13 if value > 199 { 14 Err(value) 15 } else { 16 Ok(Note(value)) 17 } 18 } 19 20 pub const fn get_octave(self) -> u8 { 21 self.0 / 12 22 } 23 24 pub const fn get_note_name(self) -> &'static str { 25 match self.0 % 12 { 26 0 => "C", 27 1 => "C#", 28 2 => "D", 29 3 => "D#", 30 4 => "E", 31 5 => "F", 32 6 => "F#", 33 7 => "G", 34 8 => "G#", 35 9 => "A", 36 10 => "A#", 37 11 => "B", 38 _ => panic!(), 39 } 40 } 41 42 pub fn get_frequency(self) -> f32 { 43 // taken from https://en.wikipedia.org/wiki/MIDI_tuning_standard 44 440. * ((f32::from(self.0) - 69.) / 12.).exp2() 45 } 46 47 pub const fn get(self) -> u8 { 48 self.0 49 } 50} 51 52impl Display for Note { 53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 54 f.write_str(self.get_note_name())?; 55 f.write_char('-')?; 56 self.get_octave().fmt(f)?; 57 Ok(()) 58 } 59} 60 61impl Default for Note { 62 fn default() -> Self { 63 Self(60) // C-5 64 } 65} 66 67#[derive(Clone, Copy, Debug, Default)] 68pub struct NoteEvent { 69 pub note: Note, 70 pub sample_instr: u8, 71 pub vol: VolumeEffect, 72 pub command: NoteCommand, 73} 74 75#[derive(Debug, Clone, Copy, Default)] 76pub enum VolumeEffect { 77 FineVolSlideUp(u8), 78 FineVolSlideDown(u8), 79 VolSlideUp(u8), 80 VolSlideDown(u8), 81 PitchSlideUp(u8), 82 PitchSlideDown(u8), 83 SlideToNoteWithSpeed(u8), 84 VibratoWithSpeed(u8), 85 Volume(u8), 86 Panning(u8), 87 /// Uses Instr / Sample Default Volume 88 #[default] 89 None, 90} 91 92#[derive(Debug, Clone, Copy)] 93pub struct InvalidVolumeEffect; 94 95impl Display for InvalidVolumeEffect { 96 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 97 write!(f, "Invalid Volume Effect") 98 } 99} 100 101impl Error for InvalidVolumeEffect {} 102 103impl TryFrom<u8> for VolumeEffect { 104 type Error = InvalidVolumeEffect; 105 106 /// IT Tracker Format Conversion 107 /// no way to get None, as then it just doesn't get set 108 fn try_from(value: u8) -> Result<Self, Self::Error> { 109 match value { 110 0..=64 => Ok(Self::Volume(value)), 111 65..=74 => Ok(Self::FineVolSlideUp(value - 65)), 112 75..=84 => Ok(Self::FineVolSlideDown(value - 75)), 113 85..=94 => Ok(Self::VolSlideUp(value - 85)), 114 95..=104 => Ok(Self::VolSlideDown(value - 95)), 115 105..=114 => Ok(Self::PitchSlideDown(value - 105)), 116 115..=124 => Ok(Self::PitchSlideUp(value - 115)), 117 128..=192 => Ok(Self::Panning(value - 128)), 118 193..=202 => Ok(Self::SlideToNoteWithSpeed(value - 193)), 119 203..=212 => Ok(Self::VibratoWithSpeed(value - 203)), 120 _ => Err(InvalidVolumeEffect), 121 } 122 } 123}