pub const MAX_CC_CONTROLLER: usize = 130;
pub mod controller {
pub const AFTERTOUCH: u8 = 128;
pub const PITCH_BEND: u8 = 129;
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct MidiCcConfig {
enabled: [bool; MAX_CC_CONTROLLER],
}
impl MidiCcConfig {
pub const SYNTH_BASIC: Self = Self::new()
.with_pitch_bend()
.with_mod_wheel()
.with_cc(7) .with_cc(11) .with_cc(64);
pub const SYNTH_FULL: Self = Self::new()
.with_pitch_bend()
.with_aftertouch()
.with_mod_wheel()
.with_cc(2) .with_cc(7) .with_cc(10) .with_cc(11) .with_cc(64);
pub const EFFECT_BASIC: Self = Self::new()
.with_mod_wheel()
.with_cc(11);
#[inline]
pub const fn new() -> Self {
Self {
enabled: [false; MAX_CC_CONTROLLER],
}
}
#[inline]
pub const fn with_pitch_bend(mut self) -> Self {
self.enabled[controller::PITCH_BEND as usize] = true;
self
}
#[inline]
pub const fn with_aftertouch(mut self) -> Self {
self.enabled[controller::AFTERTOUCH as usize] = true;
self
}
#[inline]
pub const fn with_mod_wheel(mut self) -> Self {
self.enabled[1] = true;
self
}
#[inline]
pub const fn with_cc(mut self, cc: u8) -> Self {
assert!(cc < 128, "CC number must be 0-127");
self.enabled[cc as usize] = true;
self
}
#[inline]
pub fn with_ccs(mut self, ccs: &[u8]) -> Self {
for &cc in ccs {
assert!(cc < 128, "CC number must be 0-127, got {}", cc);
self.enabled[cc as usize] = true;
}
self
}
#[inline]
pub const fn with_all_ccs(mut self) -> Self {
let mut cc = 0;
while cc < 128 {
self.enabled[cc] = true;
cc += 1;
}
self
}
#[inline]
pub const fn is_enabled(&self, controller: u8) -> bool {
let idx = controller as usize;
if idx < MAX_CC_CONTROLLER {
self.enabled[idx]
} else {
false
}
}
#[inline]
pub const fn has_pitch_bend(&self) -> bool {
self.enabled[controller::PITCH_BEND as usize]
}
#[inline]
pub const fn has_aftertouch(&self) -> bool {
self.enabled[controller::AFTERTOUCH as usize]
}
#[inline]
pub const fn has_mod_wheel(&self) -> bool {
self.enabled[1]
}
#[inline]
pub const fn enabled_count(&self) -> usize {
let mut count = 0;
let mut i = 0;
while i < MAX_CC_CONTROLLER {
if self.enabled[i] {
count += 1;
}
i += 1;
}
count
}
#[inline]
pub const fn enabled_flags(&self) -> &[bool; MAX_CC_CONTROLLER] {
&self.enabled
}
}
impl Default for MidiCcConfig {
fn default() -> Self {
Self::new()
}
}
impl core::fmt::Debug for MidiCcConfig {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let enabled: Vec<u8> = self
.enabled
.iter()
.enumerate()
.filter_map(|(i, &e)| if e { Some(i as u8) } else { None })
.collect();
f.debug_struct("MidiCcConfig")
.field("enabled_controllers", &enabled)
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_const_builder() {
const CONFIG: MidiCcConfig = MidiCcConfig::new()
.with_pitch_bend()
.with_mod_wheel()
.with_cc(7)
.with_cc(64);
assert!(CONFIG.has_pitch_bend());
assert!(CONFIG.has_mod_wheel());
assert!(CONFIG.is_enabled(7));
assert!(CONFIG.is_enabled(64));
assert!(!CONFIG.has_aftertouch());
assert!(!CONFIG.is_enabled(10));
assert_eq!(CONFIG.enabled_count(), 4);
}
#[test]
fn test_with_ccs_runtime() {
let config = MidiCcConfig::new()
.with_pitch_bend()
.with_ccs(&[7, 10, 11, 64]);
assert!(config.has_pitch_bend());
assert!(config.is_enabled(7));
assert!(config.is_enabled(10));
assert!(config.is_enabled(11));
assert!(config.is_enabled(64));
assert_eq!(config.enabled_count(), 5);
}
#[test]
fn test_default() {
let config = MidiCcConfig::default();
assert_eq!(config.enabled_count(), 0);
assert!(!config.has_pitch_bend());
}
#[test]
fn test_with_all_ccs() {
let config = MidiCcConfig::new().with_all_ccs();
assert_eq!(config.enabled_count(), 128);
assert!(config.is_enabled(0));
assert!(config.is_enabled(127));
assert!(!config.has_pitch_bend());
assert!(!config.has_aftertouch());
}
#[test]
fn test_synth_basic_preset() {
const CONFIG: MidiCcConfig = MidiCcConfig::SYNTH_BASIC;
assert!(CONFIG.has_pitch_bend());
assert!(CONFIG.has_mod_wheel());
assert!(CONFIG.is_enabled(7)); assert!(CONFIG.is_enabled(11)); assert!(CONFIG.is_enabled(64)); assert!(!CONFIG.has_aftertouch());
assert_eq!(CONFIG.enabled_count(), 5);
}
#[test]
fn test_synth_full_preset() {
const CONFIG: MidiCcConfig = MidiCcConfig::SYNTH_FULL;
assert!(CONFIG.has_pitch_bend());
assert!(CONFIG.has_aftertouch());
assert!(CONFIG.has_mod_wheel());
assert!(CONFIG.is_enabled(2)); assert!(CONFIG.is_enabled(7)); assert!(CONFIG.is_enabled(10)); assert!(CONFIG.is_enabled(11)); assert!(CONFIG.is_enabled(64)); assert_eq!(CONFIG.enabled_count(), 8);
}
#[test]
fn test_effect_basic_preset() {
const CONFIG: MidiCcConfig = MidiCcConfig::EFFECT_BASIC;
assert!(CONFIG.has_mod_wheel());
assert!(CONFIG.is_enabled(11)); assert!(!CONFIG.has_pitch_bend());
assert_eq!(CONFIG.enabled_count(), 2);
}
#[test]
#[should_panic(expected = "CC number must be 0-127")]
fn test_invalid_cc_panics() {
let _ = MidiCcConfig::new().with_cc(130);
}
#[test]
#[should_panic(expected = "CC number must be 0-127")]
fn test_invalid_ccs_panics() {
let _ = MidiCcConfig::new().with_ccs(&[7, 130, 64]);
}
}