diff --git a/src/main.rs b/src/main.rs index ecfb5e9..dd86448 100644 --- a/src/main.rs +++ b/src/main.rs @@ -84,20 +84,8 @@ fn run() -> Result<(), Box> { let result = DmfModule::from_file( &input_filename )?; - for ( filename, data ) in result.get_eefs()? { - println!( "Creating EEF instrument file {}", filename ); - let mut file = File::create( filename )?; - file.write_all( &data )? - } - - for ( filename, data ) in result.get_eifs()? { - println!( "Creating EIF instrument file {}", filename ); - let mut file = File::create( filename )?; - file.write_all( &data )? - } - - for ( filename, data ) in result.get_ewfs()? { - println!( "Creating EWF audio file {}", filename ); + for ( filename, data ) in result.get_instruments()? { + println!( "Creating instrument file {}", filename ); let mut file = File::create( filename )?; file.write_all( &data )? } diff --git a/src/reskit/soundtrack/engines/echo.rs b/src/reskit/soundtrack/engines/echo.rs index 81e8c17..42d1250 100644 --- a/src/reskit/soundtrack/engines/echo.rs +++ b/src/reskit/soundtrack/engines/echo.rs @@ -1,7 +1,7 @@ use std::{collections::HashMap, error::Error}; use linked_hash_set::LinkedHashSet; -use crate::reskit::{soundtrack::types::{PatternRow, Note, Channel, OctaveFrequency, Effect}, utility::print_warning}; +use crate::reskit::{soundtrack::{types::{PatternRow, Note, Channel, OctaveFrequency, Effect}, formats::dmf::DmfModule}, utility::print_warning}; // https://github.com/sikthehedgehog/Echo/blob/master/doc/esf.txt const ESF_NOTE_ON: u8 = 0x00; @@ -55,13 +55,10 @@ pub type EchoEvent = Vec; pub trait EchoFormat { - fn get_eefs( &self ) -> Result>, Box>; - - fn get_eifs( &self ) -> Result>, Box>; - - fn get_ewfs( &self ) -> Result>, Box>; + fn get_instruments( &self ) -> Result>, Box>; fn get_esf( &self ) -> Result, Box>; + } fn get_semitone( note: &Note ) -> Result> { diff --git a/src/reskit/soundtrack/formats/dmf.rs b/src/reskit/soundtrack/formats/dmf.rs index cc21ee2..be30475 100644 --- a/src/reskit/soundtrack/formats/dmf.rs +++ b/src/reskit/soundtrack/formats/dmf.rs @@ -631,202 +631,189 @@ impl DmfModule { impl EchoFormat for DmfModule { - fn get_eefs( &self ) -> Result>, Box> { - let mut eefs: HashMap> = HashMap::new(); + fn get_instruments( &self ) -> Result>, Box> { + let mut files: HashMap> = HashMap::new(); for instrument in &self.instruments { - if let InstrumentType::PsgDcsg( settings ) = &instrument.instrument_type { - // Echo uses volume and arpeggio envelopes - // Noise envelopes are usable but cannot be articulated in an instrument. - // For use of the noise envelope, use ESF $0Bnn/$3Bnn and $3Ann in the stream. + match &instrument.instrument_type { + InstrumentType::Fm2612( settings ) => { + // Create feedback + algorithm byte + let alg_fb: u8 = ( settings.fb << 3 ) | settings.alg; - // comments using example: 20 vol values, 6 arp values, both repeat from the beginning + // The operators are laid out as FmOperator objects in order 1 -> 3 -> 2 -> 4 + // EIF instrument layout below + let mut mult_dt: [u8; 4] = [ 0x00, 0x00, 0x00, 0x00 ]; + let mut tl: [u8; 4] = [ 0x00, 0x00, 0x00, 0x00 ]; + let mut ar_rs: [u8; 4] = [ 0x00, 0x00, 0x00, 0x00 ]; + let mut dr_am: [u8; 4] = [ 0x00, 0x00, 0x00, 0x00 ]; + let mut sr: [u8; 4] = [ 0x00, 0x00, 0x00, 0x00 ]; + let mut rr_sl: [u8; 4] = [ 0x00, 0x00, 0x00, 0x00 ]; + let mut ssg_eg: [u8; 4] = [ 0x00, 0x00, 0x00, 0x00 ]; - // generate the initial portion (always). this is just an expansion - // of the envelopes, without loops, into two Vec's. - // 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 - // 01 02 03 04 05 06 -- -- -- -- -- -- -- -- -- -- -- -- -- -- - let mut volume_envelope: Vec = settings.volume.envelope.iter().map( | long | *long as u8 ).collect(); - let mut arpeggio_envelope: Vec = settings.arpeggio.envelope.iter().map( | long | *long as i8 ).collect(); + // Likewise, "operators" is laid out 1 -> 3 -> 2 -> 4 + for i in 0..4 { + let dt: u8 = match settings.operators[ i ].dt { + 0 => 0b000, + 1 => 0b001, + 2 => 0b010, + 3 => 0b011, + -1 => 0b101, + -2 => 0b110, + -3 => 0b111, + _ => return Err( "invalid file: invalid value given for dt" )? + }; - // now take a slice of the repeatable portion of the smaller array, - // and use it to expand the smaller array to the same size as the - // larger array. if there is no repeatable portion, take a one-element - // slice of the last element in the array. - // 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 - // 01 02 03 04 05 06 01 02 03 04 05 06 01 02 03 04 05 06 01 02 - let mut volume_repeat_pattern: Ring = Ring::create( - if let Some( loop_at ) = settings.volume.loop_at { - Vec::from( &volume_envelope[ loop_at.. ] ) - } else { - vec![ volume_envelope[ volume_envelope.len() - 1 ] ] - }, - None - ); - let mut arpeggio_repeat_pattern: Ring = Ring::create( - if let Some( loop_at ) = settings.arpeggio.loop_at { - Vec::from( &arpeggio_envelope[ loop_at.. ] ) - } else { - vec![ arpeggio_envelope[ arpeggio_envelope.len() - 1 ] ] - }, - None - ); + // https://plutiedev.com/ym2612-registers + // https://github.com/sikthehedgehog/Echo/blob/master/doc/eif.txt + mult_dt[ i ] = ( dt << 4 ) | settings.operators[ i ].mult; + tl[ i ] = settings.operators[ i ].tl; + ar_rs[ i ] = ( settings.operators[ i ].rs << 6 ) | settings.operators[ i ].ar; + dr_am[ i ] = ( settings.operators[ i ].am << 7 ) | settings.operators[ i ].dr; + sr[ i ] = settings.operators[ i ].d2r; // "Sometimes also called 'second decay rate' (D2R)." + rr_sl[ i ] = ( settings.operators[ i ].sl << 4 ) | settings.operators[ i ].rr; - if volume_envelope.len() < arpeggio_envelope.len() { - let difference = arpeggio_envelope.len() - volume_envelope.len(); - for _ in 0..difference { - volume_envelope.push( - volume_repeat_pattern.next().expect( "fatal: internal error (volume_repeat_pattern)" ) - ); - } - } else if arpeggio_envelope.len() < volume_envelope.len() { - let difference = volume_envelope.len() - arpeggio_envelope.len(); - for _ in 0..difference { - arpeggio_envelope.push( - arpeggio_repeat_pattern.next().expect( "fatal: internal error (arpeggio_repeat_pattern)" ) - ); - } - } + if settings.operators[ i ].ssg_mode > 0 { + print_warning( &format!( "SSG-EG mode set on instrument {}, operator {}. this operator may not work on clone hardware or certain emulators", instrument.name, i ) ); + } - let interval_size = max( volume_repeat_pattern.len(), arpeggio_repeat_pattern.len() ); - - // now, you generate the repeating portion using the two repeat_pattern iterators. - // each segment at a time is generated by the size of the larger array. - // generate it until the first repeat pattern is regenerated. - // 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 .. (volume repeats from beginning) - // 03 04 05 06 01 02 03 04 05 06 01 02 03 04 05 06 01 02 03 04 .. (arpeggio repeats up from where it left off) - - // 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 .. - // 05 06 01 02 03 04 05 06 01 02 03 04 05 06 01 02 03 04 05 06 .. - - // 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 .. - // 01 02 03 04 05 06 01 02 03 04 05 06 01 02 03 04 05 06 01 02 .. - let mut volume_repeat: Vec = Vec::new(); - let mut arpeggio_repeat: Vec = Vec::new(); - - let mut initial_volume_repeat: Vec = Vec::new(); - let mut initial_arpeggio_repeat: Vec = Vec::new(); - for _ in 0..interval_size { - initial_volume_repeat.push( volume_repeat_pattern.next().expect( "fatal: internal error (volume_repeat_pattern)" ) ); - initial_arpeggio_repeat.push( arpeggio_repeat_pattern.next().expect( "fatal: internal error (arpeggio_repeat_pattern)" ) ); - } - - volume_repeat.extend( &initial_volume_repeat ); - arpeggio_repeat.extend( &initial_arpeggio_repeat ); - - loop { - // Guard against OOM - if volume_repeat.len() > 65535 { - return Err( "eef error: psg instrument repeat period too long (try modifying repeating envelopes to have evenly-divisible parameters)" )? + ssg_eg[ i ] = settings.operators[ i ].ssg_mode; } - let mut volume_repeat_period: Vec = Vec::new(); - let mut arpeggio_repeat_period: Vec = Vec::new(); + let mut eif: Vec = Vec::new(); + eif.push( alg_fb ); + eif.extend( mult_dt ); + eif.extend( tl ); + eif.extend( ar_rs ); + eif.extend( dr_am ); + eif.extend( sr ); + eif.extend( rr_sl ); + eif.extend( ssg_eg ); + + files.insert( format!( "{}.eif", if instrument.name.is_empty() { Uuid::new_v4().to_string() } else { instrument.name.to_owned() } ), eif ); + }, + InstrumentType::PsgDcsg( settings ) => { + // Echo uses volume and arpeggio envelopes + // Noise envelopes are usable but cannot be articulated in an instrument. + // For use of the noise envelope, use ESF $0Bnn/$3Bnn and $3Ann in the stream. + + // comments using example: 20 vol values, 6 arp values, both repeat from the beginning + + // generate the initial portion (always). this is just an expansion + // of the envelopes, without loops, into two Vec's. + // 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 + // 01 02 03 04 05 06 -- -- -- -- -- -- -- -- -- -- -- -- -- -- + let mut volume_envelope: Vec = settings.volume.envelope.iter().map( | long | *long as u8 ).collect(); + let mut arpeggio_envelope: Vec = settings.arpeggio.envelope.iter().map( | long | *long as i8 ).collect(); + + // now take a slice of the repeatable portion of the smaller array, + // and use it to expand the smaller array to the same size as the + // larger array. if there is no repeatable portion, take a one-element + // slice of the last element in the array. + // 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 + // 01 02 03 04 05 06 01 02 03 04 05 06 01 02 03 04 05 06 01 02 + let mut volume_repeat_pattern: Ring = Ring::create( + if let Some( loop_at ) = settings.volume.loop_at { + Vec::from( &volume_envelope[ loop_at.. ] ) + } else { + vec![ volume_envelope[ volume_envelope.len() - 1 ] ] + }, + None + ); + let mut arpeggio_repeat_pattern: Ring = Ring::create( + if let Some( loop_at ) = settings.arpeggio.loop_at { + Vec::from( &arpeggio_envelope[ loop_at.. ] ) + } else { + vec![ arpeggio_envelope[ arpeggio_envelope.len() - 1 ] ] + }, + None + ); + + if volume_envelope.len() < arpeggio_envelope.len() { + let difference = arpeggio_envelope.len() - volume_envelope.len(); + for _ in 0..difference { + volume_envelope.push( + volume_repeat_pattern.next().expect( "fatal: internal error (volume_repeat_pattern)" ) + ); + } + } else if arpeggio_envelope.len() < volume_envelope.len() { + let difference = volume_envelope.len() - arpeggio_envelope.len(); + for _ in 0..difference { + arpeggio_envelope.push( + arpeggio_repeat_pattern.next().expect( "fatal: internal error (arpeggio_repeat_pattern)" ) + ); + } + } + + let interval_size = max( volume_repeat_pattern.len(), arpeggio_repeat_pattern.len() ); + + // now, you generate the repeating portion using the two repeat_pattern iterators. + // each segment at a time is generated by the size of the larger array. + // generate it until the first repeat pattern is regenerated. + // 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 .. (volume repeats from beginning) + // 03 04 05 06 01 02 03 04 05 06 01 02 03 04 05 06 01 02 03 04 .. (arpeggio repeats up from where it left off) + + // 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 .. + // 05 06 01 02 03 04 05 06 01 02 03 04 05 06 01 02 03 04 05 06 .. + + // 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 .. + // 01 02 03 04 05 06 01 02 03 04 05 06 01 02 03 04 05 06 01 02 .. + let mut volume_repeat: Vec = Vec::new(); + let mut arpeggio_repeat: Vec = Vec::new(); + + let mut initial_volume_repeat: Vec = Vec::new(); + let mut initial_arpeggio_repeat: Vec = Vec::new(); for _ in 0..interval_size { - volume_repeat_period.push( volume_repeat_pattern.next().expect( "fatal: internal error (volume_repeat_pattern)" ) ); - arpeggio_repeat_period.push( arpeggio_repeat_pattern.next().expect( "fatal: internal error (arpeggio_repeat_pattern)" ) ); + initial_volume_repeat.push( volume_repeat_pattern.next().expect( "fatal: internal error (volume_repeat_pattern)" ) ); + initial_arpeggio_repeat.push( arpeggio_repeat_pattern.next().expect( "fatal: internal error (arpeggio_repeat_pattern)" ) ); } - // End loop once the pattern begins repeating - if initial_volume_repeat == volume_repeat_period && initial_arpeggio_repeat == arpeggio_repeat_period { - break; - } else { - // If it doesn't repeat, append and generate another period - volume_repeat.append( &mut volume_repeat_period ); - arpeggio_repeat.append( &mut arpeggio_repeat_period ); + volume_repeat.extend( &initial_volume_repeat ); + arpeggio_repeat.extend( &initial_arpeggio_repeat ); + + loop { + // Guard against OOM + if volume_repeat.len() > 65535 { + return Err( "eef error: psg instrument repeat period too long (try modifying repeating envelopes to have evenly-divisible parameters)" )? + } + + let mut volume_repeat_period: Vec = Vec::new(); + let mut arpeggio_repeat_period: Vec = Vec::new(); + for _ in 0..interval_size { + volume_repeat_period.push( volume_repeat_pattern.next().expect( "fatal: internal error (volume_repeat_pattern)" ) ); + arpeggio_repeat_period.push( arpeggio_repeat_pattern.next().expect( "fatal: internal error (arpeggio_repeat_pattern)" ) ); + } + + // End loop once the pattern begins repeating + if initial_volume_repeat == volume_repeat_period && initial_arpeggio_repeat == arpeggio_repeat_period { + break; + } else { + // If it doesn't repeat, append and generate another period + volume_repeat.append( &mut volume_repeat_period ); + arpeggio_repeat.append( &mut arpeggio_repeat_period ); + } } + + // Now generate the Echo EEF envelope from volume_envelope/volume_repeat + arpeggio_envelope/arpeggio_repeat + let mut envelope: Vec = vec![ 0x00 ]; + for i in 0..volume_envelope.len() { + envelope.push( get_eef_volume( volume_envelope[ i ] )? | get_eef_shift( arpeggio_envelope[ i ] )? ); + } + + // Echo begin loop command + envelope.push( 0xFE ); + + for i in 0..volume_repeat.len() { + envelope.push( get_eef_volume( volume_repeat[ i ] )? | get_eef_shift( arpeggio_repeat[ i ] )? ); + } + + // Echo end loop command + envelope.push( 0xFF ); + + files.insert( format!( "{}.eef", if instrument.name.is_empty() { Uuid::new_v4().to_string() } else { instrument.name.to_owned() } ), envelope ); } - - // Now generate the Echo EEF envelope from volume_envelope/volume_repeat + arpeggio_envelope/arpeggio_repeat - let mut envelope: Vec = vec![ 0x00 ]; - for i in 0..volume_envelope.len() { - envelope.push( get_eef_volume( volume_envelope[ i ] )? | get_eef_shift( arpeggio_envelope[ i ] )? ); - } - - // Echo begin loop command - envelope.push( 0xFE ); - - for i in 0..volume_repeat.len() { - envelope.push( get_eef_volume( volume_repeat[ i ] )? | get_eef_shift( arpeggio_repeat[ i ] )? ); - } - - // Echo end loop command - envelope.push( 0xFF ); - - eefs.insert( format!( "{}.eef", if instrument.name.is_empty() { Uuid::new_v4().to_string() } else { instrument.name.to_owned() } ), envelope ); } } - Ok( eefs ) - } - - fn get_eifs( &self ) -> Result>, Box> { - let mut eifs: HashMap> = HashMap::new(); - - for instrument in &self.instruments { - if let InstrumentType::Fm2612( settings ) = &instrument.instrument_type { - // Create feedback + algorithm byte - let alg_fb: u8 = ( settings.fb << 3 ) | settings.alg; - - // The operators are laid out as FmOperator objects in order 1 -> 3 -> 2 -> 4 - // EIF instrument layout below - let mut mult_dt: [u8; 4] = [ 0x00, 0x00, 0x00, 0x00 ]; - let mut tl: [u8; 4] = [ 0x00, 0x00, 0x00, 0x00 ]; - let mut ar_rs: [u8; 4] = [ 0x00, 0x00, 0x00, 0x00 ]; - let mut dr_am: [u8; 4] = [ 0x00, 0x00, 0x00, 0x00 ]; - let mut sr: [u8; 4] = [ 0x00, 0x00, 0x00, 0x00 ]; - let mut rr_sl: [u8; 4] = [ 0x00, 0x00, 0x00, 0x00 ]; - let mut ssg_eg: [u8; 4] = [ 0x00, 0x00, 0x00, 0x00 ]; - - // Likewise, "operators" is laid out 1 -> 3 -> 2 -> 4 - for i in 0..4 { - let dt: u8 = match settings.operators[ i ].dt { - 0 => 0b000, - 1 => 0b001, - 2 => 0b010, - 3 => 0b011, - -1 => 0b101, - -2 => 0b110, - -3 => 0b111, - _ => return Err( "invalid file: invalid value given for dt" )? - }; - - // https://plutiedev.com/ym2612-registers - // https://github.com/sikthehedgehog/Echo/blob/master/doc/eif.txt - mult_dt[ i ] = ( dt << 4 ) | settings.operators[ i ].mult; - tl[ i ] = settings.operators[ i ].tl; - ar_rs[ i ] = ( settings.operators[ i ].rs << 6 ) | settings.operators[ i ].ar; - dr_am[ i ] = ( settings.operators[ i ].am << 7 ) | settings.operators[ i ].dr; - sr[ i ] = settings.operators[ i ].d2r; // "Sometimes also called 'second decay rate' (D2R)." - rr_sl[ i ] = ( settings.operators[ i ].sl << 4 ) | settings.operators[ i ].rr; - - if settings.operators[ i ].ssg_mode > 0 { - print_warning( &format!( "SSG-EG mode set on instrument {}, operator {}. this operator may not work on clone hardware or certain emulators", instrument.name, i ) ); - } - - ssg_eg[ i ] = settings.operators[ i ].ssg_mode; - } - - let mut eif: Vec = Vec::new(); - eif.push( alg_fb ); - eif.extend( mult_dt ); - eif.extend( tl ); - eif.extend( ar_rs ); - eif.extend( dr_am ); - eif.extend( sr ); - eif.extend( rr_sl ); - eif.extend( ssg_eg ); - - eifs.insert( format!( "{}.eif", if instrument.name.is_empty() { Uuid::new_v4().to_string() } else { instrument.name.to_owned() } ), eif ); - } - } - - Ok( eifs ) - } - - fn get_ewfs( &self ) -> Result>, Box> { - let mut ewfs: HashMap> = HashMap::new(); - for sample in &self.samples { // Amplify data in original sample let data: Vec = sample.data @@ -877,10 +864,10 @@ impl EchoFormat for DmfModule { // Terminate stream data.push( 0xFF ); - ewfs.insert( format!( "{}.ewf", if sample.name.is_empty() { Uuid::new_v4().to_string() } else { sample.name.to_owned() } ), data ); + files.insert( format!( "{}.ewf", if sample.name.is_empty() { Uuid::new_v4().to_string() } else { sample.name.to_owned() } ), data ); } - Ok( ewfs ) + Ok( files ) } fn get_esf( &self ) -> Result, Box> {