diff --git a/src/reskit/soundtrack/engines/echo/engine.rs b/src/reskit/soundtrack/engines/echo/engine.rs index 7217847..9a98246 100644 --- a/src/reskit/soundtrack/engines/echo/engine.rs +++ b/src/reskit/soundtrack/engines/echo/engine.rs @@ -192,27 +192,29 @@ 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> { +pub fn get_noise_generator_setting( note: &Note, noise_mode: Option<&Effect> ) -> Result, Box> { 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::Ch3Frequency => Some( 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 + Note::C(_) => Some( ESF_NOISE_PERIODIC_LOW ), + Note::CSharp(_) => Some( ESF_NOISE_PERIODIC_MED ), + Note::D(_) => Some( ESF_NOISE_PERIODIC_HIGH ), + _ => None } }, NoiseType::WhiteNoise => match mode { - DcsgChannelMode::Ch3Frequency => ESF_NOISE_WHITE_PSG_3, + DcsgChannelMode::Ch3Frequency => Some( 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 + Note::C(_) => Some( ESF_NOISE_WHITE_LOW ), + Note::CSharp(_) => Some( ESF_NOISE_WHITE_MED ), + Note::D(_) => Some( ESF_NOISE_WHITE_HIGH ), + _ => None } } } @@ -222,7 +224,7 @@ pub fn get_noise_generator_setting( note: &Note, noise_mode: Option<&Effect> ) - } else { match note { Note::NoteOff => return Err( "internal error: attempted to get noise generator setting for NoteOff" )?, - _ => ESF_NOISE_WHITE_PSG_3 + _ => Some( ESF_NOISE_WHITE_PSG_3 ) } } ) @@ -272,34 +274,34 @@ pub fn get_delay( delay: u8 ) -> Result> { * For a specific row and channel, get the events this row and channel contribute to the stream. * While doing so, update the state of the channel for future event generation. */ -pub fn get_events_for_channel( channel: &mut Channel, row: &PatternRow, pcm_offset: u8, default_psg_instr_index: Option ) -> Result, Box> { +pub fn get_events_for_channel( channels: &mut [Channel], active_channel: usize, row: &PatternRow, pcm_offset: u8, default_psg_instr_index: Option ) -> Result, Box> { let mut events: Vec = Vec::new(); // Adjust volume if let Some( volume ) = &row.volume { // https://github.com/sikthehedgehog/Echo/blob/master/doc/esf.txt#L206-L207 - let volume = get_volume( channel.id, *volume )?; - channel.current_volume = Some( volume ); - events.push( vec![ ESF_SET_VOLUME | channel.id, volume ] ); + let volume = get_volume( channels[ active_channel ].id, *volume )?; + channels[ active_channel ].current_volume = Some( volume ); + events.push( vec![ ESF_SET_VOLUME | channels[ active_channel ].id, volume ] ); } // Set the instrument if let Some( instrument ) = &row.instrument_index { // Do not set the same instrument if it was already set before - if &row.instrument_index != &channel.active_instrument { - events.push( vec![ ESF_SET_INSTRUMENT | channel.id, *instrument as u8 ] ); - channel.active_instrument = Some( *instrument ); + if &row.instrument_index != &channels[ active_channel ].active_instrument { + events.push( vec![ ESF_SET_INSTRUMENT | channels[ active_channel ].id, *instrument as u8 ] ); + channels[ active_channel ].active_instrument = Some( *instrument ); } } else { // 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_4 ).contains( &channel.id ) { + if channels[ active_channel ].active_instrument.is_none() && ( ESF_PSG_1..=ESF_PSG_4 ).contains( &channels[ active_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" )?; - events.push( vec![ ESF_SET_INSTRUMENT | channel.id, instrument as u8 ] ); - channel.active_instrument = Some( instrument ); + events.push( vec![ ESF_SET_INSTRUMENT | channels[ active_channel ].id, instrument as u8 ] ); + channels[ active_channel ].active_instrument = Some( instrument ); } } } @@ -307,34 +309,61 @@ pub fn get_events_for_channel( channel: &mut Channel, row: &PatternRow, pcm_offs // Key on or key off note if let Some( note ) = &row.note { if let Note::NoteOff = note { - channel.active_note = None; - events.push( vec![ ESF_NOTE_OFF | channel.id ] ); + channels[ active_channel ].active_note = None; + events.push( vec![ ESF_NOTE_OFF | channels[ active_channel ].id ] ); } else { - channel.active_note = Some( + channels[ active_channel ].active_note = Some( OctaveFrequency { octave: note.get_octave()?, - frequency: if ( ESF_PSG_1..=ESF_PSG_3 ).contains( &channel.id ) { + frequency: if ( ESF_PSG_1..=ESF_PSG_4 ).contains( &channels[ active_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 )? )? } } ); - 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 { + if channels[ active_channel ].id == ESF_FM_6_PCM { + events.push( vec![ ESF_NOTE_ON | channels[ active_channel ].id, get_pcm_index( note )? + pcm_offset ] ); + } else if channels[ active_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 )? ] ); + let dcsg_noise_mode = channels[ active_channel ].active_effects.iter().find( | effect | matches!( effect, Effect::DcsgNoiseMode { mode: _, noise_type: _ } ) ); + + // Do we need to copy channel.active_note to psg3 and set its frequency? psg3 sets the actual note played for psg4 in special circumstances + if let Some( dcsg_noise_mode ) = dcsg_noise_mode { + if let Effect::DcsgNoiseMode { mode, noise_type: _ } = dcsg_noise_mode { + if mode == &DcsgChannelMode::Ch3Frequency { + let index = channels.iter().position( | channel | channel.id == ESF_PSG_3 ).ok_or( "internal error: where is psg3??" )?; + // Copy over the active note to psg3 + channels[ index ].active_note = channels[ active_channel ].active_note; + // Issue a set frequency for psg3 + events.push( vec![ + ESF_SET_FREQUENCY | channels[ index ].id, + ( 0x000F & channels[ index ].active_note.ok_or( "internal error: wtf happened here (1)??" )?.frequency ) as u8, + ( ( 0x3F0 & channels[ index ].active_note.ok_or( "internal error: wtf happened here (2)??" )?.frequency ) >> 4 ) as u8 + ] ); + } + } else { + return Err( "internal error: wtf happened here (3)??" )? + } + } else { + let index = channels.iter().position( | channel | channel.id == ESF_PSG_3 ).ok_or( "internal error: where is psg3??" )?; + // Copy over the active note + channels[ index ].active_note = channels[ active_channel ].active_note; + // Issue a set frequency for psg3 + events.push( vec![ + ESF_SET_FREQUENCY | channels[ index ].id, + ( 0x000F & channels[ index ].active_note.ok_or( "internal error: wtf happened here (1)??" )?.frequency ) as u8, + ( ( 0x3F0 & channels[ index ].active_note.ok_or( "internal error: wtf happened here (2)??" )?.frequency ) >> 4 ) as u8 + ] ); + } + + if let Some( noise_generator_setting ) = get_noise_generator_setting( note, dcsg_noise_mode )? { + events.push( vec![ ESF_NOTE_ON | channels[ active_channel ].id, noise_generator_setting ] ); + } } else { - let note = get_note( note, channel.id )?; - events.push( vec![ ESF_NOTE_ON | channel.id, note ] ); + let note = get_note( note, channels[ active_channel ].id )?; + events.push( vec![ ESF_NOTE_ON | channels[ active_channel ].id, note ] ); } } } @@ -738,7 +767,7 @@ pub fn get_events_for_row( channels: &mut [Channel], instruments: &Vec Note::CSharp( if channel > 5 { octave - 1 } else { octave } ), - 2 => Note::D( if channel > 5 { octave - 1 } else { octave } ), - 3 => Note::DSharp( if channel > 5 { octave - 1 } else { octave } ), - 4 => Note::E( if channel > 5 { octave - 1 } else { octave } ), - 5 => Note::F( if channel > 5 { octave - 1 } else { octave } ), - 6 => Note::FSharp( if channel > 5 { octave - 1 } else { octave } ), - 7 => Note::G( if channel > 5 { octave - 1 } else { octave } ), - 8 => Note::GSharp( if channel > 5 { octave - 1 } else { octave } ), - 9 => Note::A( if channel > 5 { octave - 1 } else { octave } ), - 10 => Note::ASharp( if channel > 5 { octave - 1 } else { octave } ), - 11 => Note::B( if channel > 5 { octave - 1 } else { octave } ), - 12 => Note::C( if channel > 5 { octave } else { octave + 1 } ), // deflemask y u shift the octave down 1 for C?? - 0 => Note::C( if channel > 5 { octave - 1 } else { octave } ), // deflemask y u do this?? 0 is not documented + 1 => Note::CSharp( if (6..9).contains( &channel ) { octave - 1 } else { octave } ), + 2 => Note::D( if (6..9).contains( &channel ) { octave - 1 } else { octave } ), + 3 => Note::DSharp( if (6..9).contains( &channel ) { octave - 1 } else { octave } ), + 4 => Note::E( if (6..9).contains( &channel ) { octave - 1 } else { octave } ), + 5 => Note::F( if (6..9).contains( &channel ) { octave - 1 } else { octave } ), + 6 => Note::FSharp( if (6..9).contains( &channel ) { octave - 1 } else { octave } ), + 7 => Note::G( if (6..9).contains( &channel ) { octave - 1 } else { octave } ), + 8 => Note::GSharp( if (6..9).contains( &channel ) { octave - 1 } else { octave } ), + 9 => Note::A( if (6..9).contains( &channel ) { octave - 1 } else { octave } ), + 10 => Note::ASharp( if (6..9).contains( &channel ) { octave - 1 } else { octave } ), + 11 => Note::B( if (6..9).contains( &channel ) { octave - 1 } else { octave } ), + 12 => Note::C( if (6..9).contains( &channel ) { octave } else { octave + 1 } ), // deflemask y u shift the octave down 1 for C?? + 0 => Note::C( if (6..9).contains( &channel ) { octave - 1 } else { octave } ), // deflemask y u do this?? 0 is not documented _ => return Err( format!( "invalid file: invalid note id {}", note ) )? } ) @@ -665,13 +665,14 @@ impl DmfModule { } pub fn uses_default_psg( channel_patterns: &Vec> ) -> Result> { - let mut channels: [Channel; 3] = [ + let mut channels: [Channel; 4] = [ Channel::new( 1 ), Channel::new( 2 ), - Channel::new( 3 ) + Channel::new( 3 ), + Channel::new( 4 ) ]; - for channel_id in 6..9 { + 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