Getting started with noise generator functions
parent
9d82240445
commit
0714ef5de9
|
@ -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.
|
* 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>> {
|
pub fn get_noise_generator_setting( note: &Note, noise_mode: Option<&Effect> ) -> Result<Option<u8>, Box<dyn Error>> {
|
||||||
Ok(
|
Ok(
|
||||||
if let Some( noise_mode ) = noise_mode {
|
if let Some( noise_mode ) = noise_mode {
|
||||||
if let Effect::DcsgNoiseMode { mode, noise_type } = noise_mode {
|
if let Effect::DcsgNoiseMode { mode, noise_type } = noise_mode {
|
||||||
match noise_type {
|
match noise_type {
|
||||||
NoiseType::PeriodicNoise => match mode {
|
NoiseType::PeriodicNoise => match mode {
|
||||||
DcsgChannelMode::Ch3Frequency => ESF_NOISE_PERIODIC_PSG_3,
|
DcsgChannelMode::Ch3Frequency => Some( ESF_NOISE_PERIODIC_PSG_3 ),
|
||||||
DcsgChannelMode::FixedFrequency => match note {
|
DcsgChannelMode::FixedFrequency => match note {
|
||||||
Note::NoteOff => return Err( "internal error: attempted to get noise generator setting for NoteOff" )?,
|
Note::NoteOff => return Err( "internal error: attempted to get noise generator setting for NoteOff" )?,
|
||||||
Note::C(_) => ESF_NOISE_PERIODIC_LOW,
|
Note::C(_) => Some( ESF_NOISE_PERIODIC_LOW ),
|
||||||
Note::CSharp(_) => ESF_NOISE_PERIODIC_MED,
|
Note::CSharp(_) => Some( ESF_NOISE_PERIODIC_MED ),
|
||||||
Note::D(_) | _ => ESF_NOISE_PERIODIC_HIGH
|
Note::D(_) => Some( ESF_NOISE_PERIODIC_HIGH ),
|
||||||
|
_ => None
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
NoiseType::WhiteNoise => match mode {
|
NoiseType::WhiteNoise => match mode {
|
||||||
DcsgChannelMode::Ch3Frequency => ESF_NOISE_WHITE_PSG_3,
|
DcsgChannelMode::Ch3Frequency => Some( ESF_NOISE_WHITE_PSG_3 ),
|
||||||
DcsgChannelMode::FixedFrequency => match note {
|
DcsgChannelMode::FixedFrequency => match note {
|
||||||
Note::NoteOff => return Err( "internal error: attempted to get noise generator setting for NoteOff" )?,
|
Note::NoteOff => return Err( "internal error: attempted to get noise generator setting for NoteOff" )?,
|
||||||
Note::C(_) => ESF_NOISE_WHITE_LOW,
|
Note::C(_) => Some( ESF_NOISE_WHITE_LOW ),
|
||||||
Note::CSharp(_) => ESF_NOISE_WHITE_MED,
|
Note::CSharp(_) => Some( ESF_NOISE_WHITE_MED ),
|
||||||
Note::D(_) | _ => ESF_NOISE_WHITE_HIGH
|
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 {
|
} else {
|
||||||
match note {
|
match note {
|
||||||
Note::NoteOff => return Err( "internal error: attempted to get noise generator setting for NoteOff" )?,
|
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<EchoEvent, Box<dyn Error>> {
|
||||||
* For a specific row and channel, get the events this row and channel contribute to the stream.
|
* 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.
|
* 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<u16> ) -> Result<Vec<EchoEvent>, Box<dyn Error>> {
|
pub fn get_events_for_channel( channels: &mut [Channel], active_channel: usize, row: &PatternRow, pcm_offset: u8, default_psg_instr_index: Option<u16> ) -> Result<Vec<EchoEvent>, Box<dyn Error>> {
|
||||||
let mut events: Vec<EchoEvent> = Vec::new();
|
let mut events: Vec<EchoEvent> = Vec::new();
|
||||||
|
|
||||||
// Adjust volume
|
// Adjust volume
|
||||||
if let Some( volume ) = &row.volume {
|
if let Some( volume ) = &row.volume {
|
||||||
// https://github.com/sikthehedgehog/Echo/blob/master/doc/esf.txt#L206-L207
|
// https://github.com/sikthehedgehog/Echo/blob/master/doc/esf.txt#L206-L207
|
||||||
let volume = get_volume( channel.id, *volume )?;
|
let volume = get_volume( channels[ active_channel ].id, *volume )?;
|
||||||
channel.current_volume = Some( volume );
|
channels[ active_channel ].current_volume = Some( volume );
|
||||||
events.push( vec![ ESF_SET_VOLUME | channel.id, volume ] );
|
events.push( vec![ ESF_SET_VOLUME | channels[ active_channel ].id, volume ] );
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the instrument
|
// Set the instrument
|
||||||
if let Some( instrument ) = &row.instrument_index {
|
if let Some( instrument ) = &row.instrument_index {
|
||||||
// Do not set the same instrument if it was already set before
|
// Do not set the same instrument if it was already set before
|
||||||
if &row.instrument_index != &channel.active_instrument {
|
if &row.instrument_index != &channels[ active_channel ].active_instrument {
|
||||||
events.push( vec![ ESF_SET_INSTRUMENT | channel.id, *instrument as u8 ] );
|
events.push( vec![ ESF_SET_INSTRUMENT | channels[ active_channel ].id, *instrument as u8 ] );
|
||||||
channel.active_instrument = Some( *instrument );
|
channels[ active_channel ].active_instrument = Some( *instrument );
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// This is SN1, SN2, SN3, or SN4 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_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() {
|
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" )?;
|
||||||
|
|
||||||
events.push( vec![ ESF_SET_INSTRUMENT | channel.id, instrument as u8 ] );
|
events.push( vec![ ESF_SET_INSTRUMENT | channels[ active_channel ].id, instrument as u8 ] );
|
||||||
channel.active_instrument = Some( instrument );
|
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
|
// Key on or key off note
|
||||||
if let Some( note ) = &row.note {
|
if let Some( note ) = &row.note {
|
||||||
if let Note::NoteOff = note {
|
if let Note::NoteOff = note {
|
||||||
channel.active_note = None;
|
channels[ active_channel ].active_note = None;
|
||||||
events.push( vec![ ESF_NOTE_OFF | channel.id ] );
|
events.push( vec![ ESF_NOTE_OFF | channels[ active_channel ].id ] );
|
||||||
} else {
|
} else {
|
||||||
channel.active_note = Some(
|
channels[ active_channel ].active_note = Some(
|
||||||
OctaveFrequency {
|
OctaveFrequency {
|
||||||
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_4 ).contains( &channels[ active_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 )? )?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if channel.id == ESF_FM_6_PCM {
|
if channels[ active_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 | channels[ active_channel ].id, get_pcm_index( note )? + pcm_offset ] );
|
||||||
} else if channel.id == ESF_PSG_4 {
|
} 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
|
// 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: _ } ) );
|
let dcsg_noise_mode = channels[ active_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 )? ] );
|
|
||||||
|
// 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 {
|
} else {
|
||||||
let note = get_note( note, channel.id )?;
|
return Err( "internal error: wtf happened here (3)??" )?
|
||||||
events.push( vec![ ESF_NOTE_ON | channel.id, note ] );
|
}
|
||||||
|
} 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, 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<Instrumen
|
||||||
events.extend( apply_effects_to_channel( &mut channels[ i ], &subrows[ i ].effects )? );
|
events.extend( apply_effects_to_channel( &mut channels[ i ], &subrows[ i ].effects )? );
|
||||||
|
|
||||||
// Get the ESF events for this channel's part of the row
|
// Get the ESF events for this channel's part of the row
|
||||||
events.extend( get_events_for_channel( &mut channels[ i ], subrows[ i ], pcm_offset, default_psg_instr_index )? );
|
events.extend( get_events_for_channel( channels, i, subrows[ i ], pcm_offset, default_psg_instr_index )? );
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok( get_delays( events, channels, ticks_to_wait )? )
|
Ok( get_delays( events, channels, ticks_to_wait )? )
|
||||||
|
|
|
@ -469,19 +469,19 @@ impl DmfModule {
|
||||||
// check: do these octave shifts make sense for all dmf modules?
|
// check: do these octave shifts make sense for all dmf modules?
|
||||||
// they were needed on the test dmf but no documentation supports it
|
// they were needed on the test dmf but no documentation supports it
|
||||||
// test dmf sounds off key otherwise...
|
// test dmf sounds off key otherwise...
|
||||||
1 => Note::CSharp( if channel > 5 { octave - 1 } else { octave } ),
|
1 => Note::CSharp( if (6..9).contains( &channel ) { octave - 1 } else { octave } ),
|
||||||
2 => Note::D( if channel > 5 { octave - 1 } else { octave } ),
|
2 => Note::D( if (6..9).contains( &channel ) { octave - 1 } else { octave } ),
|
||||||
3 => Note::DSharp( if channel > 5 { octave - 1 } else { octave } ),
|
3 => Note::DSharp( if (6..9).contains( &channel ) { octave - 1 } else { octave } ),
|
||||||
4 => Note::E( if channel > 5 { octave - 1 } else { octave } ),
|
4 => Note::E( if (6..9).contains( &channel ) { octave - 1 } else { octave } ),
|
||||||
5 => Note::F( if channel > 5 { octave - 1 } else { octave } ),
|
5 => Note::F( if (6..9).contains( &channel ) { octave - 1 } else { octave } ),
|
||||||
6 => Note::FSharp( if channel > 5 { octave - 1 } else { octave } ),
|
6 => Note::FSharp( if (6..9).contains( &channel ) { octave - 1 } else { octave } ),
|
||||||
7 => Note::G( if channel > 5 { octave - 1 } else { octave } ),
|
7 => Note::G( if (6..9).contains( &channel ) { octave - 1 } else { octave } ),
|
||||||
8 => Note::GSharp( if channel > 5 { octave - 1 } else { octave } ),
|
8 => Note::GSharp( if (6..9).contains( &channel ) { octave - 1 } else { octave } ),
|
||||||
9 => Note::A( if channel > 5 { octave - 1 } else { octave } ),
|
9 => Note::A( if (6..9).contains( &channel ) { octave - 1 } else { octave } ),
|
||||||
10 => Note::ASharp( if channel > 5 { octave - 1 } else { octave } ),
|
10 => Note::ASharp( if (6..9).contains( &channel ) { octave - 1 } else { octave } ),
|
||||||
11 => Note::B( if channel > 5 { octave - 1 } else { octave } ),
|
11 => Note::B( if (6..9).contains( &channel ) { octave - 1 } else { octave } ),
|
||||||
12 => Note::C( if channel > 5 { octave } else { octave + 1 } ), // deflemask y u shift the octave down 1 for C??
|
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 channel > 5 { octave - 1 } else { octave } ), // deflemask y u do this?? 0 is not documented
|
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 ) )?
|
_ => return Err( format!( "invalid file: invalid note id {}", note ) )?
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -665,13 +665,14 @@ impl DmfModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn uses_default_psg( channel_patterns: &Vec<Vec<PatternRow>> ) -> Result<bool, Box<dyn Error>> {
|
pub fn uses_default_psg( channel_patterns: &Vec<Vec<PatternRow>> ) -> Result<bool, Box<dyn Error>> {
|
||||||
let mut channels: [Channel; 3] = [
|
let mut channels: [Channel; 4] = [
|
||||||
Channel::new( 1 ),
|
Channel::new( 1 ),
|
||||||
Channel::new( 2 ),
|
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 ];
|
let channel_patterns = &channel_patterns[ channel_id ];
|
||||||
for row in channel_patterns {
|
for row in channel_patterns {
|
||||||
// If a set instrument is encountered, just set the instrument on the channel
|
// If a set instrument is encountered, just set the instrument on the channel
|
||||||
|
|
Loading…
Reference in New Issue