Implement portamento up and down for psg channels
parent
e01675fcad
commit
c7c703ef65
|
@ -1,4 +1,4 @@
|
||||||
use std::{collections::HashMap, error::Error};
|
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}, utility::print_warning};
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ pub fn get_pcm_index( note: &Note ) -> Result<u8, Box<dyn Error>> {
|
||||||
D - 722 | F# - 910 | A# - 1146
|
D - 722 | F# - 910 | A# - 1146
|
||||||
D# - 765 | G - 964 | B - 1214
|
D# - 765 | G - 964 | B - 1214
|
||||||
*/
|
*/
|
||||||
pub fn get_semitone_frequency( semitone: u8 ) -> Result<u16, Box<dyn Error>> {
|
pub fn get_fm_semitone_frequency( semitone: u8 ) -> Result<u16, Box<dyn Error>> {
|
||||||
Ok( match semitone {
|
Ok( match semitone {
|
||||||
ESF_SEMITONE_C => 644,
|
ESF_SEMITONE_C => 644,
|
||||||
ESF_SEMITONE_C_SHARP => 681,
|
ESF_SEMITONE_C_SHARP => 681,
|
||||||
|
@ -121,10 +121,65 @@ pub fn get_semitone_frequency( semitone: u8 ) -> Result<u16, Box<dyn Error>> {
|
||||||
ESF_SEMITONE_A => 1081,
|
ESF_SEMITONE_A => 1081,
|
||||||
ESF_SEMITONE_A_SHARP => 1146,
|
ESF_SEMITONE_A_SHARP => 1146,
|
||||||
ESF_SEMITONE_B => 1214,
|
ESF_SEMITONE_B => 1214,
|
||||||
_ => return Err( "internal error: invalid semitone value provided to get_semitone_frequency" )?
|
_ => return Err( "internal error: invalid semitone value provided to get_fm_semitone_frequency" )?
|
||||||
} )
|
} )
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the semitone frequency used by Echo (PSG).
|
||||||
|
https://github.com/sikthehedgehog/Echo/blob/master/doc/esf.txt#L273
|
||||||
|
|Oct.0|Oct.1|Oct.2|Oct.3|Oct.4|Oct.5
|
||||||
|
---|-----|-----|-----|-----|-----|-----
|
||||||
|
C | 851 | 425 | 212 | 106 | 53 | 26
|
||||||
|
C# | 803 | 401 | 200 | 100 | 50 | 25
|
||||||
|
D | 758 | 379 | 189 | 94 | 47 | 23
|
||||||
|
D# | 715 | 357 | 178 | 89 | 44 | 22
|
||||||
|
E | 675 | 337 | 168 | 84 | 42 | 21
|
||||||
|
F | 637 | 318 | 159 | 79 | 39 | 19
|
||||||
|
F# | 601 | 300 | 150 | 75 | 37 | 18
|
||||||
|
G | 568 | 284 | 142 | 71 | 35 | 17
|
||||||
|
G# | 536 | 268 | 134 | 67 | 33 | 16
|
||||||
|
A | 506 | 253 | 126 | 63 | 31 | 15
|
||||||
|
A# | 477 | 238 | 119 | 59 | 29 | 14
|
||||||
|
B | 450 | 225 | 112 | 56 | 28 | 14
|
||||||
|
*/
|
||||||
|
pub fn get_psg_semitone_frequency( octave: u8, semitone: u8 ) -> Result<u16, Box<dyn Error>> {
|
||||||
|
Ok( match semitone {
|
||||||
|
ESF_SEMITONE_C => match octave { 0 => 851, 1 => 425, 2 => 212, 3 => 106, 4 => 53, 5 | _ => 26 },
|
||||||
|
ESF_SEMITONE_C_SHARP => match octave { 0 => 803, 1 => 401, 2 => 200, 3 => 100, 4 => 50, 5 | _ => 25 },
|
||||||
|
ESF_SEMITONE_D => match octave { 0 => 758, 1 => 379, 2 => 189, 3 => 94, 4 => 47, 5 | _ => 23 },
|
||||||
|
ESF_SEMITONE_D_SHARP => match octave { 0 => 715, 1 => 357, 2 => 178, 3 => 89, 4 => 44, 5 | _ => 22 },
|
||||||
|
ESF_SEMITONE_E => match octave { 0 => 675, 1 => 337, 2 => 168, 3 => 84, 4 => 42, 5 | _ => 21 },
|
||||||
|
ESF_SEMITONE_F => match octave { 0 => 637, 1 => 318, 2 => 159, 3 => 79, 4 => 39, 5 | _ => 19 },
|
||||||
|
ESF_SEMITONE_F_SHARP => match octave { 0 => 601, 1 => 300, 2 => 150, 3 => 75, 4 => 37, 5 | _ => 18 },
|
||||||
|
ESF_SEMITONE_G => match octave { 0 => 568, 1 => 284, 2 => 142, 3 => 71, 4 => 35, 5 | _ => 17 },
|
||||||
|
ESF_SEMITONE_G_SHARP => match octave { 0 => 536, 1 => 268, 2 => 134, 3 => 67, 4 => 33, 5 | _ => 16 },
|
||||||
|
ESF_SEMITONE_A => match octave { 0 => 506, 1 => 253, 2 => 126, 3 => 63, 4 => 31, 5 | _ => 15 },
|
||||||
|
ESF_SEMITONE_A_SHARP => match octave { 0 => 477, 1 => 238, 2 => 119, 3 => 59, 4 => 29, 5 | _ => 14 },
|
||||||
|
ESF_SEMITONE_B => match octave { 0 => 450, 1 => 225, 2 => 112, 3 => 56, 4 => 28, 5 | _ => 14 },
|
||||||
|
_ => return Err( "internal error: invalid semitone value provided to get_psg_semitone_frequency" )?
|
||||||
|
} )
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience method to get the octave of a given PSG frequency
|
||||||
|
*/
|
||||||
|
pub fn get_psg_octave_from_frequency( frequency: i16 ) -> u8 {
|
||||||
|
if frequency > 425 {
|
||||||
|
0
|
||||||
|
} else if frequency > 212 && frequency <= 425 {
|
||||||
|
1
|
||||||
|
} else if frequency > 106 && frequency <= 212 {
|
||||||
|
2
|
||||||
|
} else if frequency > 53 && frequency <= 106 {
|
||||||
|
3
|
||||||
|
} else if frequency > 26 && frequency <= 53 {
|
||||||
|
4
|
||||||
|
} else {
|
||||||
|
5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
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
|
||||||
}
|
}
|
||||||
|
@ -190,7 +245,7 @@ pub fn get_events_for_channel( channel: &mut Channel, row: &PatternRow, pcm_offs
|
||||||
} else {
|
} else {
|
||||||
// This is SN1, SN2, or SN3 with no active instrument
|
// This is SN1, SN2, or SN3 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() && ( 6..=8 ).contains( &channel.id ) {
|
if channel.active_instrument.is_none() && ( ESF_PSG_1..=ESF_PSG_3 ).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" )?;
|
||||||
|
@ -210,7 +265,11 @@ pub fn get_events_for_channel( channel: &mut Channel, row: &PatternRow, pcm_offs
|
||||||
channel.active_note = Some(
|
channel.active_note = Some(
|
||||||
OctaveFrequency {
|
OctaveFrequency {
|
||||||
octave: note.get_octave()?,
|
octave: note.get_octave()?,
|
||||||
frequency: get_semitone_frequency( get_semitone( note )? )?
|
frequency: if ( ESF_PSG_1..=ESF_PSG_3 ).contains( &channel.id ) {
|
||||||
|
get_psg_semitone_frequency( note.get_octave()?, get_semitone( note )? )?
|
||||||
|
} else {
|
||||||
|
get_fm_semitone_frequency( get_semitone( note )? )?
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -418,28 +477,40 @@ pub fn get_portamento( channel: &mut Channel, portamento_effect: &Effect ) -> Re
|
||||||
_ => return Err( "internal error: provided effect is not a supported portamento effect" )?
|
_ => return Err( "internal error: provided effect is not a supported portamento effect" )?
|
||||||
};
|
};
|
||||||
|
|
||||||
let new_frequency: i16 = octave_frequency.frequency as i16 + by_amount;
|
if ( ESF_PSG_1..=ESF_PSG_3 ).contains( &channel.id ) {
|
||||||
if new_frequency > get_semitone_frequency( ESF_SEMITONE_B )? as i16 {
|
// Frequency note shifts are backward in PSG
|
||||||
if octave_frequency.octave == 7 {
|
let new_frequency: i16 = octave_frequency.frequency as i16 + ( by_amount * -1 );
|
||||||
// Nowhere else to go up
|
|
||||||
( 7, get_semitone_frequency( ESF_SEMITONE_B )? )
|
// Clamp frequency
|
||||||
} else {
|
let new_frequency = max( min( new_frequency, 1024 ), 0 );
|
||||||
// Go up an octave then add the difference to middle C
|
|
||||||
let difference = new_frequency - get_semitone_frequency( ESF_SEMITONE_B )? as i16;
|
// Get psg octave from frequency (which will clamp octave to 0-5) and return the new frequency
|
||||||
( octave_frequency.octave + 1, get_semitone_frequency( ESF_SEMITONE_C )? + difference as u16 )
|
( get_psg_octave_from_frequency( new_frequency ), new_frequency as u16 )
|
||||||
}
|
|
||||||
} else if new_frequency < get_semitone_frequency( ESF_SEMITONE_C )? as i16 {
|
|
||||||
if octave_frequency.octave == 0 {
|
|
||||||
// Nowhere else to go down
|
|
||||||
( 0, get_semitone_frequency( ESF_SEMITONE_C )? )
|
|
||||||
} else {
|
|
||||||
// Go down an octave then subtract the overshoot of C from B
|
|
||||||
let difference = get_semitone_frequency( ESF_SEMITONE_C )? as i16 - new_frequency;
|
|
||||||
( octave_frequency.octave - 1, get_semitone_frequency( ESF_SEMITONE_B )? - difference as u16 )
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// Move within the same octave
|
let new_frequency: i16 = octave_frequency.frequency as i16 + by_amount;
|
||||||
( octave_frequency.octave, new_frequency as u16 )
|
|
||||||
|
if new_frequency > get_fm_semitone_frequency( ESF_SEMITONE_B )? as i16 {
|
||||||
|
if octave_frequency.octave == 7 {
|
||||||
|
// Nowhere else to go up
|
||||||
|
( 7, get_fm_semitone_frequency( ESF_SEMITONE_B )? )
|
||||||
|
} else {
|
||||||
|
// Go up an octave then add the difference to middle C
|
||||||
|
let difference = new_frequency - get_fm_semitone_frequency( ESF_SEMITONE_B )? as i16;
|
||||||
|
( octave_frequency.octave + 1, get_fm_semitone_frequency( ESF_SEMITONE_C )? + difference as u16 )
|
||||||
|
}
|
||||||
|
} else if new_frequency < get_fm_semitone_frequency( ESF_SEMITONE_C )? as i16 {
|
||||||
|
if octave_frequency.octave == 0 {
|
||||||
|
// Nowhere else to go down
|
||||||
|
( 0, get_fm_semitone_frequency( ESF_SEMITONE_C )? )
|
||||||
|
} else {
|
||||||
|
// Go down an octave then subtract the overshoot of C from B
|
||||||
|
let difference = get_fm_semitone_frequency( ESF_SEMITONE_C )? as i16 - new_frequency;
|
||||||
|
( octave_frequency.octave - 1, get_fm_semitone_frequency( ESF_SEMITONE_B )? - difference as u16 )
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Move within the same octave
|
||||||
|
( octave_frequency.octave, new_frequency as u16 )
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -450,8 +521,16 @@ pub fn get_portamento( channel: &mut Channel, portamento_effect: &Effect ) -> Re
|
||||||
// YM2612 register format for frequency: https://plutiedev.com/ym2612-registers#reg-A0
|
// YM2612 register format for frequency: https://plutiedev.com/ym2612-registers#reg-A0
|
||||||
vec![
|
vec![
|
||||||
ESF_SET_FREQUENCY | channel.id,
|
ESF_SET_FREQUENCY | channel.id,
|
||||||
octave << 3 | ( ( ( 0x0700 & frequency ) >> 8 ) as u8 ),
|
if ( ESF_PSG_1..=ESF_PSG_3 ).contains( &channel.id ) {
|
||||||
( 0x00FF & frequency ) as u8
|
( 0x000F & frequency ) as u8
|
||||||
|
} else {
|
||||||
|
octave << 3 | ( ( ( 0x0700 & frequency ) >> 8 ) as u8 )
|
||||||
|
},
|
||||||
|
if ( ESF_PSG_1..=ESF_PSG_3 ).contains( &channel.id ) {
|
||||||
|
( ( 0x3F0 & frequency ) >> 4 ) as u8
|
||||||
|
} else {
|
||||||
|
( 0x00FF & frequency ) as u8
|
||||||
|
}
|
||||||
]
|
]
|
||||||
} else {
|
} else {
|
||||||
// No active note, nothing to generate
|
// No active note, nothing to generate
|
||||||
|
|
Loading…
Reference in New Issue