From d86efb29f230a2b354d3cceb1a559c6e5cb4cb43 Mon Sep 17 00:00:00 2001 From: ashley Date: Tue, 22 Aug 2023 11:12:38 -0400 Subject: [PATCH] Better data structure for tracker effects --- src/reskit/soundtrack/formats/dmf.rs | 75 ++++++++++++++++++++++++++-- src/reskit/soundtrack/types.rs | 27 ++++++++-- 2 files changed, 95 insertions(+), 7 deletions(-) diff --git a/src/reskit/soundtrack/formats/dmf.rs b/src/reskit/soundtrack/formats/dmf.rs index 719c162..35285d9 100644 --- a/src/reskit/soundtrack/formats/dmf.rs +++ b/src/reskit/soundtrack/formats/dmf.rs @@ -2,7 +2,7 @@ use std::{error::Error, fs::File, io::Read, convert::TryInto, collections::HashM use flate2::read::ZlibDecoder; use samplerate::convert; use uuid::Uuid; -use crate::reskit::{utility::{get_string, get_u8, skip, get_u32, get_i8, get_i32, get_u16, get_i16, Ring, print_warning}, soundtrack::{types::{SampleFormat, PsgEnvelope, Note, Sample, PsgSettings, PatternRow, Effect}, engines::echo::{EchoFormat, EchoEvent}}}; +use crate::reskit::{utility::{get_string, get_u8, skip, get_u32, get_i8, get_i32, get_u16, get_i16, Ring, print_warning}, soundtrack::{types::{SampleFormat, PsgEnvelope, Note, Sample, PsgSettings, PatternRow, Effect, DcsgChannelMode, NoiseType}, engines::echo::{EchoFormat, EchoEvent}}}; const DMF_MAGIC_NUMBER: &'static str = ".DelekDefleMask."; const DMF_SUPPORTED_VERSION: u8 = 27; @@ -10,6 +10,17 @@ const DMF_MD: u8 = 0x02; const DMF_MD_ENHANCED_CH3: u8 = 0x42; const ECHO_EWF_SAMPLE_RATE: u32 = 10650; +const DMF_EFFECT_ARPEGGIO: u8 = 0x00; +const DMF_EFFECT_PORTAMENTO_UP: u8 = 0x01; +const DMF_EFFECT_PORTAMENTO_DOWN: u8 = 0x02; +const DMF_EFFECT_PORTA_TO_NOTE: u8 = 0x03; +const DMF_EFFECT_SET_PANNING: u8 = 0x08; +const DMF_EFFECT_POSITION_JUMP: u8 = 0x0B; +const DMF_EFFECT_PATTERN_BREAK: u8 = 0x0D; +const DMF_EFFECT_DAC_ENABLE: u8 = 0x17; +const DMF_EFFECT_PSG_NOISE_MODE: u8 = 0x20; +const DMF_EFFECT_NOTE_CUT: u8 = 0xEC; + #[derive(Debug)] pub enum FrameMode { Ntsc, @@ -525,12 +536,68 @@ impl DmfModule { for _ in 0..num_effects { let effect_code = get_i16( bytes.by_ref() )?; let effect_value = get_i16( bytes.by_ref() )?; + let effect_value: Option = if effect_value == -1 { None } else { Some( effect_value as u8 ) }; if effect_code != -1 { effects.push( - Effect{ - effect_code, - effect_value: if effect_value != -1 { Some( effect_value ) } else { None } + match effect_code as u8 { + DMF_EFFECT_ARPEGGIO => Effect::Arpeggio { + first_shift: effect_value.ok_or( "invalid file: expected effect value for arpeggio" )? >> 4, + second_shift: effect_value.ok_or( "invalid file: expected effect value for arpeggio" )? & 0x0F + }, + DMF_EFFECT_PORTAMENTO_UP => Effect::PortamentoUp { + speed: effect_value.ok_or( "invalid file: expected effect value for portamento up" )? + }, + DMF_EFFECT_PORTAMENTO_DOWN => Effect::PortamentoDown { + speed: effect_value.ok_or( "invalid file: expected effect value for portamento down`" )? + }, + DMF_EFFECT_PORTA_TO_NOTE => Effect::PortamentoToNote { + speed: effect_value.ok_or( "invalid file: expected effect value for porta to note" )? + }, + DMF_EFFECT_SET_PANNING => { + let effect_value = effect_value.ok_or( "invalid file: expected effect value for panning" )?; + + Effect::SetPanning { + left: effect_value == 0x10 || effect_value == 0x11, + right: effect_value == 0x01 || effect_value == 0x11 + } + }, + DMF_EFFECT_POSITION_JUMP => { + let pattern = effect_value.ok_or( "invalid file: expected effect value for position jump" )?; + + Effect::PositionJump { pattern } + }, + DMF_EFFECT_PATTERN_BREAK => { + let start_row = effect_value.ok_or( "invalid file: expected effect value for start row" )?; + + Effect::JumpToNextPattern { start_row } + }, + DMF_EFFECT_DAC_ENABLE => { + let enabled = effect_value.ok_or( "invalid file: expected effect value for dac enable" )? != 0; + + Effect::DacEnable { enabled } + }, + DMF_EFFECT_PSG_NOISE_MODE => { + let effect_value = effect_value.ok_or( "invalid file: expected effect value for psg noise mode" )?; + let mode = match effect_value { + 0x00 | 0x01 => DcsgChannelMode::FixedFrequency, + 0x10 | 0x11 => DcsgChannelMode::Ch3Frequency, + _ => return Err( "invalid file: invalid effect value setting for psg noise mode" )? + }; + let noise_type = match effect_value { + 0x00 | 0x10 => NoiseType::PeriodicNoise, + 0x01 | 0x11 => NoiseType::WhiteNoise, + _ => return Err( "invalid file: invalid effect value setting for psg noise mode" )? + }; + + Effect::DcsgNoiseMode { mode, noise_type } + }, + DMF_EFFECT_NOTE_CUT => { + let after_ticks = effect_value.ok_or( "invalid file: expected effect value setting for note cut" )?; + + Effect::NoteCut { after_ticks } + } + _ => Effect::UnknownEffect{ code: effect_code as u8, parameter: effect_value } } ); } diff --git a/src/reskit/soundtrack/types.rs b/src/reskit/soundtrack/types.rs index b5601f1..7a832b5 100644 --- a/src/reskit/soundtrack/types.rs +++ b/src/reskit/soundtrack/types.rs @@ -49,9 +49,30 @@ pub enum Note { } #[derive(Debug)] -pub struct Effect { - pub effect_code: i16, - pub effect_value: Option +pub enum DcsgChannelMode { + Ch3Frequency, + FixedFrequency +} + +#[derive(Debug)] +pub enum NoiseType { + PeriodicNoise, + WhiteNoise +} + +#[derive(Debug)] +pub enum Effect { + Arpeggio { first_shift: u8, second_shift: u8 }, + PortamentoUp { speed: u8 }, + PortamentoDown { speed: u8 }, + PortamentoToNote { speed: u8 }, + SetPanning { left: bool, right: bool }, + PositionJump { pattern: u8 }, + JumpToNextPattern { start_row: u8 }, + DacEnable { enabled: bool }, + DcsgNoiseMode { mode: DcsgChannelMode, noise_type: NoiseType }, + NoteCut { after_ticks: u8 }, + UnknownEffect { code: u8, parameter: Option } } #[derive(Debug)]