Ability to use PSG channels without defined instrument in track

master
Ashley N. 2023-08-27 12:54:13 -04:00
parent a1eb158292
commit 2f84d47d99
3 changed files with 75 additions and 4 deletions

View File

@ -2,7 +2,7 @@ use std::{collections::HashMap, cmp::{max, min}, error::Error};
use convert_case::{Case, Casing}; use convert_case::{Case, Casing};
use samplerate::convert; use samplerate::convert;
use uuid::Uuid; use uuid::Uuid;
use crate::reskit::{soundtrack::{formats::dmf::{DmfModule, ECHO_EWF_SAMPLE_RATE}, types::{InstrumentType, SampleFormat, Channel, PatternRow}}, utility::{print_warning, Ring}}; use crate::reskit::{soundtrack::{formats::dmf::{DmfModule, ECHO_EWF_SAMPLE_RATE}, types::{InstrumentType, SampleFormat, Channel, PatternRow}}, utility::{print_warning, Ring, print_info}};
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}; 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};
@ -88,6 +88,16 @@ impl EchoFormat for DmfModule {
let mut volume_envelope: Vec<u8> = settings.volume.envelope.iter().map( | long | *long as u8 ).collect(); let mut volume_envelope: Vec<u8> = settings.volume.envelope.iter().map( | long | *long as u8 ).collect();
let mut arpeggio_envelope: Vec<i8> = settings.arpeggio.envelope.iter().map( | long | *long as i8 ).collect(); let mut arpeggio_envelope: Vec<i8> = settings.arpeggio.envelope.iter().map( | long | *long as i8 ).collect();
// Validations (this makes my life easier when dmf format changes again)
if volume_envelope.is_empty() {
// Push one max volume if there is no volume defined in this instrument
volume_envelope.push( 0x0F );
}
if arpeggio_envelope.is_empty() {
// Push one byte of no arpeggio shift if no arpeggio is defined in this instrument
arpeggio_envelope.push( 0x00 );
}
// now take a slice of the repeatable portion of the smaller array, // 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 // 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 // larger array. if there is no repeatable portion, take a one-element

View File

@ -1,7 +1,7 @@
use std::{error::Error, fs::File, io::Read, convert::TryInto, collections::HashMap}; use std::{error::Error, fs::File, io::Read, convert::TryInto, collections::HashMap};
use flate2::read::ZlibDecoder; use flate2::read::ZlibDecoder;
use linked_hash_set::LinkedHashSet; use linked_hash_set::LinkedHashSet;
use crate::reskit::{utility::{get_string, get_u8, skip, get_u32, get_i8, get_i32, get_u16, get_i16, print_info}, soundtrack::types::{SampleFormat, PsgEnvelope, Note, Sample, PsgSettings, PatternRow, Effect, DcsgChannelMode, NoiseType, Instrument, InstrumentType, Fm2612Operator, Fm2612Settings}}; use crate::reskit::{utility::{get_string, get_u8, skip, get_u32, get_i8, get_i32, get_u16, get_i16, print_info}, soundtrack::types::{SampleFormat, PsgEnvelope, Note, Sample, PsgSettings, PatternRow, Effect, DcsgChannelMode, NoiseType, Instrument, InstrumentType, Fm2612Operator, Fm2612Settings, Channel}};
const DMF_MAGIC_NUMBER: &'static str = ".DelekDefleMask."; const DMF_MAGIC_NUMBER: &'static str = ".DelekDefleMask.";
const DMF_SUPPORTED_VERSION: u8 = 27; const DMF_SUPPORTED_VERSION: u8 = 27;
@ -432,7 +432,7 @@ impl DmfModule {
_ => return Err( format!( "invalid file: invalid instrument mode {}", mode ) )? _ => return Err( format!( "invalid file: invalid instrument mode {}", mode ) )?
}; };
instruments.push( Instrument { index: i as usize, name, instrument_type } ); instruments.push( Instrument { name, instrument_type } );
} }
// Version 27 of DMF specifies "1 wavetable of 0 bytes" for sega megadrive // Version 27 of DMF specifies "1 wavetable of 0 bytes" for sega megadrive
@ -581,6 +581,40 @@ impl DmfModule {
channel_patterns.push( channel_rows ); channel_patterns.push( channel_rows );
} }
// If at least one PSG channel never defines an instrument, insert the default PSG instrument
// which is a repeat of zero attentuation.
print_info( "Default PSG instruments:" );
if DmfModule::uses_default_psg( &channel_patterns )? {
print_info( "One or more channels use the default PSG instrument" );
instruments.push( Instrument{
name: format!( "Default PSG Instrument" ),
instrument_type: InstrumentType::PsgDcsg( PsgSettings {
volume: PsgEnvelope {
envelope: vec![ 0x0F as u32 ],
loop_at: Some( 0 ),
settings: HashMap::new()
},
arpeggio: PsgEnvelope {
envelope: vec![],
loop_at: None,
settings: HashMap::new()
},
noise: PsgEnvelope {
envelope: vec![],
loop_at: None,
settings: HashMap::new()
},
wavetable: PsgEnvelope {
envelope: vec![],
loop_at: None,
settings: HashMap::new()
}
} )
} )
} else {
print_info( "All PSG channels set instruments" );
}
print_info( "PCM Samples:" ); print_info( "PCM Samples:" );
let mut samples: Vec<Sample> = Vec::new(); let mut samples: Vec<Sample> = Vec::new();
let num_samples = get_u8( bytes.by_ref() )?; let num_samples = get_u8( bytes.by_ref() )?;
@ -629,4 +663,32 @@ impl DmfModule {
) )
} }
pub fn uses_default_psg( channel_patterns: &Vec<Vec<PatternRow>> ) -> Result<bool, Box<dyn Error>> {
let mut channels: [Channel; 3] = [
Channel::new( 1 ),
Channel::new( 2 ),
Channel::new( 3 )
];
for channel_id in 6..9 {
let channel_patterns = &channel_patterns[ channel_id ];
for row in channel_patterns {
// If a set instrument is encountered, just set the instrument on the channel
// We will check all three channels to see if at least one does not contain
// an active instrument.
if row.instrument_index.is_some() {
channels[ channel_id - 6 ].active_instrument = Some( 0 );
}
}
}
for channel in channels {
if channel.active_instrument.is_none() {
return Ok( true )
}
}
Ok( false )
}
} }

View File

@ -50,7 +50,6 @@ pub enum InstrumentType {
#[derive(Debug)] #[derive(Debug)]
pub struct Instrument { pub struct Instrument {
pub index: usize,
pub name: String, pub name: String,
pub instrument_type: InstrumentType pub instrument_type: InstrumentType
} }