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 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# - 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 {
|
||||
ESF_SEMITONE_C => 644,
|
||||
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_SHARP => 1146,
|
||||
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 {
|
||||
32 * octave + 2 * semitone + 1
|
||||
}
|
||||
|
@ -190,7 +245,7 @@ pub fn get_events_for_channel( channel: &mut Channel, row: &PatternRow, pcm_offs
|
|||
} else {
|
||||
// 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
|
||||
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() {
|
||||
// Set "__reskit_default_psg_instrument"
|
||||
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(
|
||||
OctaveFrequency {
|
||||
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,29 +477,41 @@ pub fn get_portamento( channel: &mut Channel, portamento_effect: &Effect ) -> Re
|
|||
_ => return Err( "internal error: provided effect is not a supported portamento effect" )?
|
||||
};
|
||||
|
||||
if ( ESF_PSG_1..=ESF_PSG_3 ).contains( &channel.id ) {
|
||||
// Frequency note shifts are backward in PSG
|
||||
let new_frequency: i16 = octave_frequency.frequency as i16 + ( by_amount * -1 );
|
||||
|
||||
// Clamp frequency
|
||||
let new_frequency = max( min( new_frequency, 1024 ), 0 );
|
||||
|
||||
// Get psg octave from frequency (which will clamp octave to 0-5) and return the new frequency
|
||||
( get_psg_octave_from_frequency( new_frequency ), new_frequency as u16 )
|
||||
} else {
|
||||
let new_frequency: i16 = octave_frequency.frequency as i16 + by_amount;
|
||||
if new_frequency > get_semitone_frequency( ESF_SEMITONE_B )? as i16 {
|
||||
|
||||
if new_frequency > get_fm_semitone_frequency( ESF_SEMITONE_B )? as i16 {
|
||||
if octave_frequency.octave == 7 {
|
||||
// Nowhere else to go up
|
||||
( 7, get_semitone_frequency( ESF_SEMITONE_B )? )
|
||||
( 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_semitone_frequency( ESF_SEMITONE_B )? as i16;
|
||||
( octave_frequency.octave + 1, get_semitone_frequency( ESF_SEMITONE_C )? + difference as u16 )
|
||||
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_semitone_frequency( ESF_SEMITONE_C )? as i16 {
|
||||
} 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_semitone_frequency( ESF_SEMITONE_C )? )
|
||||
( 0, get_fm_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 )
|
||||
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 )
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Set the new OctaveFrequency on the channel
|
||||
|
@ -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
|
||||
vec![
|
||||
ESF_SET_FREQUENCY | channel.id,
|
||||
octave << 3 | ( ( ( 0x0700 & frequency ) >> 8 ) as u8 ),
|
||||
if ( ESF_PSG_1..=ESF_PSG_3 ).contains( &channel.id ) {
|
||||
( 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 {
|
||||
// No active note, nothing to generate
|
||||
|
|
Loading…
Reference in New Issue