Apply noise generator notes minus required cross-channel event for psg3 mode
parent
c7c703ef65
commit
9d82240445
|
@ -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, 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};
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::{collections::HashMap, error::Error, cmp::{min, max}};
|
||||
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
|
||||
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_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;
|
||||
|
||||
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 {
|
||||
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 );
|
||||
}
|
||||
} 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
|
||||
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() {
|
||||
// Set "__reskit_default_psg_instrument"
|
||||
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()?,
|
||||
frequency: if ( ESF_PSG_1..=ESF_PSG_3 ).contains( &channel.id ) {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
let note = get_note( note, channel.id )?;
|
||||
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 ) )
|
||||
}
|
||||
}
|
||||
|
@ -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>> {
|
||||
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
|
||||
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![]
|
||||
} else if let Some( octave_frequency ) = channel.active_note {
|
||||
let ( octave, frequency ) = {
|
||||
|
|
Loading…
Reference in New Issue