From 3ea276cfa4cc639746a97ff08054601ce88a7586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?joseLu=C3=ADs?= Date: Fri, 30 Sep 2022 14:09:55 +0200 Subject: [PATCH] several updates and fixes - fix clippy warnings. - derive more traits, specially Clone, PartialEq, Eq, Default. - update comments. - update example. - rustfmt. --- examples/parse.rs | 5 +++ src/sfz/group.rs | 4 ++- src/sfz/headers.rs | 2 +- src/sfz/instrument.rs | 4 +-- src/sfz/opcodes/opcode.rs | 2 +- src/sfz/opcodes/parse.rs | 14 ++++---- src/sfz/region.rs | 4 ++- src/sfz/types.rs | 67 +++++++++++++++++++++++++++++++++++---- 8 files changed, 81 insertions(+), 21 deletions(-) diff --git a/examples/parse.rs b/examples/parse.rs index 6efb41a..6d21390 100644 --- a/examples/parse.rs +++ b/examples/parse.rs @@ -14,6 +14,11 @@ fn main() { Please run the example from the examples/ directory", ); + // for region in &i.regions { + // println!("{:?}", region); + // } + + println!("{:#?}", i); println!("groups: {}\nregions: {}", i.groups(), i.regions()); diff --git a/src/sfz/group.rs b/src/sfz/group.rs index f57a063..764610c 100644 --- a/src/sfz/group.rs +++ b/src/sfz/group.rs @@ -7,7 +7,7 @@ use crate::sfz::{Opcode, OpcodeMap}; /// A group is defined with the opcode, and the parameters enumerated /// on it last till the next group opcode, or till the end of the file. /// -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default, PartialEq)] pub struct Group { /// This list of opcodes overwrites the default ones. pub opcodes: OpcodeMap, @@ -17,10 +17,12 @@ pub struct Group { } impl Group { + /// New group. pub fn new() -> Self { Self::default() } + /// Add an opcode to the group. pub fn add_opcode(&mut self, o: &Opcode) { self.opcodes.insert(o.str_name(), o.clone()); } diff --git a/src/sfz/headers.rs b/src/sfz/headers.rs index 9185815..388452f 100644 --- a/src/sfz/headers.rs +++ b/src/sfz/headers.rs @@ -9,7 +9,7 @@ use crate::sfz::SfzToken; /// /// [sfzformat.com/headers/](https://sfzformat.com/headers/) -#[derive(Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Header { // sfz v1 headers /// The basic component of an instrument. An instrument is defined by one or more regions. diff --git a/src/sfz/instrument.rs b/src/sfz/instrument.rs index 0bfb15a..08eaa75 100644 --- a/src/sfz/instrument.rs +++ b/src/sfz/instrument.rs @@ -22,7 +22,7 @@ use crate::{ /// International Pitch Notation (IPN) convention. According to this rules, /// middle C in the keyboard is C4 and the MIDI note number 60. /// -#[derive(Debug)] +#[derive(Clone, Debug)] pub struct Instrument { /// The default opcodes for this instrument. pub global: OpcodeMap, @@ -341,7 +341,7 @@ impl Instrument { } /// The current status of the parsing of the instrument -#[derive(Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] struct InstrumentParsingStatus { is_header_control: bool, is_header_global: bool, diff --git a/src/sfz/opcodes/opcode.rs b/src/sfz/opcodes/opcode.rs index 63c8689..e484492 100644 --- a/src/sfz/opcodes/opcode.rs +++ b/src/sfz/opcodes/opcode.rs @@ -26,7 +26,7 @@ use crate::sfz::types::{ /// - [Cakewalk Extensions Opcodes](https://sfzformat.com/extensions/cakewalk/) /// #[allow(non_camel_case_types)] -#[derive(Debug, PartialEq, Clone)] +#[derive(Clone, Debug, PartialEq)] pub enum Opcode { // sfz v1 opcodes ----------------------------------------------------------- // https://sfzformat.com/misc/sfz2 diff --git a/src/sfz/opcodes/parse.rs b/src/sfz/opcodes/parse.rs index daa9731..cdfedb3 100644 --- a/src/sfz/opcodes/parse.rs +++ b/src/sfz/opcodes/parse.rs @@ -172,7 +172,7 @@ impl Opcode { ("cutoff", _) => { utils::check_f32_between(value, 0., MAX_SAMPLE_RATE).map(Opcode::cutoff) } - ("fil_type", _) => fil_type::from_str(value).map(Opcode::fil_type), + ("fil_type", _) => fil_type::from_name(value).map(Opcode::fil_type), ("fil_veltrack", _) => { utils::check_i16_between(value, -9600, 9600).map(Opcode::fil_veltrack) } @@ -183,16 +183,14 @@ impl Opcode { // NOTE: lokey v2 accepts i8, from -1: ("lokey", _) => utils::check_midi_note(value).map(Opcode::lokey), ("lovel", _) => utils::check_u8_between(value, 0, 127).map(Opcode::lovel), - ("loop_mode", _) => loop_mode::from_str(value).map(Opcode::loop_mode), + ("loop_mode", _) => loop_mode::from_name(value).map(Opcode::loop_mode), ("lorand", _) => utils::check_f32_between(value, 0., 1.).map(Opcode::lorand), ("off_by", _) => utils::check_u32_between(value, 0, u32::MAX).map(Opcode::off_by), ("offset", _) => utils::check_u32_between(value, 0, u32::MAX).map(Opcode::offset), ("on_loccN", _) => utils::check_i8_between(value, 0, 127).map(Opcode::on_loccN), ("on_hiccN", _) => utils::check_i8_between(value, 0, 127).map(Opcode::on_hiccN), ("pan", _) => utils::check_f32_between(value, 0., 100.).map(Opcode::pan), - ("pitch_keycenter", _) => { - utils::check_midi_note(value).map(Opcode::pitch_keycenter) - } + ("pitch_keycenter", _) => utils::check_midi_note(value).map(Opcode::pitch_keycenter), ("pitch_keytrack", _) => { utils::check_i16_between(value, -1200, 1200).map(Opcode::pitch_keytrack) } @@ -203,7 +201,7 @@ impl Opcode { ("sample", _) => Some(Opcode::sample(utils::fix_path_separators(value))), ("seq_lenght", _) => utils::check_u8_between(value, 1, 100).map(Opcode::seq_length), ("seq_position", _) => utils::check_u8_between(value, 1, 100).map(Opcode::seq_position), - ("trigger", _) => trigger::from_str(value).map(Opcode::trigger), + ("trigger", _) => trigger::from_name(value).map(Opcode::trigger), ("sw_hikey", _) => utils::check_midi_note(value).map(Opcode::sw_hikey), ("sw_last", _) => utils::check_u8_between(value, 0, 127).map(Opcode::sw_last), ("sw_lokey", _) => utils::check_midi_note(value).map(Opcode::sw_lokey), @@ -232,7 +230,7 @@ impl Opcode { /// Token for parsing SFZ format elements like headers and tokens /// -#[derive(Logos, Debug, PartialEq)] +#[derive(Logos, Clone, Debug, PartialEq)] pub(crate) enum SfzToken { /// Parses a Header /// @@ -266,7 +264,7 @@ pub(crate) enum SfzToken { /// Some opcode names contains numbers that must not be interpreted as parameters. /// It makes sure to filter out false positives, /// as parameters, -#[derive(Logos, Debug, PartialEq)] +#[derive(Logos, Clone, Debug, PartialEq)] pub(crate) enum OpcodeParameter { /// Skip numbers that must not be recognized as parameters, /// since they are part of the opcode's name. diff --git a/src/sfz/region.rs b/src/sfz/region.rs index 6e5f671..95f08b9 100644 --- a/src/sfz/region.rs +++ b/src/sfz/region.rs @@ -20,7 +20,7 @@ use crate::sfz::{Opcode, OpcodeMap}; /// All Input Controls defined in a region act using the AND boolean operator. /// Consequently, all conditions must be matched for the region to play. /// -#[derive(Debug, Default)] +#[derive(Clone, Debug, Default)] pub struct Region { /// The opcodes of this group are applied and will override the defaults. pub group: Option, @@ -30,10 +30,12 @@ pub struct Region { } impl Region { + /// New region. pub fn new() -> Self { Self::default() } + /// New region with some group. // FIXME (add group at posteriori) pub fn with_group(group: usize) -> Self { Self { diff --git a/src/sfz/types.rs b/src/sfz/types.rs index 5580b89..587e013 100644 --- a/src/sfz/types.rs +++ b/src/sfz/types.rs @@ -22,7 +22,7 @@ pub const MAX_SAMPLE_RATE: f32 = 384_000.0; /// All the possible types allowed in an Opcode #[allow(non_camel_case_types)] -#[derive(Debug, Clone)] +#[derive(Clone, Debug, PartialEq, Eq)] pub enum OpcodeType { i8(Option), u8(Option), @@ -43,17 +43,41 @@ pub type OpcodeMap = HashMap; /// Allows playing samples with loops defined in the unlooped mode. /// /// - info: [loop_mode](https://sfzformat.com/opcodes/loop_mode) -#[derive(Debug, Clone, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[allow(non_camel_case_types)] pub enum loop_mode { + /// no looping will be performed. Sample will play straight from start to end, + /// or until note off, whatever reaches first. + /// + /// This is the default. no_loop, + + /// sample will play from start to end, ignoring note off. This is commonly + /// used for drums. This mode is engaged automatically if the count opcode + /// is defined. one_shot, + + /// once the player reaches sample loop point, the loop will play until note + /// expiration. This includes looping during the release phase. loop_continuous, + + /// the player will play the loop while the note is held, by keeping it + /// depressed or by using the sustain pedal (CC64). During the release phase, + /// there’s no looping. loop_sustain, } + +// IMPROVE: `no_loop` for samples without a loop defined, +// `loop_continuous` for samples with defined loop(s). +impl Default for loop_mode { + fn default() -> loop_mode { + Self::no_loop + } +} + impl loop_mode { /// Constructor from the variant name, as a string - pub fn from_str(name: &str) -> Option { + pub fn from_name(name: &str) -> Option { match name { "no_loop" => Some(Self::no_loop), "one_shot" => Some(Self::one_shot), @@ -67,18 +91,38 @@ impl loop_mode { /// Sets the trigger which will be used for the sample to play. /// /// - info: [trigger](https://sfzformat.com/opcodes/trigger) -#[derive(Debug, Clone, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[allow(non_camel_case_types)] pub enum trigger { + /// (Default): Region will play on note-on. attack, + + /// Region will play on note-off or sustain pedal off. The velocity used to + /// play the note-off sample is the velocity value of the corresponding + /// (previous) note-on message. release, + + /// Region will play on note-on, but if there’s no other note going on + /// (comoonly used for or first note in a legato phrase). first, + + /// Region will play on note-on, but only if there’s a note going on + /// (notes after first note in a legato phrase). legato, + + /// Region will play on note-off. Ignores sustain pedal. release_key, // aria } + +impl Default for trigger { + fn default() -> trigger { + Self::attack + } +} + impl trigger { /// Constructor from the variant name, as a string - pub fn from_str(name: &str) -> Option { + pub fn from_name(name: &str) -> Option { match name { "attack" => Some(Self::attack), "release" => Some(Self::release), @@ -93,7 +137,7 @@ impl trigger { /// Allows you to choose which type of filter you use if not specified /// /// - info: [fil_type](https://sfzformat.com/opcodes/fil_type) -#[derive(Debug, Clone, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] #[allow(non_camel_case_types)] pub enum fil_type { /// One-pole low pass filter (6dB/octave) @@ -108,6 +152,8 @@ pub enum fil_type { /// Two-pole low pass filter (12dB/octave) /// + /// This is the default. + /// /// - version: v1 lpf_2p, @@ -211,9 +257,16 @@ pub enum fil_type { /// - version: ARIA peq, } + +impl Default for fil_type { + fn default() -> fil_type { + Self::lpf_2p + } +} + impl fil_type { /// Constructor from the variant name, as a string - pub fn from_str(name: &str) -> Option { + pub fn from_name(name: &str) -> Option { match name { "lpf_1p" => Some(Self::lpf_1p), "hpf_1p" => Some(Self::hpf_1p),