support for overriding pixtone samples

This commit is contained in:
Alula 2021-06-21 00:42:10 +02:00
parent 3fe8e132e5
commit 752ecac3ee
No known key found for this signature in database
GPG Key ID: 3E00485503A1D8BA
5 changed files with 237 additions and 97 deletions

View File

@ -7,6 +7,8 @@ use crate::case_insensitive_hashmap;
use crate::common::{BulletFlag, Color, Rect}; use crate::common::{BulletFlag, Color, Rect};
use crate::engine_constants::npcs::NPCConsts; use crate::engine_constants::npcs::NPCConsts;
use crate::player::ControlMode; use crate::player::ControlMode;
use crate::sound::pixtone::{Channel, PixToneParameters, Waveform, Envelope};
use crate::sound::SoundManager;
use crate::str; use crate::str;
use crate::text_script::TextScriptEncoding; use crate::text_script::TextScriptEncoding;
@ -1395,7 +1397,7 @@ impl EngineConstants {
inventory_item_count_x: 6, inventory_item_count_x: 6,
text_shadow: false, text_shadow: false,
text_speed_normal: 4, text_speed_normal: 4,
text_speed_fast: 1 text_speed_fast: 1,
}, },
title: TitleConsts { title: TitleConsts {
intro_text: "Studio Pixel presents".to_string(), intro_text: "Studio Pixel presents".to_string(),
@ -1468,7 +1470,7 @@ impl EngineConstants {
} }
} }
pub fn apply_csplus_patches(&mut self) { pub fn apply_csplus_patches(&mut self, sound_manager: &SoundManager) {
info!("Applying Cave Story+ constants patches..."); info!("Applying Cave Story+ constants patches...");
self.is_cs_plus = true; self.is_cs_plus = true;
@ -1482,6 +1484,33 @@ impl EngineConstants {
self.font_space_offset = 2.0; self.font_space_offset = 2.0;
self.soundtracks.insert("Remastered".to_string(), "/base/Ogg11/".to_string()); self.soundtracks.insert("Remastered".to_string(), "/base/Ogg11/".to_string());
self.soundtracks.insert("New".to_string(), "/base/Ogg/".to_string()); self.soundtracks.insert("New".to_string(), "/base/Ogg/".to_string());
let typewriter_sample = PixToneParameters {
// fx2 (CS+)
channels: [
Channel {
enabled: true,
length: 2000,
carrier: Waveform { waveform_type: 0, pitch: 92.000000, level: 32, offset: 0 },
frequency: Waveform { waveform_type: 0, pitch: 3.000000, level: 44, offset: 0 },
amplitude: Waveform { waveform_type: 0, pitch: 0.000000, level: 32, offset: 0 },
envelope: Envelope {
initial: 7,
time_a: 2,
value_a: 18,
time_b: 128,
value_b: 0,
time_c: 255,
value_c: 0,
},
},
Channel::disabled(),
Channel::disabled(),
Channel::disabled(),
],
};
sound_manager.set_sample_params(2, typewriter_sample);
} }
pub fn apply_csplus_nx_patches(&mut self) { pub fn apply_csplus_nx_patches(&mut self) {

View File

@ -130,16 +130,17 @@ pub struct SharedGameState {
impl SharedGameState { impl SharedGameState {
pub fn new(ctx: &mut Context) -> GameResult<SharedGameState> { pub fn new(ctx: &mut Context) -> GameResult<SharedGameState> {
let mut constants = EngineConstants::defaults(); let mut constants = EngineConstants::defaults();
let sound_manager = SoundManager::new(ctx)?;
let mut base_path = "/"; let mut base_path = "/";
let settings = Settings::load(ctx)?; let settings = Settings::load(ctx)?;
if filesystem::exists(ctx, "/base/Nicalis.bmp") { if filesystem::exists(ctx, "/base/Nicalis.bmp") {
info!("Cave Story+ (PC) data files detected."); info!("Cave Story+ (PC) data files detected.");
constants.apply_csplus_patches(); constants.apply_csplus_patches(&sound_manager);
base_path = "/base/"; base_path = "/base/";
} else if filesystem::exists(ctx, "/base/lighting.tbl") { } else if filesystem::exists(ctx, "/base/lighting.tbl") {
info!("Cave Story+ (Switch) data files detected."); info!("Cave Story+ (Switch) data files detected.");
constants.apply_csplus_patches(); constants.apply_csplus_patches(&sound_manager);
constants.apply_csplus_nx_patches(); constants.apply_csplus_nx_patches();
base_path = "/base/"; base_path = "/base/";
} else if filesystem::exists(ctx, "/mrmap.bin") { } else if filesystem::exists(ctx, "/mrmap.bin") {
@ -157,6 +158,32 @@ impl SharedGameState {
texture_set.apply_seasonal_content(season, &settings); texture_set.apply_seasonal_content(season, &settings);
} }
for i in 0..0xffu8 {
let path = format!("{}/pxt/fx{:02x}.pxt", base_path, i);
if let Ok(file) = filesystem::open(ctx, path) {
sound_manager.set_sample_params_from_file(i, file)?;
continue;
}
let path = format!("/pxt/fx{:02x}.pxt", i);
if let Ok(file) = filesystem::open(ctx, path) {
sound_manager.set_sample_params_from_file(i, file)?;
continue;
}
let path = format!("{}/PixTone/{:03}.pxt", base_path, i);
if let Ok(file) = filesystem::open(ctx, path) {
sound_manager.set_sample_params_from_file(i, file)?;
continue;
}
let path = format!("/PixTone/{:03}.pxt", i);
if let Ok(file) = filesystem::open(ctx, path) {
sound_manager.set_sample_params_from_file(i, file)?;
continue;
}
}
println!("lookup path: {:#?}", texture_set.paths); println!("lookup path: {:#?}", texture_set.paths);
#[cfg(feature = "hooks")] #[cfg(feature = "hooks")]
@ -195,7 +222,7 @@ impl SharedGameState {
texture_set, texture_set,
#[cfg(feature = "scripting")] #[cfg(feature = "scripting")]
lua: LuaScriptingState::new(), lua: LuaScriptingState::new(),
sound_manager: SoundManager::new(ctx)?, sound_manager,
settings, settings,
shutdown: false, shutdown: false,
}) })

View File

@ -1,17 +1,18 @@
use std::io;
use std::sync::mpsc; use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender}; use std::sync::mpsc::{Receiver, Sender, TryRecvError};
use std::time::Duration; use std::time::Duration;
use cpal::Sample;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use cpal::Sample;
#[cfg(feature = "ogg-playback")] #[cfg(feature = "ogg-playback")]
use lewton::inside_ogg::OggStreamReader; use lewton::inside_ogg::OggStreamReader;
use num_traits::clamp; use num_traits::clamp;
use crate::engine_constants::EngineConstants; use crate::engine_constants::EngineConstants;
use crate::framework::context::Context; use crate::framework::context::Context;
use crate::framework::error::{GameError, GameResult};
use crate::framework::error::GameError::{AudioError, InvalidValue}; use crate::framework::error::GameError::{AudioError, InvalidValue};
use crate::framework::error::{GameError, GameResult};
use crate::framework::filesystem; use crate::framework::filesystem;
use crate::framework::filesystem::File; use crate::framework::filesystem::File;
use crate::settings::Settings; use crate::settings::Settings;
@ -19,15 +20,17 @@ use crate::settings::Settings;
use crate::sound::ogg_playback::{OggPlaybackEngine, SavedOggPlaybackState}; use crate::sound::ogg_playback::{OggPlaybackEngine, SavedOggPlaybackState};
use crate::sound::org_playback::{OrgPlaybackEngine, SavedOrganyaPlaybackState}; use crate::sound::org_playback::{OrgPlaybackEngine, SavedOrganyaPlaybackState};
use crate::sound::organya::Song; use crate::sound::organya::Song;
use crate::sound::pixtone::PixTonePlayback; use crate::sound::pixtone::{PixToneParameters, PixTonePlayback};
use crate::sound::wave_bank::SoundBank; use crate::sound::wave_bank::SoundBank;
use crate::str; use crate::str;
use std::io::{BufReader, BufRead, Lines};
use std::str::FromStr;
#[cfg(feature = "ogg-playback")] #[cfg(feature = "ogg-playback")]
mod ogg_playback; mod ogg_playback;
mod org_playback; mod org_playback;
mod organya; mod organya;
mod pixtone; pub mod pixtone;
mod pixtone_sfx; mod pixtone_sfx;
mod stuff; mod stuff;
mod wav; mod wav;
@ -52,7 +55,8 @@ impl SoundManager {
let (tx, rx): (Sender<PlaybackMessage>, Receiver<PlaybackMessage>) = mpsc::channel(); let (tx, rx): (Sender<PlaybackMessage>, Receiver<PlaybackMessage>) = mpsc::channel();
let host = cpal::default_host(); let host = cpal::default_host();
let device = host.default_output_device().ok_or_else(|| AudioError(str!("Error initializing audio device.")))?; let device =
host.default_output_device().ok_or_else(|| AudioError(str!("Error initializing audio device.")))?;
let config = device.default_output_config()?; let config = device.default_output_config()?;
let bnk = wave_bank::SoundBank::load_from(filesystem::open(ctx, "/builtin/organya-wavetable-doukutsu.bin")?)?; let bnk = wave_bank::SoundBank::load_from(filesystem::open(ctx, "/builtin/organya-wavetable-doukutsu.bin")?)?;
@ -70,11 +74,17 @@ impl SoundManager {
Ok(SoundManager { tx: tx.clone(), prev_song_id: 0, current_song_id: 0 }) Ok(SoundManager { tx: tx.clone(), prev_song_id: 0, current_song_id: 0 })
} }
pub fn play_sfx(&mut self, id: u8) { pub fn play_sfx(&self, id: u8) {
let _ = self.tx.send(PlaybackMessage::PlaySample(id)); let _ = self.tx.send(PlaybackMessage::PlaySample(id));
} }
pub fn play_song(&mut self, song_id: usize, constants: &EngineConstants, settings: &Settings, ctx: &mut Context) -> GameResult { pub fn play_song(
&mut self,
song_id: usize,
constants: &EngineConstants,
settings: &Settings,
ctx: &mut Context,
) -> GameResult {
if self.current_song_id == song_id { if self.current_song_id == song_id {
return Ok(()); return Ok(());
} }
@ -98,16 +108,21 @@ impl SoundManager {
let songs_paths = paths.iter().map(|prefix| { let songs_paths = paths.iter().map(|prefix| {
[ [
#[cfg(feature = "ogg-playback")] #[cfg(feature = "ogg-playback")]
(SongFormat::OggMultiPart, vec![format!("{}{}_intro.ogg", prefix, song_name), format!("{}{}_loop.ogg", prefix, song_name)]), (
#[cfg(feature = "ogg-playback")] SongFormat::OggMultiPart,
vec![format!("{}{}_intro.ogg", prefix, song_name), format!("{}{}_loop.ogg", prefix, song_name)],
),
#[cfg(feature = "ogg-playback")]
(SongFormat::OggSinglePart, vec![format!("{}{}.ogg", prefix, song_name)]), (SongFormat::OggSinglePart, vec![format!("{}{}.ogg", prefix, song_name)]),
(SongFormat::Organya, vec![format!("{}{}.org", prefix, song_name)]), (SongFormat::Organya, vec![format!("{}{}.org", prefix, song_name)]),
] ]
}); });
for songs in songs_paths { for songs in songs_paths {
for (format, paths) in songs.iter().filter(|(_, paths)| paths.iter().all(|path| filesystem::exists(ctx, path))) { for (format, paths) in
songs.iter().filter(|(_, paths)| paths.iter().all(|path| filesystem::exists(ctx, path)))
{
match format { match format {
SongFormat::Organya => { SongFormat::Organya => {
// we're sure that there's one element // we're sure that there's one element
@ -134,7 +149,9 @@ impl SoundManager {
// we're sure that there's one element // we're sure that there's one element
let path = unsafe { paths.get_unchecked(0) }; let path = unsafe { paths.get_unchecked(0) };
match filesystem::open(ctx, path).map(|f| OggStreamReader::new(f).map_err(|e| GameError::ResourceLoadError(e.to_string()))) { match filesystem::open(ctx, path).map(|f| {
OggStreamReader::new(f).map_err(|e| GameError::ResourceLoadError(e.to_string()))
}) {
Ok(Ok(song)) => { Ok(Ok(song)) => {
log::info!("Playing single part Ogg BGM: {} {}", song_id, path); log::info!("Playing single part Ogg BGM: {} {}", song_id, path);
@ -157,16 +174,28 @@ impl SoundManager {
let path_loop = unsafe { paths.get_unchecked(1) }; let path_loop = unsafe { paths.get_unchecked(1) };
match ( match (
filesystem::open(ctx, path_intro).map(|f| OggStreamReader::new(f).map_err(|e| GameError::ResourceLoadError(e.to_string()))), filesystem::open(ctx, path_intro).map(|f| {
filesystem::open(ctx, path_loop).map(|f| OggStreamReader::new(f).map_err(|e| GameError::ResourceLoadError(e.to_string()))), OggStreamReader::new(f).map_err(|e| GameError::ResourceLoadError(e.to_string()))
}),
filesystem::open(ctx, path_loop).map(|f| {
OggStreamReader::new(f).map_err(|e| GameError::ResourceLoadError(e.to_string()))
}),
) { ) {
(Ok(Ok(song_intro)), Ok(Ok(song_loop))) => { (Ok(Ok(song_intro)), Ok(Ok(song_loop))) => {
log::info!("Playing multi part Ogg BGM: {} {} + {}", song_id, path_intro, path_loop); log::info!(
"Playing multi part Ogg BGM: {} {} + {}",
song_id,
path_intro,
path_loop
);
self.prev_song_id = self.current_song_id; self.prev_song_id = self.current_song_id;
self.current_song_id = song_id; self.current_song_id = song_id;
self.tx.send(PlaybackMessage::SaveState)?; self.tx.send(PlaybackMessage::SaveState)?;
self.tx.send(PlaybackMessage::PlayOggSongMultiPart(Box::new(song_intro), Box::new(song_loop)))?; self.tx.send(PlaybackMessage::PlayOggSongMultiPart(
Box::new(song_intro),
Box::new(song_loop),
))?;
return Ok(()); return Ok(());
} }
@ -197,7 +226,7 @@ impl SoundManager {
Ok(()) Ok(())
} }
pub fn set_speed(&mut self, speed: f32) -> GameResult { pub fn set_speed(&self, speed: f32) -> GameResult {
if speed <= 0.0 { if speed <= 0.0 {
return Err(InvalidValue(str!("Speed must be bigger than 0.0!"))); return Err(InvalidValue(str!("Speed must be bigger than 0.0!")));
} }
@ -209,6 +238,72 @@ impl SoundManager {
pub fn current_song(&self) -> usize { pub fn current_song(&self) -> usize {
self.current_song_id self.current_song_id
} }
pub fn set_sample_params_from_file<R: io::Read>(&self, id: u8, data: R) -> GameResult {
let mut reader = BufReader::new(data).lines();
let mut params = PixToneParameters::empty();
fn next_string<T: FromStr, R: io::Read>(reader: &mut Lines<BufReader<R>>) -> GameResult<T> {
loop {
if let Some(Ok(str)) = reader.next() {
let str = str.trim();
if str == "" || str.starts_with("#") {
continue;
}
let mut splits = str.split(":");
let _ = splits.next();
if let Some(str) = splits.next() {
println!("{}", str);
return str.trim().parse::<T>().map_err(|_| GameError::ParseError("failed to parse the value as specified type.".to_string()))
} else {
break;
}
} else {
break;
}
}
return Err(GameError::ParseError("unexpected end.".to_string()))
};
for channel in params.channels.iter_mut() {
channel.enabled = next_string::<u8, R>(&mut reader)? != 0;
channel.length = next_string::<u32, R>(&mut reader)?;
channel.carrier.waveform_type = next_string::<u8, R>(&mut reader)?;
channel.carrier.pitch = next_string::<f32, R>(&mut reader)?;
channel.carrier.level = next_string::<i32, R>(&mut reader)?;
channel.carrier.offset = next_string::<i32, R>(&mut reader)?;
channel.frequency.waveform_type = next_string::<u8, R>(&mut reader)?;
channel.frequency.pitch = next_string::<f32, R>(&mut reader)?;
channel.frequency.level = next_string::<i32, R>(&mut reader)?;
channel.frequency.offset = next_string::<i32, R>(&mut reader)?;
channel.amplitude.waveform_type = next_string::<u8, R>(&mut reader)?;
channel.amplitude.pitch = next_string::<f32, R>(&mut reader)?;
channel.amplitude.level = next_string::<i32, R>(&mut reader)?;
channel.amplitude.offset = next_string::<i32, R>(&mut reader)?;
channel.envelope.initial = next_string::<i32, R>(&mut reader)?;
channel.envelope.time_a = next_string::<i32, R>(&mut reader)?;
channel.envelope.value_a = next_string::<i32, R>(&mut reader)?;
channel.envelope.time_b = next_string::<i32, R>(&mut reader)?;
channel.envelope.value_b = next_string::<i32, R>(&mut reader)?;
channel.envelope.time_c = next_string::<i32, R>(&mut reader)?;
channel.envelope.value_c = next_string::<i32, R>(&mut reader)?;
}
self.set_sample_params(id, params)
}
pub fn set_sample_params(&self, id: u8, params: PixToneParameters) -> GameResult {
self.tx.send(PlaybackMessage::SetSampleParams(id, params))?;
Ok(())
}
} }
enum PlaybackMessage { enum PlaybackMessage {
@ -222,6 +317,7 @@ enum PlaybackMessage {
SetSpeed(f32), SetSpeed(f32),
SaveState, SaveState,
RestoreState, RestoreState,
SetSampleParams(u8, PixToneParameters),
} }
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
@ -239,7 +335,12 @@ enum PlaybackStateType {
Ogg(SavedOggPlaybackState), Ogg(SavedOggPlaybackState),
} }
fn run<T>(rx: Receiver<PlaybackMessage>, bank: SoundBank, device: &cpal::Device, config: &cpal::StreamConfig) -> GameResult fn run<T>(
rx: Receiver<PlaybackMessage>,
bank: SoundBank,
device: &cpal::Device,
config: &cpal::StreamConfig,
) -> GameResult
where where
T: cpal::Sample, T: cpal::Sample,
{ {
@ -257,10 +358,10 @@ where
log::info!("Audio format: {} {}", sample_rate, channels); log::info!("Audio format: {} {}", sample_rate, channels);
org_engine.set_sample_rate(sample_rate as usize); org_engine.set_sample_rate(sample_rate as usize);
#[cfg(feature = "ogg-playback")] #[cfg(feature = "ogg-playback")]
{ {
org_engine.loops = usize::MAX; org_engine.loops = usize::MAX;
ogg_engine.set_sample_rate(sample_rate as usize); ogg_engine.set_sample_rate(sample_rate as usize);
} }
let buf_size = sample_rate as usize * 10 / 1000; let buf_size = sample_rate as usize * 10 / 1000;
let mut bgm_buf = vec![0x8080; buf_size * 2]; let mut bgm_buf = vec![0x8080; buf_size * 2];
@ -388,6 +489,9 @@ where
} }
} }
} }
Ok(PlaybackMessage::SetSampleParams(id, params)) => {
pixtone.set_sample_parameters(id, params);
}
Err(_) => { Err(_) => {
break; break;
} }
@ -449,16 +553,25 @@ where
} }
if frame.len() >= 2 { if frame.len() >= 2 {
let sample_l = let sample_l = clamp(
clamp((((bgm_sample_l ^ 0x8000) as i16) as isize) + (((pxt_sample ^ 0x8000) as i16) as isize), -0x7fff, 0x7fff) as u16 ^ 0x8000; (((bgm_sample_l ^ 0x8000) as i16) as isize) + (((pxt_sample ^ 0x8000) as i16) as isize),
let sample_r = -0x7fff,
clamp((((bgm_sample_r ^ 0x8000) as i16) as isize) + (((pxt_sample ^ 0x8000) as i16) as isize), -0x7fff, 0x7fff) as u16 ^ 0x8000; 0x7fff,
) as u16
^ 0x8000;
let sample_r = clamp(
(((bgm_sample_r ^ 0x8000) as i16) as isize) + (((pxt_sample ^ 0x8000) as i16) as isize),
-0x7fff,
0x7fff,
) as u16
^ 0x8000;
frame[0] = Sample::from::<u16>(&sample_l); frame[0] = Sample::from::<u16>(&sample_l);
frame[1] = Sample::from::<u16>(&sample_r); frame[1] = Sample::from::<u16>(&sample_r);
} else { } else {
let sample = clamp( let sample = clamp(
((((bgm_sample_l ^ 0x8000) as i16) + ((bgm_sample_r ^ 0x8000) as i16)) / 2) as isize + (((pxt_sample ^ 0x8000) as i16) as isize), ((((bgm_sample_l ^ 0x8000) as i16) + ((bgm_sample_r ^ 0x8000) as i16)) / 2) as isize
+ (((pxt_sample ^ 0x8000) as i16) as isize),
-0x7fff, -0x7fff,
0x7fff, 0x7fff,
) as u16 ) as u16

View File

@ -4,7 +4,7 @@ use lazy_static::lazy_static;
use num_traits::clamp; use num_traits::clamp;
use vec_mut_scan::VecMutScan; use vec_mut_scan::VecMutScan;
use crate::sound::pixtone_sfx::PIXTONE_TABLE; use crate::sound::pixtone_sfx::{DEFAULT_PIXTONE_TABLE};
use crate::sound::stuff::cubic_interp; use crate::sound::stuff::cubic_interp;
lazy_static! { lazy_static! {
@ -31,17 +31,7 @@ lazy_static! {
}; };
} }
/*#[test] #[derive(Copy, Clone)]
fn test_waveforms() {
let reference = include_bytes!("pixtone_ref.dat");
for n in 1..(WAVEFORMS.len()) {
for (i, &val) in WAVEFORMS[n].iter().enumerate() {
assert_eq!((val as u8, i, n), (reference[n as usize * 256 + i], i, n));
}
}
}*/
pub struct Waveform { pub struct Waveform {
pub waveform_type: u8, pub waveform_type: u8,
pub pitch: f32, pub pitch: f32,
@ -55,6 +45,7 @@ impl Waveform {
} }
} }
#[derive(Copy, Clone)]
pub struct Envelope { pub struct Envelope {
pub initial: i32, pub initial: i32,
pub time_a: i32, pub time_a: i32,
@ -104,6 +95,7 @@ impl Envelope {
} }
} }
#[derive(Copy, Clone)]
pub struct Channel { pub struct Channel {
pub enabled: bool, pub enabled: bool,
pub length: u32, pub length: u32,
@ -149,6 +141,7 @@ impl Channel {
} }
} }
#[derive(Copy, Clone)]
pub struct PixToneParameters { pub struct PixToneParameters {
pub channels: [Channel; 4], pub channels: [Channel; 4],
} }
@ -204,23 +197,39 @@ pub struct PlaybackState(u8, f32, u32);
pub struct PixTonePlayback { pub struct PixTonePlayback {
pub samples: HashMap<u8, Vec<i16>>, pub samples: HashMap<u8, Vec<i16>>,
pub playback_state: Vec<PlaybackState>, pub playback_state: Vec<PlaybackState>,
pub table: [PixToneParameters; 256],
} }
#[allow(unused)]
impl PixTonePlayback { impl PixTonePlayback {
pub fn new() -> PixTonePlayback { pub fn new() -> PixTonePlayback {
let mut table = [PixToneParameters::empty(); 256];
for (i, params) in DEFAULT_PIXTONE_TABLE.iter().enumerate() {
table[i] = *params;
}
PixTonePlayback { PixTonePlayback {
samples: HashMap::new(), samples: HashMap::new(),
playback_state: vec![], playback_state: vec![],
table,
} }
} }
pub fn create_samples(&mut self) { pub fn create_samples(&mut self) {
for (i, params) in PIXTONE_TABLE.iter().enumerate() { for (i, params) in self.table.iter().enumerate() {
self.samples.insert(i as u8, params.synth()); self.samples.insert(i as u8, params.synth());
} }
} }
pub fn set_sample_parameters(&mut self, id: u8, params: PixToneParameters) {
self.table[id as usize] = params;
self.samples.insert(id, params.synth());
}
pub fn set_sample_data(&mut self, id: u8, data: Vec<i16>) {
self.samples.insert(id, data);
}
pub fn play_sfx(&mut self, id: u8) { pub fn play_sfx(&mut self, id: u8) {
for state in self.playback_state.iter_mut() { for state in self.playback_state.iter_mut() {
if state.0 == id && state.2 == 0 { if state.0 == id && state.2 == 0 {

View File

@ -1,6 +1,6 @@
use crate::sound::pixtone::{Channel, Envelope, PixToneParameters, Waveform}; use crate::sound::pixtone::{Channel, Envelope, PixToneParameters, Waveform};
pub static PIXTONE_TABLE: [PixToneParameters; 160] = [ pub static DEFAULT_PIXTONE_TABLE: [PixToneParameters; 160] = [
PixToneParameters::empty(), // fx0 PixToneParameters::empty(), // fx0
PixToneParameters { // fx1 PixToneParameters { // fx1
channels: [ channels: [
@ -40,21 +40,21 @@ pub static PIXTONE_TABLE: [PixToneParameters; 160] = [
Channel::disabled(), Channel::disabled(),
], ],
}, },
PixToneParameters { // fx2 (CS+) PixToneParameters { // fx2
channels: [ channels: [
Channel { Channel {
enabled: true, enabled: true,
length: 2000, length: 4000,
carrier: Waveform { carrier: Waveform {
waveform_type: 0, waveform_type: 1,
pitch: 92.000000, pitch: 54.000000,
level: 32, level: 32,
offset: 0, offset: 0,
}, },
frequency: Waveform { frequency: Waveform {
waveform_type: 0, waveform_type: 5,
pitch: 3.000000, pitch: 0.100000,
level: 44, level: 33,
offset: 0, offset: 0,
}, },
amplitude: Waveform { amplitude: Waveform {
@ -64,11 +64,11 @@ pub static PIXTONE_TABLE: [PixToneParameters; 160] = [
offset: 0, offset: 0,
}, },
envelope: Envelope { envelope: Envelope {
initial: 7, initial: 53,
time_a: 2, time_a: 57,
value_a: 18, value_a: 44,
time_b: 128, time_b: 128,
value_b: 0, value_b: 24,
time_c: 255, time_c: 255,
value_c: 0, value_c: 0,
}, },
@ -78,44 +78,6 @@ pub static PIXTONE_TABLE: [PixToneParameters; 160] = [
Channel::disabled(), Channel::disabled(),
], ],
}, },
// PixToneParameters { // fx2
// channels: [
// Channel {
// enabled: true,
// length: 4000,
// carrier: Waveform {
// waveform_type: 1,
// pitch: 54.000000,
// level: 32,
// offset: 0,
// },
// frequency: Waveform {
// waveform_type: 5,
// pitch: 0.100000,
// level: 33,
// offset: 0,
// },
// amplitude: Waveform {
// waveform_type: 0,
// pitch: 0.000000,
// level: 32,
// offset: 0,
// },
// envelope: Envelope {
// initial: 53,
// time_a: 57,
// value_a: 44,
// time_b: 128,
// value_b: 24,
// time_c: 255,
// value_c: 0,
// },
// },
// Channel::disabled(),
// Channel::disabled(),
// Channel::disabled(),
// ],
// },
PixToneParameters { // fx3 PixToneParameters { // fx3
channels: [ channels: [
Channel { Channel {