Apply noise generator notes minus required cross-channel event for psg3 mode

master
Ashley N. 2023-08-27 20:01:40 -04:00
parent c7c703ef65
commit 9d82240445
2 changed files with 79 additions and 6 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, print_info}}; use crate::reskit::{soundtrack::{formats::dmf::{DmfModule, ECHO_EWF_SAMPLE_RATE}, types::{InstrumentType, SampleFormat, Channel, PatternRow, Effect, DcsgChannelMode, NoiseType}}, 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};

View File

@ -1,6 +1,6 @@
use std::{collections::HashMap, error::Error, cmp::{min, max}}; use std::{collections::HashMap, error::Error, cmp::{min, max}};
use linked_hash_set::LinkedHashSet; use linked_hash_set::LinkedHashSet;
use crate::reskit::{soundtrack::types::{PatternRow, Note, Channel, OctaveFrequency, Effect, Instrument}, utility::print_warning}; use crate::reskit::{soundtrack::types::{PatternRow, Note, Channel, OctaveFrequency, Effect, Instrument, NoiseType, DcsgChannelMode}, 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;
@ -32,6 +32,15 @@ pub const ESF_PSG_2: u8 = 0x09;
pub const ESF_PSG_3: u8 = 0x0A; pub const ESF_PSG_3: u8 = 0x0A;
pub const ESF_PSG_4: u8 = 0x0B; pub const ESF_PSG_4: u8 = 0x0B;
pub const ESF_NOISE_PERIODIC_HIGH: u8 = 0x00;
pub const ESF_NOISE_PERIODIC_MED: u8 = 0x01;
pub const ESF_NOISE_PERIODIC_LOW: u8 = 0x02;
pub const ESF_NOISE_PERIODIC_PSG_3: u8 = 0x03;
pub const ESF_NOISE_WHITE_HIGH: u8 = 0x04;
pub const ESF_NOISE_WHITE_MED: u8 = 0x05;
pub const ESF_NOISE_WHITE_LOW: u8 = 0x06;
pub const ESF_NOISE_WHITE_PSG_3: u8 = 0x07;
pub const ESF_FM_6_PCM: u8 = 0x0C; pub const ESF_FM_6_PCM: u8 = 0x0C;
const ESF_FM_PARAM_LEFT_SPEAKER: u8 = 0x80; const ESF_FM_PARAM_LEFT_SPEAKER: u8 = 0x80;
@ -180,6 +189,45 @@ pub fn get_psg_octave_from_frequency( frequency: i16 ) -> u8 {
} }
} }
/**
* Get the setting for the noise generator based on the active noise mode effect.
*/
pub fn get_noise_generator_setting( note: &Note, noise_mode: Option<&Effect> ) -> Result<u8, Box<dyn Error>> {
Ok(
if let Some( noise_mode ) = noise_mode {
if let Effect::DcsgNoiseMode { mode, noise_type } = noise_mode {
match noise_type {
NoiseType::PeriodicNoise => match mode {
DcsgChannelMode::Ch3Frequency => ESF_NOISE_PERIODIC_PSG_3,
DcsgChannelMode::FixedFrequency => match note {
Note::NoteOff => return Err( "internal error: attempted to get noise generator setting for NoteOff" )?,
Note::C(_) => ESF_NOISE_PERIODIC_LOW,
Note::CSharp(_) => ESF_NOISE_PERIODIC_MED,
Note::D(_) | _ => ESF_NOISE_PERIODIC_HIGH
}
},
NoiseType::WhiteNoise => match mode {
DcsgChannelMode::Ch3Frequency => ESF_NOISE_WHITE_PSG_3,
DcsgChannelMode::FixedFrequency => match note {
Note::NoteOff => return Err( "internal error: attempted to get noise generator setting for NoteOff" )?,
Note::C(_) => ESF_NOISE_WHITE_LOW,
Note::CSharp(_) => ESF_NOISE_WHITE_MED,
Note::D(_) | _ => ESF_NOISE_WHITE_HIGH
}
}
}
} else {
return Err( "internal error: non-DcsgNoiseMode effect passed to get_noise_generator_setting" )?
}
} else {
match note {
Note::NoteOff => return Err( "internal error: attempted to get noise generator setting for NoteOff" )?,
_ => ESF_NOISE_WHITE_PSG_3
}
}
)
}
pub fn get_fm_note_byte( octave: u8, semitone: u8 ) -> u8 { pub fn get_fm_note_byte( octave: u8, semitone: u8 ) -> u8 {
32 * octave + 2 * semitone + 1 32 * octave + 2 * semitone + 1
} }
@ -243,9 +291,9 @@ pub fn get_events_for_channel( channel: &mut Channel, row: &PatternRow, pcm_offs
channel.active_instrument = Some( *instrument ); channel.active_instrument = Some( *instrument );
} }
} else { } else {
// This is SN1, SN2, or SN3 with no active instrument // This is SN1, SN2, SN3, or SN4 with no active instrument
// Check if the channel needs "__reskit_default_psg_instrument" set before a note is set // Check if the channel needs "__reskit_default_psg_instrument" set before a note is set
if channel.active_instrument.is_none() && ( ESF_PSG_1..=ESF_PSG_3 ).contains( &channel.id ) { if channel.active_instrument.is_none() && ( ESF_PSG_1..=ESF_PSG_4 ).contains( &channel.id ) {
if row.note.is_some() { if row.note.is_some() {
// Set "__reskit_default_psg_instrument" // Set "__reskit_default_psg_instrument"
let instrument = default_psg_instr_index.ok_or( "internal error: no default psg instrument to apply" )?; let instrument = default_psg_instr_index.ok_or( "internal error: no default psg instrument to apply" )?;
@ -267,6 +315,11 @@ pub fn get_events_for_channel( channel: &mut Channel, row: &PatternRow, pcm_offs
octave: note.get_octave()?, octave: note.get_octave()?,
frequency: if ( ESF_PSG_1..=ESF_PSG_3 ).contains( &channel.id ) { frequency: if ( ESF_PSG_1..=ESF_PSG_3 ).contains( &channel.id ) {
get_psg_semitone_frequency( note.get_octave()?, get_semitone( note )? )? get_psg_semitone_frequency( note.get_octave()?, get_semitone( note )? )?
} else if channel.id == ESF_PSG_4 {
// probably doesn't matter a damn what we put here, the OctaveFrequency is used only for portamentos.
// and you can't portamento on PSG_4 (you can, however, portamento the frequency set on PSG_3, but
// we'll get to cross-channel generated events in a bit.)
0
} else { } else {
get_fm_semitone_frequency( get_semitone( note )? )? get_fm_semitone_frequency( get_semitone( note )? )?
} }
@ -275,6 +328,10 @@ pub fn get_events_for_channel( channel: &mut Channel, row: &PatternRow, pcm_offs
if channel.id == ESF_FM_6_PCM { if channel.id == ESF_FM_6_PCM {
events.push( vec![ ESF_NOTE_ON | channel.id, get_pcm_index( note )? + pcm_offset ] ); events.push( vec![ ESF_NOTE_ON | channel.id, get_pcm_index( note )? + pcm_offset ] );
} else if channel.id == ESF_PSG_4 {
// Find the single (and it should only ever be a single) Effect::DcsgNoiseMode attached to the channel
let dcsg_noise_mode = channel.active_effects.iter().find( | effect | matches!( effect, Effect::DcsgNoiseMode { mode: _, noise_type: _ } ) );
events.push( vec![ ESF_NOTE_ON | channel.id, get_noise_generator_setting( note, dcsg_noise_mode )? ] );
} else { } else {
let note = get_note( note, channel.id )?; let note = get_note( note, channel.id )?;
events.push( vec![ ESF_NOTE_ON | channel.id, note ] ); events.push( vec![ ESF_NOTE_ON | channel.id, note ] );
@ -395,6 +452,22 @@ pub fn apply_effects_to_channel( channel: &mut Channel, effects: &LinkedHashSet<
} }
} }
}, },
Effect::DcsgNoiseMode { mode, noise_type } => {
let mode = *mode;
let noise_type = *noise_type;
if channel.id == ESF_PSG_4 {
channel.active_effects = channel.active_effects
.clone()
.into_iter()
.filter( | effect | !( matches!( effect, Effect::DcsgNoiseMode { mode: _, noise_type: _ } ) ) )
.collect();
channel.active_effects.insert( Effect::DcsgNoiseMode { mode, noise_type } );
} else {
print_warning( "Effect:DcsgNoiseMode can only be applied to SN4. ignoring..." );
}
},
unsupported_effect => print_warning( &format!( "effect unsupported: {:?}. your soundtrack may sound different than expected.", unsupported_effect ) ) unsupported_effect => print_warning( &format!( "effect unsupported: {:?}. your soundtrack may sound different than expected.", unsupported_effect ) )
} }
} }
@ -465,9 +538,9 @@ pub fn compact_delays( events: Vec<EchoEvent> ) -> Result<Vec<EchoEvent>, Box<dy
*/ */
pub fn get_portamento( channel: &mut Channel, portamento_effect: &Effect ) -> Result<EchoEvent, Box<dyn Error>> { pub fn get_portamento( channel: &mut Channel, portamento_effect: &Effect ) -> Result<EchoEvent, Box<dyn Error>> {
Ok( Ok(
if channel.id == ESF_FM_6_PCM { if channel.id == ESF_FM_6_PCM || channel.id == ESF_PSG_4 {
// Generate no events if channel is set to pcm // Generate no events if channel is set to pcm
print_warning( "attempted to portamento on FM6 when it is set to pcm. this makes no sense and does nothing" ); print_warning( "attempted to portamento on FM6 when it is set to pcm, or on SN4. this makes no sense and does nothing" );
vec![] vec![]
} else if let Some( octave_frequency ) = channel.active_note { } else if let Some( octave_frequency ) = channel.active_note {
let ( octave, frequency ) = { let ( octave, frequency ) = {