diff --git a/src/reskit/soundtrack.rs b/src/reskit/soundtrack.rs index ab77793..39342e8 100644 --- a/src/reskit/soundtrack.rs +++ b/src/reskit/soundtrack.rs @@ -1,4 +1,4 @@ -use std::{error::Error, fs::File, io::Read, str::from_utf8, slice::Iter, convert::TryInto}; +use std::{error::Error, fs::File, io::Read, str::from_utf8, slice::Iter, convert::TryInto, collections::HashMap}; use flate2::read::ZlibDecoder; const DMF_MAGIC_NUMBER: &'static str = ".DelekDefleMask."; @@ -6,6 +6,12 @@ const DMF_SUPPORTED_VERSION: u8 = 0x18; const DMF_MD: u8 = 0x02; const DMF_MD_ENHANCED_CH3: u8 = 0x42; +#[derive(Debug)] +pub enum Either< Type1, Type2 > { + Type1( Type1 ), + Type2( Type2 ) +} + #[derive(Debug)] pub enum FrameMode { Ntsc, @@ -13,6 +19,58 @@ pub enum FrameMode { Custom( u8, u8, u8 ) } +#[derive(Debug, Default)] +pub struct FmOperator { + am: u8, + ar: u8, + dr: u8, + mult: u8, + rr: u8, + sl: u8, + tl: u8, + dt2: u8, + rs: u8, + dt: i8, + d2r: u8, + ssg_mode: u8 +} + +#[derive(Debug)] +pub struct FmSettings { + alg: u8, + fb: u8, + fms: u8, + ams: u8, + operators: [FmOperator; 4] +} + +#[derive(Debug)] +pub struct PsgEnvelope { + envelope: Either, Vec>, + loop_at: Option, + settings: HashMap<&'static str, bool> +} + +#[derive(Debug)] +pub struct PsgSettings { + volume: PsgEnvelope, + arpeggio: PsgEnvelope, + noise: PsgEnvelope, + wavetable: PsgEnvelope +} + +#[derive(Debug)] +pub enum InstrumentType { + Fm( FmSettings ), + Psg( PsgSettings ) +} + +#[derive(Debug)] +pub struct Instrument { + name: String, + instrument_type: InstrumentType +} + pub struct DmfModule { platform: u8, version: u8, @@ -21,7 +79,8 @@ pub struct DmfModule { speed_b: u8, frame_mode: FrameMode, rows_per_pattern: u32, - patterns: Vec> + pattern_matrix: Vec>, + instruments: Vec } fn get_string( bytes: &mut Iter<'_, u8>, take: usize ) -> Result> { @@ -38,6 +97,12 @@ fn get_u8( bytes: &mut Iter<'_, u8> ) -> Result> { Ok( took ) } +fn get_i8( bytes: &mut Iter<'_, u8> ) -> Result> { + let took = bytes.take( 1 ).cloned().next().ok_or( "invalid file: expected 1 byte" )?; + + Ok( took as i8 ) +} + fn get_u32( bytes: &mut Iter<'_, u8> ) -> Result> { let took = bytes.take( 4 ); let took: Vec = took.cloned().collect(); @@ -46,8 +111,16 @@ fn get_u32( bytes: &mut Iter<'_, u8> ) -> Result> { Ok( took ) } +fn get_i32( bytes: &mut Iter<'_, u8> ) -> Result> { + let took = bytes.take( 4 ); + let took: Vec = took.cloned().collect(); + let took = i32::from_le_bytes( took[0..4].try_into()? ); + + Ok( took ) +} + fn skip( bytes: &mut Iter<'_, u8>, by: usize ) { - for i in 0..by { + for _i in 0..by { bytes.next(); } } @@ -139,16 +212,16 @@ impl DmfModule { 13 }; - let mut patterns: Vec> = vec![vec![0; system_total_channels]; patterns_count.into()]; + let mut pattern_matrix: Vec> = vec![vec![0; system_total_channels]; patterns_count.into()]; for channel in 0..system_total_channels { for pattern in 0..patterns_count { let channel: usize = channel.into(); let pattern: usize = pattern.into(); - patterns[ pattern ][ channel ] = get_u8( bytes.by_ref() )?; + pattern_matrix[ pattern ][ channel ] = get_u8( bytes.by_ref() )?; } } - for pattern in &patterns { + for pattern in &pattern_matrix { for channel in pattern { print!( "{:#04X?} ", channel ); } @@ -157,10 +230,296 @@ impl DmfModule { print!( "\n" ); + let mut instruments: Vec = Vec::new(); + let instrument_count = get_u8( bytes.by_ref() )?; + + println!( "Instruments:\t{}", instrument_count ); + + for i in 0..instrument_count { + let name_len = get_u8( bytes.by_ref() )?; + let name = get_string( bytes.by_ref(), name_len.into() )?; + + println!( "Instrument {}:\t{}", i, name ); + + let mode = get_u8( bytes.by_ref() )?; + let instrument_type = match mode { + 0 => { + println!( "Psg Instrument" ); + + // Volume envelope + let volume = { + let size = get_u8( bytes.by_ref() )?; + let mut envelope: Vec = Vec::new(); + + for _i in 0..size { + envelope.push( get_u32( bytes.by_ref() )? ); + } + + let loop_at: Option = if size > 0 { + let loop_position = get_i8( bytes.by_ref() )?; + if loop_position != -1 { + Some( loop_position.try_into()? ) + } else { + None + } + } else { + None + }; + + println!( "Volume Envelope Size:\t{}", envelope.len() ); + + PsgEnvelope { + envelope: Either::Type1( envelope ), + loop_at, + settings: HashMap::new() + } + }; + + // Arpeggio envelope + let arpeggio = { + let size = get_u8( bytes.by_ref() )?; + let mut envelope: Vec = Vec::new(); + + for _i in 0..size { + envelope.push( get_i32( bytes.by_ref() )? - 12 ); + } + + let loop_at: Option = if size > 0 { + let loop_position = get_i8( bytes.by_ref() )?; + if loop_position != -1 { + Some( loop_position.try_into()? ) + } else { + None + } + } else { + None + }; + + let settings = HashMap::from([ + ( "arpeggio_macro_fixed", get_u8( bytes.by_ref() )? == 1 ) + ]); + + println!( "Arpeggio Envelope Size:\t{}", envelope.len() ); + println!( "Arpeggio Macro Fixed:\t{}", settings[ "arpeggio_macro_fixed" ] ); + + PsgEnvelope { + envelope: Either::Type2( envelope ), + loop_at, + settings + } + }; + + // Noise envelope + let noise = { + let size = get_u8( bytes.by_ref() )?; + let mut envelope: Vec = Vec::new(); + + for _i in 0..size { + envelope.push( get_u32( bytes.by_ref() )? ); + } + + let loop_at: Option = if size > 0 { + let loop_position = get_i8( bytes.by_ref() )?; + if loop_position != -1 { + Some( loop_position.try_into()? ) + } else { + None + } + } else { + None + }; + + println!( "Noise Envelope Size:\t{}", envelope.len() ); + + PsgEnvelope { + envelope: Either::Type1( envelope ), + loop_at, + settings: HashMap::new() + } + }; + + // Wavetable envelope + let wavetable = { + let size = get_u8( bytes.by_ref() )?; + let mut envelope: Vec = Vec::new(); + + for _i in 0..size { + envelope.push( get_u32( bytes.by_ref() )? ); + } + + let loop_at: Option = if size > 0 { + let loop_position = get_i8( bytes.by_ref() )?; + if loop_position != -1 { + Some( loop_position.try_into()? ) + } else { + None + } + } else { + None + }; + + println!( "WavTbl Envelope Size:\t{}\n", envelope.len() ); + + PsgEnvelope { + envelope: Either::Type1( envelope ), + loop_at, + settings: HashMap::new() + } + }; + + InstrumentType::Psg( PsgSettings { volume, arpeggio, noise, wavetable } ) + }, + 1 => { + println!( "Fm Instrument" ); + + let alg = get_u8( bytes.by_ref() )?; + let fb = get_u8( bytes.by_ref() )?; + let fms = get_u8( bytes.by_ref() )?; + let ams = get_u8( bytes.by_ref() )?; + + println!( "Alg:\t{}", alg ); + println!( "Fb:\t{}", fb ); + println!( "FMS:\t{}", fms ); + println!( "AMS:\t{}", ams ); + + let mut operators: [FmOperator; 4] = [ + FmOperator::default(), + FmOperator::default(), + FmOperator::default(), + FmOperator::default() + ]; + + // Operator 1 + { + operators[ 0 ].am = get_u8( bytes.by_ref() )?; + operators[ 0 ].ar = get_u8( bytes.by_ref() )?; + operators[ 0 ].dr = get_u8( bytes.by_ref() )?; + operators[ 0 ].mult = get_u8( bytes.by_ref() )?; + operators[ 0 ].rr = get_u8( bytes.by_ref() )?; + operators[ 0 ].sl = get_u8( bytes.by_ref() )?; + operators[ 0 ].tl = get_u8( bytes.by_ref() )?; + operators[ 0 ].dt2 = get_u8( bytes.by_ref() )?; + operators[ 0 ].rs = get_u8( bytes.by_ref() )?; + operators[ 0 ].dt = get_i8( bytes.by_ref() )? - 3; + operators[ 0 ].d2r = get_u8( bytes.by_ref() )?; + operators[ 0 ].ssg_mode = get_u8( bytes.by_ref() )?; + + println!( "Op 1 AM:\t{}", operators[ 0 ].am ); + println!( "Op 1 AR:\t{}", operators[ 0 ].ar ); + println!( "Op 1 DR:\t{}", operators[ 0 ].dr ); + println!( "Op 1 MULT:\t{}", operators[ 0 ].mult ); + println!( "Op 1 RR:\t{}", operators[ 0 ].rr ); + println!( "Op 1 SL:\t{}", operators[ 0 ].sl ); + println!( "Op 1 TL:\t{}", operators[ 0 ].tl ); + println!( "Op 1 DT2:\t{}", operators[ 0 ].dt2 ); + println!( "Op 1 RS:\t{}", operators[ 0 ].rs ); + println!( "Op 1 DT:\t{}", operators[ 0 ].dt ); + println!( "Op 1 D2R:\t{}", operators[ 0 ].d2r ); + println!( "Op 1 SSG_MODE:\t{}\n", operators[ 0 ].ssg_mode ); + } + + // Operator 3 + { + operators[ 2 ].am = get_u8( bytes.by_ref() )?; + operators[ 2 ].ar = get_u8( bytes.by_ref() )?; + operators[ 2 ].dr = get_u8( bytes.by_ref() )?; + operators[ 2 ].mult = get_u8( bytes.by_ref() )?; + operators[ 2 ].rr = get_u8( bytes.by_ref() )?; + operators[ 2 ].sl = get_u8( bytes.by_ref() )?; + operators[ 2 ].tl = get_u8( bytes.by_ref() )?; + operators[ 2 ].dt2 = get_u8( bytes.by_ref() )?; + operators[ 2 ].rs = get_u8( bytes.by_ref() )?; + operators[ 2 ].dt = get_i8( bytes.by_ref() )? - 3; + operators[ 2 ].d2r = get_u8( bytes.by_ref() )?; + operators[ 2 ].ssg_mode = get_u8( bytes.by_ref() )?; + + println!( "Op 3 AM:\t{}", operators[ 2 ].am ); + println!( "Op 3 AR:\t{}", operators[ 2 ].ar ); + println!( "Op 3 DR:\t{}", operators[ 2 ].dr ); + println!( "Op 3 MULT:\t{}", operators[ 2 ].mult ); + println!( "Op 3 RR:\t{}", operators[ 2 ].rr ); + println!( "Op 3 SL:\t{}", operators[ 2 ].sl ); + println!( "Op 3 TL:\t{}", operators[ 2 ].tl ); + println!( "Op 3 DT2:\t{}", operators[ 2 ].dt2 ); + println!( "Op 3 RS:\t{}", operators[ 2 ].rs ); + println!( "Op 3 DT:\t{}", operators[ 2 ].dt ); + println!( "Op 3 D2R:\t{}", operators[ 2 ].d2r ); + println!( "Op 3 SSG_MODE:\t{}\n", operators[ 2 ].ssg_mode ); + } + + // Operator 2 + { + operators[ 1 ].am = get_u8( bytes.by_ref() )?; + operators[ 1 ].ar = get_u8( bytes.by_ref() )?; + operators[ 1 ].dr = get_u8( bytes.by_ref() )?; + operators[ 1 ].mult = get_u8( bytes.by_ref() )?; + operators[ 1 ].rr = get_u8( bytes.by_ref() )?; + operators[ 1 ].sl = get_u8( bytes.by_ref() )?; + operators[ 1 ].tl = get_u8( bytes.by_ref() )?; + operators[ 1 ].dt2 = get_u8( bytes.by_ref() )?; + operators[ 1 ].rs = get_u8( bytes.by_ref() )?; + operators[ 1 ].dt = get_i8( bytes.by_ref() )? - 3; + operators[ 1 ].d2r = get_u8( bytes.by_ref() )?; + operators[ 1 ].ssg_mode = get_u8( bytes.by_ref() )?; + + println!( "Op 2 AM:\t{}", operators[ 1 ].am ); + println!( "Op 2 AR:\t{}", operators[ 1 ].ar ); + println!( "Op 2 DR:\t{}", operators[ 1 ].dr ); + println!( "Op 2 MULT:\t{}", operators[ 1 ].mult ); + println!( "Op 2 RR:\t{}", operators[ 1 ].rr ); + println!( "Op 2 SL:\t{}", operators[ 1 ].sl ); + println!( "Op 2 TL:\t{}", operators[ 1 ].tl ); + println!( "Op 2 DT2:\t{}", operators[ 1 ].dt2 ); + println!( "Op 2 RS:\t{}", operators[ 1 ].rs ); + println!( "Op 2 DT:\t{}", operators[ 1 ].dt ); + println!( "Op 2 D2R:\t{}", operators[ 1 ].d2r ); + println!( "Op 2 SSG_MODE:\t{}\n", operators[ 1 ].ssg_mode ); + } + + // Operator 4 + { + operators[ 3 ].am = get_u8( bytes.by_ref() )?; + operators[ 3 ].ar = get_u8( bytes.by_ref() )?; + operators[ 3 ].dr = get_u8( bytes.by_ref() )?; + operators[ 3 ].mult = get_u8( bytes.by_ref() )?; + operators[ 3 ].rr = get_u8( bytes.by_ref() )?; + operators[ 3 ].sl = get_u8( bytes.by_ref() )?; + operators[ 3 ].tl = get_u8( bytes.by_ref() )?; + operators[ 3 ].dt2 = get_u8( bytes.by_ref() )?; + operators[ 3 ].rs = get_u8( bytes.by_ref() )?; + operators[ 3 ].dt = get_i8( bytes.by_ref() )? - 3; + operators[ 3 ].d2r = get_u8( bytes.by_ref() )?; + operators[ 3 ].ssg_mode = get_u8( bytes.by_ref() )?; + + println!( "Op 4 AM:\t{}", operators[ 3 ].am ); + println!( "Op 4 AR:\t{}", operators[ 3 ].ar ); + println!( "Op 4 DR:\t{}", operators[ 3 ].dr ); + println!( "Op 4 MULT:\t{}", operators[ 3 ].mult ); + println!( "Op 4 RR:\t{}", operators[ 3 ].rr ); + println!( "Op 4 SL:\t{}", operators[ 3 ].sl ); + println!( "Op 4 TL:\t{}", operators[ 3 ].tl ); + println!( "Op 4 DT2:\t{}", operators[ 3 ].dt2 ); + println!( "Op 4 RS:\t{}", operators[ 3 ].rs ); + println!( "Op 4 DT:\t{}", operators[ 3 ].dt ); + println!( "Op 4 D2R:\t{}", operators[ 3 ].d2r ); + println!( "Op 4 SSG_MODE:\t{}\n", operators[ 3 ].ssg_mode ); + } + + InstrumentType::Fm( FmSettings { alg, fb, fms, ams, operators } ) + }, + _ => return Err( format!( "invalid file: invalid instrument mode {}", mode ) )? + }; + + instruments.push( Instrument { name, instrument_type } ); + } + // TODO !! Ok( - DmfModule { platform, version, time_base, speed_a, speed_b, frame_mode, rows_per_pattern, patterns } + DmfModule { + platform, version, time_base, speed_a, speed_b, frame_mode, rows_per_pattern, + pattern_matrix, instruments + } ) }