Skip to content

Commit

Permalink
Issue14: Typed parameters for Reverb (#34)
Browse files Browse the repository at this point in the history
* Restructure chorus effect modules

* Implement first reverb type enum

* Implement CathedralParameters and Gm2ReverbParameters

* Implement CharacterParameters

* Fix damp gain types

* Add CharacterParameter generics for default

* Add generic to OneIndexedU8
  • Loading branch information
davidlang42 authored Aug 14, 2023
1 parent 19b24d2 commit a6e12bf
Show file tree
Hide file tree
Showing 12 changed files with 519 additions and 134 deletions.
4 changes: 2 additions & 2 deletions src/json/warnings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

use strum::IntoEnumIterator;

use crate::roland::{live_set::{LiveSet, mfx::Mfx, reverb::Reverb, chorus::Chorus}, layers::InternalLayer, types::enums::{Layer, ReverbType, PedalFunction}};
use crate::roland::{live_set::{LiveSet, mfx::Mfx, reverb::Reverb, chorus::Chorus}, layers::InternalLayer, types::enums::{Layer, PedalFunction}};

use super::validation::LayerRanges;

Expand Down Expand Up @@ -58,7 +58,7 @@ pub fn tone_remain_warnings(a: &LiveSet, b: &LiveSet, fc1_from_system: Option<Pe
&a.layers[i].internal,
&b.layers[i].internal,
!a.chorus.chorus_type.is_off(),
a.reverb.reverb_type != ReverbType::Off,
a.reverb.reverb_type.is_off(),
&fc1_from_system.unwrap_or(a.common.fc1_assign),
&fc1_from_system.unwrap_or(b.common.fc1_assign),
&fc2_from_system.unwrap_or(a.common.fc2_assign),
Expand Down
2 changes: 1 addition & 1 deletion src/roland/live_set/chorus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use validator::Validate;
use crate::bytes::{Bytes, BytesError, Bits, BitStream};
use crate::json::{Json, StructuredJson, StructuredJsonError};
use crate::roland::types::enums::OutputSelect;
use crate::roland::types::effects::ChorusType;
use crate::roland::types::effects::chorus::ChorusType;
use crate::roland::types::numeric::Parameter;

#[derive(Serialize, Deserialize, Debug, JsonSchema, Validate)]
Expand Down
39 changes: 16 additions & 23 deletions src/roland/live_set/reverb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@ use schemars::JsonSchema;
use validator::Validate;

use crate::bytes::{Bytes, BytesError, Bits, BitStream};
use crate::json::{Json, StructuredJson, StructuredJsonError, serialize_default_terminated_array};
use crate::json::validation::valid_boxed_elements;
use crate::json::{Json, StructuredJson, StructuredJsonError};
use crate::roland::types::effects::reverb::ReverbType;
use crate::roland::types::numeric::Parameter;
use crate::roland::types::enums::ReverbType;

#[derive(Serialize, Deserialize, Debug, JsonSchema, Validate)]
pub struct Reverb {
Expand All @@ -16,23 +15,18 @@ pub struct Reverb {
depth: u8,
#[serde(skip_serializing_if="Bits::is_zero", default="Bits::<2>::zero")]
unused1: Bits<2>,
#[serde(deserialize_with = "serialize_default_terminated_array::deserialize")]
#[serde(serialize_with = "serialize_default_terminated_array::serialize")]
#[schemars(with = "serialize_default_terminated_array::DefaultTerminatedArraySchema::<Parameter, 20>")]
#[validate(custom = "valid_boxed_elements")]
parameters: Box<[Parameter; 20]>,
#[serde(skip_serializing_if="Bits::is_zero", default="Bits::<3>::zero")]
unused2: Bits<3>
}

impl Bytes<42> for Reverb {
fn to_bytes(&self) -> Result<Box<[u8; 42]>, BytesError> {
BitStream::write_fixed(|bs| {
bs.set_u8::<4>(self.reverb_type.into(), 0, 6)?;
bs.set_u8::<4>(self.reverb_type.number(), 0, 6)?;
bs.set_u8::<7>(self.depth, 0, 127)?;
bs.set_bits(&self.unused1);
for i in 0..self.parameters.len() {
bs.set_u16::<16>(self.parameters[i].into(), 12768, 52768)?;
for p in self.reverb_type.parameters().into_iter() {
bs.set_u16::<16>(p.into(), 12768, 52768)?;
}
bs.set_bits(&self.unused2);
Ok(())
Expand All @@ -41,18 +35,17 @@ impl Bytes<42> for Reverb {

fn from_bytes(bytes: Box<[u8; Self::BYTE_SIZE]>) -> Result<Self, BytesError> where Self: Sized {
BitStream::read_fixed(bytes, |bs| {
let reverb_type = bs.get_u8::<4>(0, 6)?.into();
let type_number = bs.get_u8::<4>(0, 6)?;
let depth = bs.get_u8::<7>(0, 127)?;
let unused1 = bs.get_bits();
let mut parameters = [Parameter::default(); 20];
for i in 0..parameters.len() {
parameters[i] = bs.get_u16::<16>(12768, 52768)?.into();
}
Ok(Self {
reverb_type,
reverb_type: ReverbType::from(type_number, parameters),
depth,
unused1,
parameters: Box::new(parameters),
unused2: bs.get_bits()
})
})
Expand All @@ -79,20 +72,20 @@ impl Json for Reverb {

impl Reverb {
pub fn tone_remain_warning(a: &Self, b: &Self, a_max_reverb_level: u8, b_max_reverb_level: u8) -> Option<String> {
let a_off = a_max_reverb_level == 0 || a.reverb_type == ReverbType::Off;
let b_off = b_max_reverb_level == 0 || b.reverb_type == ReverbType::Off;
let a_off = a_max_reverb_level == 0 || a.reverb_type.is_off();
let b_off = b_max_reverb_level == 0 || b.reverb_type.is_off();
if a_off && !b_off {
Some(format!("Reverb ({:?}) turns ON", b.reverb_type))
Some(format!("Reverb ({}) turns ON", b.reverb_type.name()))
} else if !a_off && b_off {
Some(format!("Reverb ({:?}) turns OFF", a.reverb_type))
Some(format!("Reverb ({}) turns OFF", a.reverb_type.name()))
} else if a_off && b_off {
None // other changes to Reverb are irrelevant if Reverb is off
} else if a.reverb_type != b.reverb_type {
Some(format!("Reverb ({:?}) changes to {:?}", a.reverb_type, b.reverb_type))
} else if a.reverb_type.number() != b.reverb_type.number() {
Some(format!("Reverb ({}) changes to {}", a.reverb_type.name(), b.reverb_type.name()))
} else if a.depth != b.depth {
Some(format!("Reverb ({:?}) depth changes from {} to {}", a.reverb_type, a.depth, b.depth))
} else if a.parameters != b.parameters {
Some(format!("Reverb ({:?}) parameters change", a.reverb_type))
Some(format!("Reverb ({}) depth changes from {} to {}", a.reverb_type.name(), a.depth, b.depth))
} else if a.reverb_type.parameters() != b.reverb_type.parameters() {
Some(format!("Reverb ({}) parameters change", a.reverb_type.name()))
} else {
None
}
Expand Down
8 changes: 4 additions & 4 deletions src/roland/system/favorites.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ use crate::roland::types::numeric::{OneIndexedU16, OneIndexedU8};
#[derive(Serialize, Deserialize, Debug, JsonSchema, Validate)]
pub struct Favorites {
#[validate]
one_touch_piano_current_number: OneIndexedU8,
one_touch_piano_current_number: OneIndexedU8<128>,
#[validate]
unused_one_touch_piano_current_number: [OneIndexedU8; 2],
unused_one_touch_piano_current_number: [OneIndexedU8<128>; 2],
#[validate]
one_touch_e_piano_current_number: OneIndexedU8,
one_touch_e_piano_current_number: OneIndexedU8<128>,
#[validate]
unused_one_touch_e_piano_current_number: [OneIndexedU8; 2],
unused_one_touch_e_piano_current_number: [OneIndexedU8<128>; 2],
#[validate]
bank_a: Bank,
#[validate]
Expand Down
47 changes: 7 additions & 40 deletions src/roland/types/effects.rs → src/roland/types/effects/chorus.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,11 @@
use super::{UnusedParameters, Parameters};
use super::parameters::{FilterType, RateMode, NoteLength, DelayMode};
use super::numeric::Parameter;
use super::super::numeric::Parameter;
use super::discrete::{LogFrequency, LogMilliseconds, LinearFrequency, LogFrequencyOrByPass, EvenPercent};
use crate::json::{serialize_default_terminated_array, validation::merge_all_fixed};
use crate::json::validation::{valid_boxed_elements, validate_boxed_array};
use crate::json::serialize_default_terminated_array;
use crate::json::validation::valid_boxed_elements;
use schemars::JsonSchema;
use validator::{Validate, ValidationErrors};

trait Parameters<const N: usize> : Validate + From<[Parameter; N]> {
fn parameters(&self) -> [Parameter; N];
}

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
pub struct UnusedParameters<const N: usize> {
#[serde(deserialize_with = "serialize_default_terminated_array::deserialize")]
#[serde(serialize_with = "serialize_default_terminated_array::serialize")]
#[schemars(with = "serialize_default_terminated_array::DefaultTerminatedArraySchema::<Parameter, {N}>")]
unused: Box<[Parameter; N]>
}

impl<const N: usize> From<[Parameter; N]> for UnusedParameters<N> {
fn from(value: [Parameter; N]) -> Self {
Self {
unused: Box::new(value)
}
}
}

impl<const N: usize> Parameters<N> for UnusedParameters<N> {
fn parameters(&self) -> [Parameter; N] {
*self.unused
}
}

impl<const N: usize> Validate for UnusedParameters<N> {
fn validate(&self) -> Result<(), ValidationErrors> {
let mut r = Ok(());
r = merge_all_fixed(r, "unused", validate_boxed_array(&self.unused));
r
}
}
use validator::Validate;

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
pub enum ChorusType { // 0-3
Expand Down Expand Up @@ -114,7 +81,7 @@ impl Validate for ChorusType {
#[derive(Serialize, Deserialize, Debug, JsonSchema, Validate)]
pub struct ChorusParameters {
filter_type: FilterType,
cutoff_frequency: LogFrequency,
cutoff_frequency: LogFrequency<200, 8000>,
pre_delay: LogMilliseconds,
rate_mode: RateMode,
rate_hz: LinearFrequency,
Expand Down Expand Up @@ -200,7 +167,7 @@ pub struct DelayParameters {
delay_centre_ms: u16,
delay_centre_note: NoteLength,
centre_feedback_percent: EvenPercent,
hf_damp: LogFrequencyOrByPass,
hf_damp: LogFrequencyOrByPass<200, 8000>,
#[validate(range(max = 127))]
left_level: u8,
#[validate(range(max = 127))]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ use schemars::JsonSchema;
use serde_json::Value;
use crate::json::{schema::enum_schema, type_name_pretty};

use super::numeric::Parameter;
use super::super::numeric::Parameter;
use serde::{Serialize, Deserialize, de};

trait DiscreteValues<T: PartialEq + Display, const OFFSET: i16> {
pub trait DiscreteValues<T: PartialEq + Display, const OFFSET: i16> {
const OFFSET: i16 = OFFSET;

fn values() -> Vec<T>;
Expand All @@ -31,15 +31,15 @@ trait DiscreteValues<T: PartialEq + Display, const OFFSET: i16> {
}

#[derive(Debug, Copy, Clone)]
pub struct LogFrequency(pub u16); // 0-16 (200-8000Hz)
pub struct LogFrequency<const MIN: u16, const MAX: u16>(pub u16); // 0-16 (200-8000Hz)

impl LogFrequency {
const BASE_VALUES: [u16; 10] = [200, 250, 315, 400, 500, 630, 800, 1000, 1250, 1600];
const MIN: u16 = 200;
const MAX: u16 = 8000;
impl<const L: u16, const H: u16> LogFrequency<L, H> {
const BASE_VALUES: [u16; 10] = [50, 63, 80, 100, 125, 160, 200, 250, 315, 400];
const MIN: u16 = L;
const MAX: u16 = H;
}

impl DiscreteValues<u16, 0> for LogFrequency {
impl<const L: u16, const H: u16> DiscreteValues<u16, 0> for LogFrequency<L, H> {
fn values() -> Vec<u16> {
let mut factor = 1;
let mut v = Vec::new();
Expand All @@ -63,7 +63,7 @@ impl DiscreteValues<u16, 0> for LogFrequency {
}
}

impl JsonSchema for LogFrequency {
impl<const L: u16, const H: u16> JsonSchema for LogFrequency<L, H> {
fn schema_name() -> String {
type_name_pretty::<Self>().into()
}
Expand All @@ -73,14 +73,14 @@ impl JsonSchema for LogFrequency {
}
}

impl Serialize for LogFrequency {
impl<const L: u16, const H: u16> Serialize for LogFrequency<L, H> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer {
Self::format(self.0).serialize(serializer)
}
}

impl<'de> Deserialize<'de> for LogFrequency {
impl<'de, const L: u16, const H: u16> Deserialize<'de> for LogFrequency<L, H> {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: serde::Deserializer<'de> {
let value: Value = Deserialize::deserialize(deserializer)?;
Expand All @@ -98,29 +98,29 @@ impl<'de> Deserialize<'de> for LogFrequency {
}
}

impl From<Parameter> for LogFrequency {
impl<const L: u16, const H: u16> From<Parameter> for LogFrequency<L, H> {
fn from(parameter: Parameter) -> Self {
Self(Self::value_from(parameter))
}
}

impl Into<Parameter> for LogFrequency {
impl<const L: u16, const H: u16> Into<Parameter> for LogFrequency<L, H> {
fn into(self) -> Parameter {
Self::into_parameter(self.0)
}
}

#[derive(Serialize, Deserialize, Debug, JsonSchema, Copy, Clone)]
pub enum LogFrequencyOrByPass { // 0-17 (200-8000Hz, BYPASS)
Frequency(LogFrequency),
pub enum LogFrequencyOrByPass<const MIN: u16, const MAX: u16> { // 0-17 (200-8000Hz, BYPASS)
Frequency(LogFrequency<MIN, MAX>),
ByPass
}

impl From<Parameter> for LogFrequencyOrByPass {
impl<const L: u16, const H: u16> From<Parameter> for LogFrequencyOrByPass<L, H> {
fn from(value: Parameter) -> Self {
let values = LogFrequency::values();
if value.0 < LogFrequency::OFFSET || value.0 > LogFrequency::OFFSET + values.len() as i16 {
panic!("Parameter out of range: {} (expected {}-{})", value.0, LogFrequency::OFFSET, LogFrequency::OFFSET + values.len() as i16)
let values = LogFrequency::<L, H>::values();
if value.0 < LogFrequency::<L, H>::OFFSET || value.0 > LogFrequency::<L, H>::OFFSET + values.len() as i16 {
panic!("Parameter out of range: {} (expected {}-{})", value.0, LogFrequency::<L, H>::OFFSET, LogFrequency::<L, H>::OFFSET + values.len() as i16)
} else if value.0 == values.len() as i16 {
Self::ByPass
} else {
Expand All @@ -129,11 +129,11 @@ impl From<Parameter> for LogFrequencyOrByPass {
}
}

impl Into<Parameter> for LogFrequencyOrByPass {
impl<const L: u16, const H: u16> Into<Parameter> for LogFrequencyOrByPass<L, H> {
fn into(self) -> Parameter {
match self {
Self::Frequency(f) => f.into(),
Self::ByPass => Parameter(LogFrequency::OFFSET + LogFrequency::values().len() as i16)
Self::ByPass => Parameter(LogFrequency::<L, H>::OFFSET + LogFrequency::<L, H>::values().len() as i16)
}
}
}
Expand Down
44 changes: 44 additions & 0 deletions src/roland/types/effects/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use super::numeric::Parameter;
use crate::json::{serialize_default_terminated_array, validation::merge_all_fixed};
use crate::json::validation::validate_boxed_array;
use schemars::JsonSchema;
use validator::{Validate, ValidationErrors};

pub mod discrete;
pub mod parameters;
pub mod chorus;
pub mod reverb;

trait Parameters<const N: usize> : Validate + From<[Parameter; N]> {
fn parameters(&self) -> [Parameter; N];
}

#[derive(Serialize, Deserialize, Debug, JsonSchema)]
pub struct UnusedParameters<const N: usize> {
#[serde(deserialize_with = "serialize_default_terminated_array::deserialize")]
#[serde(serialize_with = "serialize_default_terminated_array::serialize")]
#[schemars(with = "serialize_default_terminated_array::DefaultTerminatedArraySchema::<Parameter, {N}>")]
unused: Box<[Parameter; N]>
}

impl<const N: usize> From<[Parameter; N]> for UnusedParameters<N> {
fn from(value: [Parameter; N]) -> Self {
Self {
unused: Box::new(value)
}
}
}

impl<const N: usize> Parameters<N> for UnusedParameters<N> {
fn parameters(&self) -> [Parameter; N] {
*self.unused
}
}

impl<const N: usize> Validate for UnusedParameters<N> {
fn validate(&self) -> Result<(), ValidationErrors> {
let mut r = Ok(());
r = merge_all_fixed(r, "unused", validate_boxed_array(&self.unused));
r
}
}
Loading

0 comments on commit a6e12bf

Please sign in to comment.