diff --git a/src/reskit/soundtrack.rs b/src/reskit/soundtrack.rs index 39342e8..6f6c8bf 100644 --- a/src/reskit/soundtrack.rs +++ b/src/reskit/soundtrack.rs @@ -71,6 +71,37 @@ pub struct Instrument { instrument_type: InstrumentType } +#[derive(Debug)] +pub enum Note { + NoteOff, + CSharp( u16 ), + D( u16 ), + DSharp( u16 ), + E( u16 ), + F( u16 ), + FSharp( u16 ), + G( u16 ), + GSharp( u16 ), + A( u16 ), + ASharp( u16 ), + B( u16 ), + C( u16 ) +} + +#[derive(Debug)] +pub struct Effect { + effect_code: i16, + effect_value: Option +} + +#[derive(Debug)] +pub struct PatternRow { + note: Option, + volume: Option, + effects: Vec, + instrument_index: Option +} + pub struct DmfModule { platform: u8, version: u8, @@ -103,6 +134,22 @@ fn get_i8( bytes: &mut Iter<'_, u8> ) -> Result> { Ok( took as i8 ) } +fn get_u16( bytes: &mut Iter<'_, u8> ) -> Result> { + let took = bytes.take( 2 ); + let took: Vec = took.cloned().collect(); + let took = u16::from_le_bytes( took[0..2].try_into()? ); + + Ok( took ) +} + +fn get_i16( bytes: &mut Iter<'_, u8> ) -> Result> { + let took = bytes.take( 2 ); + let took: Vec = took.cloned().collect(); + let took = i16::from_le_bytes( took[0..2].try_into()? ); + + Ok( took ) +} + fn get_u32( bytes: &mut Iter<'_, u8> ) -> Result> { let took = bytes.take( 4 ); let took: Vec = took.cloned().collect(); @@ -513,7 +560,83 @@ impl DmfModule { instruments.push( Instrument { name, instrument_type } ); } - // TODO !! + // Wavetables are not used in fm synthesis, this should validate as 0 or error + let wavetable_count = get_u8( bytes.by_ref() )?; + if wavetable_count > 0 { + return Err( "invalid file: wavetables should be zero for system type Sega Megadrive" )?; + } + + // (channel, patterns) + let mut channel_patterns: Vec> = Vec::new(); + for _ in 0..system_total_channels { + let mut channel_rows: Vec = Vec::new(); + let num_effects = get_u8( bytes.by_ref() )?; + + for _ in 0..patterns_count { + for _ in 0..rows_per_pattern { + let note = get_u16( bytes.by_ref() )?; + let octave = get_u16( bytes.by_ref() )?; + + let note: Option = if note == 0 && octave == 0 { + None + } else { + Some( + match note { + 100 => Note::NoteOff, + 1 => Note::CSharp( octave ), + 2 => Note::D( octave ), + 3 => Note::DSharp( octave ), + 4 => Note::E( octave ), + 5 => Note::F( octave ), + 6 => Note::FSharp( octave ), + 7 => Note::G( octave ), + 8 => Note::GSharp( octave ), + 9 => Note::A( octave ), + 10 => Note::ASharp( octave ), + 11 => Note::B( octave ), + 12 => Note::C( octave ), + _ => return Err( format!( "invalid file: invalid note id {}", note ) )? + } + ) + }; + + let volume = get_i16( bytes.by_ref() )?; + let volume: Option = if volume == -1 { + None + } else { + Some( volume ) + }; + + let mut effects: Vec = Vec::new(); + for _ in 0..num_effects { + let effect_code = get_i16( bytes.by_ref() )?; + let effect_value = get_i16( bytes.by_ref() )?; + + if effect_code != -1 { + effects.push( + Effect{ + effect_code, + effect_value: if effect_value != -1 { Some( effect_code ) } else { None } + } + ); + } + } + + let instrument_index = get_i16( bytes.by_ref() )?; + let instrument_index: Option = if instrument_index != -1 { + Some( instrument_index.try_into()? ) + } else { + None + }; + + channel_rows.push( PatternRow { note, volume, effects, instrument_index } ); + } + } + + channel_patterns.push( channel_rows ); + } + + // TODO: PCM Samples !! Ok( DmfModule { @@ -528,4 +651,75 @@ impl DmfModule { todo!() } -} \ No newline at end of file +} + + +impl Note { + + pub fn get_freq( &self ) -> Option { + match self { + Note::NoteOff => None, + Note::CSharp( octave ) => match octave { + 0 => Some( 17.32 ), 1 => Some( 34.65 ), 2 => Some( 69.30 ), 3 => Some( 138.59 ), + 4 => Some( 277.18 ), 5 => Some( 554.37 ), 6 => Some( 1108.73 ), 7 => Some( 2217.46 ), + _ => None + }, + Note::D( octave ) => match octave { + 0 => Some( 18.35 ), 1 => Some( 36.71 ), 2 => Some( 73.42 ), 3 => Some( 146.83 ), + 4 => Some( 293.66 ), 5 => Some( 587.33 ), 6 => Some( 1174.66 ), 7 => Some( 2349.32 ), + _ => None + }, + Note::DSharp( octave ) => match octave { + 0 => Some( 19.45 ), 1 => Some( 38.89 ), 2 => Some( 77.78 ), 3 => Some( 155.56 ), + 4 => Some( 311.13 ), 5 => Some( 622.25 ), 6 => Some( 1244.51 ), 7 => Some( 2489.02 ), + _ => None + }, + Note::E( octave ) => match octave { + 0 => Some( 20.60 ), 1 => Some( 41.20 ), 2 => Some( 82.41 ), 3 => Some( 164.81 ), + 4 => Some( 329.63 ), 5 => Some( 659.25 ), 6 => Some( 1318.51 ), 7 => Some( 2637.02 ), + _ => None + }, + Note::F( octave ) => match octave { + 0 => Some( 21.83 ), 1 => Some( 43.65 ), 2 => Some( 87.31 ), 3 => Some( 174.61 ), + 4 => Some( 349.23 ), 5 => Some( 698.46 ), 6 => Some( 1396.91 ), 7 => Some( 2793.83 ), + _ => None + }, + Note::FSharp( octave ) => match octave { + 0 => Some( 23.12 ), 1 => Some( 46.25 ), 2 => Some( 92.50 ), 3 => Some( 185.00 ), + 4 => Some( 369.99 ), 5 => Some( 739.99 ), 6 => Some( 1479.98 ), 7 => Some( 2959.96 ), + _ => None + }, + Note::G( octave ) => match octave { + 0 => Some( 24.50 ), 1 => Some( 49.00 ), 2 => Some( 98.00 ), 3 => Some( 196.00 ), + 4 => Some( 392.00 ), 5 => Some( 783.99 ), 6 => Some( 1567.98 ), 7 => Some( 3135.96 ), + _ => None + }, + Note::GSharp( octave ) => match octave { + 0 => Some( 25.96 ), 1 => Some( 51.91 ), 2 => Some( 103.83 ), 3 => Some( 207.65 ), + 4 => Some( 415.30 ), 5 => Some( 830.61 ), 6 => Some( 1661.22 ), 7 => Some( 3322.44 ), + _ => None + }, + Note::A( octave ) => match octave { + 0 => Some( 27.50 ), 1 => Some( 55.00 ), 2 => Some( 110.00 ), 3 => Some( 220.00 ), + 4 => Some( 440.00 ), 5 => Some( 880.00 ), 6 => Some( 1760.00 ), 7 => Some( 3520.00 ), + _ => None + }, + Note::ASharp( octave ) => match octave { + 0 => Some( 29.14 ), 1 => Some( 58.27 ), 2 => Some( 116.54 ), 3 => Some( 233.08 ), + 4 => Some( 466.16 ), 5 => Some( 932.33 ), 6 => Some( 1864.66 ), 7 => Some( 3729.31 ), + _ => None + }, + Note::B( octave ) => match octave { + 0 => Some( 30.87 ), 1 => Some( 61.74 ), 2 => Some( 123.47 ), 3 => Some( 246.94 ), + 4 => Some( 493.88 ), 5 => Some( 987.77 ), 6 => Some( 1975.53 ), 7 => Some( 3951.07 ), + _ => None + }, + Note::C( octave ) => match octave { + 0 => Some( 16.35 ), 1 => Some( 32.70 ), 2 => Some( 65.41 ), 3 => Some( 130.81 ), + 4 => Some( 261.63 ), 5 => Some( 523.25 ), 6 => Some( 1046.50 ), 7 => Some( 2093.00 ), + _ => None + } + } + } + +}