Refactor and prepare for multiple files
parent
714b28f1aa
commit
62790d4c9c
|
@ -14,6 +14,5 @@ flate2 = "1.0.26"
|
|||
samplerate = "0.2.4"
|
||||
hound = "3.5.0"
|
||||
pitch_shift = "1.0.0"
|
||||
uuid = { version = "1.4.1", features = [ "v4" ] }
|
||||
linked_hash_set = "0.1.4"
|
||||
convert_case = "0.6.0"
|
|
@ -21,9 +21,11 @@ pub fn run_command() -> Result<(), Box<dyn Error>> {
|
|||
}
|
||||
),
|
||||
Tools::Soundtrack { input_file, output_file, input_format: _, output_format: _, source_file_format: _, artifact_output_directory, soundtrack_label, instrument_list_label } => {
|
||||
let result = DmfModule::from_file( &input_file )?;
|
||||
let result: Vec<DmfModule> = input_file.iter().map( | filename | Ok( DmfModule::from_file( &filename )? ) ).collect::<Result<Vec<DmfModule>, Box<dyn Error>>>()?;
|
||||
// very temporary!
|
||||
let result: &DmfModule = result.first().ok_or( "must provide input_file" )?;
|
||||
|
||||
for ( filename, data ) in result.get_artifacts( &soundtrack_label, &output_file, &artifact_output_directory, &instrument_list_label )? {
|
||||
for ( filename, data ) in result.get_artifacts( 0, &soundtrack_label, &output_file, &artifact_output_directory, &instrument_list_label )? {
|
||||
println!( "Creating artifact file {}{}", artifact_output_directory, filename );
|
||||
let mut file = File::create( format!( "{}{}", artifact_output_directory, filename ) )?;
|
||||
file.write_all( &data )?
|
||||
|
|
|
@ -66,9 +66,9 @@ pub enum Tools {
|
|||
Soundtrack {
|
||||
/// Input filename
|
||||
#[arg(short, long)]
|
||||
input_file: String,
|
||||
input_file: Vec<String>,
|
||||
|
||||
/// Output filename
|
||||
/// Output filename (if multiple input_files are provided, used as output parent directory)
|
||||
#[arg(short, long)]
|
||||
output_file: String,
|
||||
|
||||
|
|
|
@ -1,24 +1,20 @@
|
|||
use std::{collections::HashMap, cmp::{max, min}, error::Error};
|
||||
use convert_case::{Case, Casing};
|
||||
use samplerate::convert;
|
||||
use uuid::Uuid;
|
||||
use crate::reskit::{soundtrack::{formats::dmf::{DmfModule, ECHO_EWF_SAMPLE_RATE}, types::{InstrumentType, SampleFormat, Channel, PatternRow, Effect}}, utility::{print_warning, Ring, print_info}};
|
||||
use crate::reskit::{soundtrack::{formats::dmf::{DmfModule, ECHO_EWF_SAMPLE_RATE}, types::{InstrumentType, SampleFormat, Channel, PatternRow, Effect}}, utility::{print_warning, Ring}};
|
||||
use super::engine::{EchoFormat, ESF_FM_1, ESF_FM_2, ESF_FM_3, ESF_FM_4, ESF_FM_5, ESF_FM_6, ESF_PSG_1, ESF_PSG_2, ESF_PSG_3, ESF_PSG_4, EchoEvent, get_events_for_row, compact_delays, ESF_SET_LOOP, ESF_GO_TO_LOOP, ESF_STOP};
|
||||
|
||||
|
||||
impl EchoFormat for DmfModule {
|
||||
|
||||
fn get_artifacts( &self, soundtrack_label: &str, soundtrack_path: &str, artifact_path: &str, instr_list_label: &str ) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>> {
|
||||
fn get_artifacts( &self, file_index: u32, soundtrack_label: &str, soundtrack_path: &str, artifact_path: &str, instr_list_label: &str ) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>> {
|
||||
let mut files: HashMap<String, Vec<u8>> = HashMap::new();
|
||||
// This next list preserves order of filenames to generate the echo .asm file
|
||||
let mut instrument_filenames: Vec<String> = Vec::new();
|
||||
|
||||
let mut index = 0;
|
||||
for instrument in &self.instruments {
|
||||
match &instrument.instrument_type {
|
||||
InstrumentType::Fm2612( settings ) => {
|
||||
let uuid = Uuid::new_v4().to_string();
|
||||
instrument_filenames.push( format!( "{}.eif", if instrument.name.is_empty() { format!( "fm_{}", uuid ) } else { instrument.name.to_owned() } ) );
|
||||
|
||||
// Create feedback + algorithm byte
|
||||
let alg_fb: u8 = ( settings.fb << 3 ) | settings.alg;
|
||||
|
||||
|
@ -71,12 +67,11 @@ impl EchoFormat for DmfModule {
|
|||
eif.extend( rr_sl );
|
||||
eif.extend( ssg_eg );
|
||||
|
||||
files.insert( format!( "{}.eif", if instrument.name.is_empty() { format!( "fm_{}", uuid ) } else { instrument.name.to_owned() } ), eif );
|
||||
let filename = format!( "file{}_ins{}_{}.eif", file_index, index, instrument.name );
|
||||
instrument_filenames.push( filename.clone() );
|
||||
files.insert( filename, eif );
|
||||
},
|
||||
InstrumentType::PsgDcsg( settings ) => {
|
||||
let uuid = Uuid::new_v4().to_string();
|
||||
instrument_filenames.push( format!( "{}.eef", if instrument.name.is_empty() { format!( "psg_{}", uuid ) } else { instrument.name.to_owned() } ) );
|
||||
|
||||
// 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.
|
||||
|
@ -204,15 +199,17 @@ impl EchoFormat for DmfModule {
|
|||
// Echo end loop command
|
||||
envelope.push( 0xFF );
|
||||
|
||||
files.insert( format!( "{}.eef", if instrument.name.is_empty() { format!( "psg_{}", uuid ) } else { instrument.name.to_owned() } ), envelope );
|
||||
let filename = format!( "file{}_ins{}_{}.eef", file_index, index, instrument.name );
|
||||
instrument_filenames.push( filename.clone() );
|
||||
files.insert( filename, envelope );
|
||||
}
|
||||
}
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
index = 0;
|
||||
for sample in &self.samples {
|
||||
let uuid = Uuid::new_v4().to_string();
|
||||
instrument_filenames.push( format!( "{}.ewf", if sample.name.is_empty() { format!( "sample_{}", uuid ) } else { sample.name.to_owned() } ) );
|
||||
|
||||
// Amplify data in original sample
|
||||
let data: Vec<i16> = sample.data
|
||||
.iter()
|
||||
|
@ -262,7 +259,11 @@ impl EchoFormat for DmfModule {
|
|||
// Terminate stream
|
||||
data.push( 0xFF );
|
||||
|
||||
files.insert( format!( "{}.ewf", if sample.name.is_empty() { format!( "sample_{}", uuid ) } else { sample.name.to_owned() } ), data );
|
||||
let filename = format!( "file{}_sample{}_{}.ewf", file_index, index, sample.name );
|
||||
instrument_filenames.push( filename.clone() );
|
||||
files.insert( filename, data );
|
||||
|
||||
index += 1;
|
||||
}
|
||||
|
||||
// Write Echo ASM file that includes all the instruments
|
||||
|
|
|
@ -69,7 +69,7 @@ pub type EchoEvent = Vec<u8>;
|
|||
|
||||
pub trait EchoFormat {
|
||||
|
||||
fn get_artifacts( &self, soundtrack_label: &str, soundtrack_path: &str, artifact_path: &str, instrument_list_label: &str ) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>>;
|
||||
fn get_artifacts( &self, file_index: u32, soundtrack_label: &str, soundtrack_path: &str, artifact_path: &str, instrument_list_label: &str ) -> Result<HashMap<String, Vec<u8>>, Box<dyn Error>>;
|
||||
|
||||
fn get_esf( &self ) -> Result<Vec<u8>, Box<dyn Error>>;
|
||||
|
||||
|
|
|
@ -2,14 +2,14 @@ use std::{collections::HashMap, error::Error};
|
|||
|
||||
use linked_hash_set::LinkedHashSet;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PsgEnvelope< DataFormat > {
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct PsgEnvelope< DataFormat: PartialEq > {
|
||||
pub envelope: Vec<DataFormat>,
|
||||
pub loop_at: Option<usize>,
|
||||
pub settings: HashMap<&'static str, bool>
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct PsgSettings {
|
||||
pub volume: PsgEnvelope< u32 >,
|
||||
pub arpeggio: PsgEnvelope< i32 >,
|
||||
|
@ -17,7 +17,7 @@ pub struct PsgSettings {
|
|||
pub wavetable: PsgEnvelope< u32 >
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct Fm2612Operator {
|
||||
pub am: u8,
|
||||
pub ar: u8,
|
||||
|
@ -33,7 +33,7 @@ pub struct Fm2612Operator {
|
|||
pub ssg_mode: u8
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub struct Fm2612Settings {
|
||||
pub alg: u8,
|
||||
pub fb: u8,
|
||||
|
@ -42,7 +42,7 @@ pub struct Fm2612Settings {
|
|||
pub operators: [Fm2612Operator; 4]
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum InstrumentType {
|
||||
Fm2612( Fm2612Settings ),
|
||||
PsgDcsg( PsgSettings )
|
||||
|
|
Loading…
Reference in New Issue