From 2f84d47d991d1706e9fff81ece829d75091ed058 Mon Sep 17 00:00:00 2001 From: ashley Date: Sun, 27 Aug 2023 12:54:13 -0400 Subject: [PATCH] Ability to use PSG channels without defined instrument in track --- src/reskit/soundtrack/engines/echo/dmf.rs | 12 ++++- src/reskit/soundtrack/formats/dmf.rs | 66 ++++++++++++++++++++++- src/reskit/soundtrack/types.rs | 1 - 3 files changed, 75 insertions(+), 4 deletions(-) diff --git a/src/reskit/soundtrack/engines/echo/dmf.rs b/src/reskit/soundtrack/engines/echo/dmf.rs index 4e8104a..111dfa6 100644 --- a/src/reskit/soundtrack/engines/echo/dmf.rs +++ b/src/reskit/soundtrack/engines/echo/dmf.rs @@ -2,7 +2,7 @@ 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}}, 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}; @@ -88,6 +88,16 @@ impl EchoFormat for DmfModule { 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(); + // 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, // 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 diff --git a/src/reskit/soundtrack/formats/dmf.rs b/src/reskit/soundtrack/formats/dmf.rs index 6879792..2b5b76f 100644 --- a/src/reskit/soundtrack/formats/dmf.rs +++ b/src/reskit/soundtrack/formats/dmf.rs @@ -1,7 +1,7 @@ use std::{error::Error, fs::File, io::Read, convert::TryInto, collections::HashMap}; use flate2::read::ZlibDecoder; 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_SUPPORTED_VERSION: u8 = 27; @@ -432,7 +432,7 @@ impl DmfModule { _ => 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 @@ -581,6 +581,40 @@ impl DmfModule { 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:" ); let mut samples: Vec = Vec::new(); let num_samples = get_u8( bytes.by_ref() )?; @@ -629,4 +663,32 @@ impl DmfModule { ) } + pub fn uses_default_psg( channel_patterns: &Vec> ) -> Result> { + 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 ) + } + } \ No newline at end of file diff --git a/src/reskit/soundtrack/types.rs b/src/reskit/soundtrack/types.rs index 518c3fc..b17506b 100644 --- a/src/reskit/soundtrack/types.rs +++ b/src/reskit/soundtrack/types.rs @@ -50,7 +50,6 @@ pub enum InstrumentType { #[derive(Debug)] pub struct Instrument { - pub index: usize, pub name: String, pub instrument_type: InstrumentType }