use crate::error::Error;
use crate::raw::GeneratorType;
use super::super::utils::Reader;
use crate::riff::{Chunk, ChunkId, ScratchReader};
use std::convert::{TryFrom, TryInto};
use std::io::{Read, Seek};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum GeneralPalette {
NoController,
NoteOnVelocity,
NoteOnKeyNumber,
PolyPressure,
ChannelPressure,
PitchWheel,
PitchWheelSensitivity,
Link,
Unknown(u8),
}
impl From<u8> for GeneralPalette {
fn from(ty: u8) -> Self {
match ty {
0 => Self::NoController,
2 => Self::NoteOnVelocity,
3 => Self::NoteOnKeyNumber,
10 => Self::PolyPressure,
13 => Self::ChannelPressure,
14 => Self::PitchWheel,
16 => Self::PitchWheelSensitivity,
127 => Self::Link,
v => Self::Unknown(v),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ControllerPalette {
General(GeneralPalette),
Midi(u8),
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SourceDirection {
Positive,
Negative,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SourcePolarity {
Unipolar,
Bipolar,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum SourceType {
Linear,
Concave,
Convex,
Switch,
Unknown(u8),
}
impl From<u8> for SourceType {
fn from(ty: u8) -> Self {
match ty {
0 => Self::Linear,
1 => Self::Concave,
2 => Self::Convex,
3 => Self::Switch,
v => Self::Unknown(v),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct ModulatorSource {
pub index: u8,
pub controller_palette: ControllerPalette,
pub direction: SourceDirection,
pub polarity: SourcePolarity,
pub ty: SourceType,
}
impl ModulatorSource {
#[inline]
pub fn is_linear(&self) -> bool {
self.ty == SourceType::Linear
}
#[inline]
pub fn is_concave(&self) -> bool {
self.ty == SourceType::Concave
}
#[inline]
pub fn is_convex(&self) -> bool {
self.ty == SourceType::Convex
}
#[inline]
pub fn is_switch(&self) -> bool {
self.ty == SourceType::Switch
}
#[inline]
pub fn is_unipolar(&self) -> bool {
self.polarity == SourcePolarity::Unipolar
}
#[inline]
pub fn is_bipolar(&self) -> bool {
self.polarity == SourcePolarity::Bipolar
}
#[inline]
pub fn is_positive(&self) -> bool {
self.direction == SourceDirection::Positive
}
#[inline]
pub fn is_negative(&self) -> bool {
self.direction == SourceDirection::Negative
}
#[inline]
pub fn is_cc(&self) -> bool {
std::mem::discriminant(&self.controller_palette)
== std::mem::discriminant(&ControllerPalette::Midi(0))
}
#[inline]
pub fn is_gc(&self) -> bool {
std::mem::discriminant(&self.controller_palette)
== std::mem::discriminant(&ControllerPalette::General(GeneralPalette::NoController))
}
}
impl From<u16> for ModulatorSource {
fn from(src: u16) -> Self {
let index: u8 = (src & 0b1111111)
.try_into()
.expect("Index is longer than 7 bits!");
let controller_palette = if src & 1 << 7 != 0 {
ControllerPalette::Midi(index)
} else {
ControllerPalette::General(index.into())
};
let direction = if src & 1 << 8 != 0 {
SourceDirection::Negative
} else {
SourceDirection::Positive
};
let polarity = if src & 1 << 9 != 0 {
SourcePolarity::Bipolar
} else {
SourcePolarity::Unipolar
};
let ty = src >> 10;
let ty: u8 = (ty & 0b111111)
.try_into()
.expect("Mod source type is longer than 6 bits!");
let ty: SourceType = ty.into();
Self {
index,
controller_palette,
direction,
polarity,
ty,
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ModulatorTransform {
Linear,
Absolute,
}
impl TryFrom<u16> for ModulatorTransform {
type Error = Error;
fn try_from(v: u16) -> Result<Self, Self::Error> {
match v {
0 => Ok(Self::Linear),
2 => Ok(Self::Absolute),
v => Err(Error::UnknownModulatorTransform(v)),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Modulator {
pub src: ModulatorSource,
pub dest: GeneratorType,
pub amount: i16,
pub amt_src: ModulatorSource,
pub transform: ModulatorTransform,
}
impl Modulator {
pub(crate) fn read(reader: &mut Reader, terminal: bool) -> Result<Self, Error> {
let mut src: u16 = reader.read_u16()?;
let mut dest: u16 = reader.read_u16()?;
let mut amount: i16 = reader.read_i16()?;
let mut amt_src: u16 = reader.read_u16()?;
let mut transform: u16 = reader.read_u16()?;
if terminal {
src = 0;
dest = 0;
amount = 0;
amt_src = 0;
transform = 0;
}
Ok(Self {
src: src.into(),
dest: dest.try_into()?,
amount,
amt_src: amt_src.into(),
transform: transform.try_into()?,
})
}
pub(crate) fn read_all(
pmod: &Chunk,
file: &mut ScratchReader<impl Read + Seek>,
) -> Result<Vec<Self>, Error> {
assert!(pmod.id() == ChunkId::pmod || pmod.id() == ChunkId::imod);
let size = pmod.len();
if size % 10 != 0 || size == 0 {
Err(Error::InvalidModulatorChunkSize(size))
} else {
let amount = size / 10;
let data = pmod.read_contents(file)?;
let mut reader = Reader::new(data);
(0..amount)
.map(|id| Self::read(&mut reader, id == amount - 1))
.collect()
}
}
}
pub mod default_modulators {
use super::*;
use crate::raw::GeneratorType;
use SourceDirection::*;
use SourcePolarity::*;
use SourceType::*;
const NO_CONTROLLER_SRC: ModulatorSource = ModulatorSource {
index: 0,
controller_palette: ControllerPalette::General(GeneralPalette::NoController),
direction: Positive,
polarity: Unipolar,
ty: Linear,
};
pub static DEFAULT_VEL2ATT_MOD: Modulator = Modulator {
dest: GeneratorType::InitialAttenuation,
amount: 960,
src: ModulatorSource {
index: 2,
controller_palette: ControllerPalette::General(GeneralPalette::NoteOnVelocity),
direction: Negative,
polarity: Unipolar,
ty: Concave,
},
amt_src: NO_CONTROLLER_SRC,
transform: ModulatorTransform::Linear,
};
pub static DEFAULT_VEL2FILTER_MOD: Modulator = Modulator {
dest: GeneratorType::InitialFilterFc,
amount: -2400,
src: ModulatorSource {
index: 2,
controller_palette: ControllerPalette::General(GeneralPalette::NoteOnVelocity),
direction: Negative,
polarity: Unipolar,
ty: Linear,
},
amt_src: NO_CONTROLLER_SRC,
transform: ModulatorTransform::Linear,
};
pub static DEFAULT_AT2VIBLFO_MOD: Modulator = Modulator {
dest: GeneratorType::VibLfoToPitch,
amount: 50,
src: ModulatorSource {
index: 13,
controller_palette: ControllerPalette::General(GeneralPalette::ChannelPressure),
direction: Positive,
polarity: Unipolar,
ty: Linear,
},
amt_src: NO_CONTROLLER_SRC,
transform: ModulatorTransform::Linear,
};
pub static DEFAULT_MOD2VIBLFO_MOD: Modulator = Modulator {
dest: GeneratorType::VibLfoToPitch,
amount: 50,
src: ModulatorSource {
index: 1,
controller_palette: ControllerPalette::Midi(1),
direction: Positive,
polarity: Unipolar,
ty: Linear,
},
amt_src: NO_CONTROLLER_SRC,
transform: ModulatorTransform::Linear,
};
pub static DEFAULT_ATT_MOD: Modulator = Modulator {
dest: GeneratorType::InitialAttenuation,
amount: 960,
src: ModulatorSource {
index: 7,
controller_palette: ControllerPalette::Midi(7),
direction: Negative,
polarity: Unipolar,
ty: Concave,
},
amt_src: NO_CONTROLLER_SRC,
transform: ModulatorTransform::Linear,
};
pub static DEFAULT_PAN_MOD: Modulator = Modulator {
dest: GeneratorType::Pan,
amount: 500,
src: ModulatorSource {
index: 10,
controller_palette: ControllerPalette::Midi(10),
direction: Positive,
polarity: Bipolar,
ty: Linear,
},
amt_src: NO_CONTROLLER_SRC,
transform: ModulatorTransform::Linear,
};
pub static DEFAULT_EXPR_MOD: Modulator = Modulator {
dest: GeneratorType::InitialAttenuation,
amount: 960,
src: ModulatorSource {
index: 11,
controller_palette: ControllerPalette::Midi(11),
direction: Negative,
polarity: Unipolar,
ty: Concave,
},
amt_src: NO_CONTROLLER_SRC,
transform: ModulatorTransform::Linear,
};
pub static DEFAULT_REVERB_MOD: Modulator = Modulator {
dest: GeneratorType::ReverbEffectsSend,
amount: 200,
src: ModulatorSource {
index: 91,
controller_palette: ControllerPalette::Midi(91),
direction: Positive,
polarity: Unipolar,
ty: Linear,
},
amt_src: NO_CONTROLLER_SRC,
transform: ModulatorTransform::Linear,
};
pub static DEFAULT_CHORUS_MOD: Modulator = Modulator {
dest: GeneratorType::ChorusEffectsSend,
amount: 200,
src: ModulatorSource {
index: 93,
controller_palette: ControllerPalette::Midi(93),
direction: Positive,
polarity: Unipolar,
ty: Linear,
},
amt_src: NO_CONTROLLER_SRC,
transform: ModulatorTransform::Linear,
};
pub const fn default_pitch_bend_mod(dest: GeneratorType) -> Modulator {
Modulator {
dest,
amount: 12700,
src: ModulatorSource {
index: 14,
controller_palette: ControllerPalette::General(GeneralPalette::PitchWheel),
direction: Positive,
polarity: Bipolar,
ty: Linear,
},
amt_src: ModulatorSource {
index: 16,
controller_palette: ControllerPalette::General(
GeneralPalette::PitchWheelSensitivity,
),
direction: Positive,
polarity: Unipolar,
ty: Linear,
},
transform: ModulatorTransform::Linear,
}
}
}