get_instruments refactor

master
Ashley N. 2023-08-25 22:18:15 -04:00
parent 71c95cb205
commit b4fffa8ea9
3 changed files with 173 additions and 201 deletions

View File

@ -84,20 +84,8 @@ fn run() -> Result<(), Box<dyn Error>> {
let result = DmfModule::from_file( &input_filename )?; let result = DmfModule::from_file( &input_filename )?;
for ( filename, data ) in result.get_eefs()? { for ( filename, data ) in result.get_instruments()? {
println!( "Creating EEF instrument file {}", filename ); println!( "Creating 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 );
let mut file = File::create( filename )?; let mut file = File::create( filename )?;
file.write_all( &data )? file.write_all( &data )?
} }

View File

@ -1,7 +1,7 @@
use std::{collections::HashMap, error::Error}; use std::{collections::HashMap, error::Error};
use linked_hash_set::LinkedHashSet; 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 // https://github.com/sikthehedgehog/Echo/blob/master/doc/esf.txt
const ESF_NOTE_ON: u8 = 0x00; const ESF_NOTE_ON: u8 = 0x00;
@ -55,13 +55,10 @@ pub type EchoEvent = Vec<u8>;
pub trait EchoFormat { pub trait EchoFormat {
fn get_eefs( &self ) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>>; fn get_instruments( &self ) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>>;
fn get_eifs( &self ) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>>;
fn get_ewfs( &self ) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>>;
fn get_esf( &self ) -> Result<Vec<u8>, Box<dyn Error>>; fn get_esf( &self ) -> Result<Vec<u8>, Box<dyn Error>>;
} }
fn get_semitone( note: &Note ) -> Result<u8, Box<dyn Error>> { fn get_semitone( note: &Note ) -> Result<u8, Box<dyn Error>> {

View File

@ -631,11 +631,67 @@ impl DmfModule {
impl EchoFormat for DmfModule { impl EchoFormat for DmfModule {
fn get_eefs( &self ) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>> { fn get_instruments( &self ) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>> {
let mut eefs: HashMap<String, Vec<u8>> = HashMap::new(); let mut files: HashMap<String, Vec<u8>> = HashMap::new();
for instrument in &self.instruments { for instrument in &self.instruments {
if let InstrumentType::PsgDcsg( settings ) = &instrument.instrument_type { match &instrument.instrument_type {
InstrumentType::Fm2612( settings ) => {
// 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<u8> = 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 // Echo uses volume and arpeggio envelopes
// Noise envelopes are usable but cannot be articulated in an instrument. // 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. // For use of the noise envelope, use ESF $0Bnn/$3Bnn and $3Ann in the stream.
@ -753,80 +809,11 @@ impl EchoFormat for DmfModule {
// Echo end loop command // Echo end loop command
envelope.push( 0xFF ); envelope.push( 0xFF );
eefs.insert( format!( "{}.eef", if instrument.name.is_empty() { Uuid::new_v4().to_string() } else { instrument.name.to_owned() } ), envelope ); files.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<HashMap<String, Vec<u8>>, Box<dyn Error>> {
let mut eifs: HashMap<String, Vec<u8>> = 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<u8> = 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<HashMap<String, Vec<u8>>, Box<dyn Error>> {
let mut ewfs: HashMap<String, Vec<u8>> = HashMap::new();
for sample in &self.samples { for sample in &self.samples {
// Amplify data in original sample // Amplify data in original sample
let data: Vec<i16> = sample.data let data: Vec<i16> = sample.data
@ -877,10 +864,10 @@ impl EchoFormat for DmfModule {
// Terminate stream // Terminate stream
data.push( 0xFF ); 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<Vec<u8>, Box<dyn Error>> { fn get_esf( &self ) -> Result<Vec<u8>, Box<dyn Error>> {