use std::{
collections::HashMap,
ops::{Deref, DerefMut},
sync::{mpsc::Sender, Arc, Mutex, MutexGuard},
};
use tune::{
midi::ChannelMessageType,
pitch::Pitch,
scala::{Kbm, KbmRoot, Scl},
tuning::Tuning,
};
use tune_cli::shared::midi::MultiChannelOffset;
use crate::{
control::{LiveParameter, LiveParameterMapper, LiveParameterStorage, ParameterValue},
model::{Event, Location, SourceId},
};
pub struct PianoEngine {
model: Mutex<PianoEngineModel>,
}
#[derive(Clone)]
pub struct PianoEngineSnapshot {
pub curr_backend: usize,
pub tuning_mode: TuningMode,
pub kbm: Kbm,
pub pressed_keys: HashMap<SourceId, PressedKey>,
pub mapper: LiveParameterMapper,
pub storage: LiveParameterStorage,
}
#[derive(Clone, Copy, Debug)]
pub enum TuningMode {
Fixed,
Continuous,
}
impl TuningMode {
fn toggle(&mut self) {
*self = match *self {
TuningMode::Fixed => TuningMode::Continuous,
TuningMode::Continuous => TuningMode::Fixed,
}
}
}
#[derive(Clone, Debug)]
pub struct PressedKey {
pub backend: usize,
pub pitch: Pitch,
}
struct PianoEngineModel {
snapshot: PianoEngineSnapshot,
backends: Vec<Box<dyn Backend<SourceId>>>,
scl: Scl,
storage_updates: Sender<LiveParameterStorage>,
}
impl Deref for PianoEngineModel {
type Target = PianoEngineSnapshot;
fn deref(&self) -> &Self::Target {
&self.snapshot
}
}
impl DerefMut for PianoEngineModel {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.snapshot
}
}
impl PianoEngine {
pub fn new(
scl: Scl,
kbm: Kbm,
backends: Vec<Box<dyn Backend<SourceId>>>,
program_number: u8,
mapper: LiveParameterMapper,
storage: LiveParameterStorage,
storage_updates: Sender<LiveParameterStorage>,
) -> (Arc<Self>, PianoEngineSnapshot) {
let snapshot = PianoEngineSnapshot {
curr_backend: 0,
tuning_mode: TuningMode::Fixed,
kbm,
pressed_keys: HashMap::new(),
storage,
mapper,
};
let mut model = PianoEngineModel {
snapshot: snapshot.clone(),
backends,
scl,
storage_updates,
};
model.retune();
model.set_program(program_number);
let engine = Self {
model: Mutex::new(model),
};
(Arc::new(engine), snapshot)
}
pub fn handle_midi_event(&self, message_type: ChannelMessageType, offset: MultiChannelOffset) {
self.lock_model().handle_midi_event(message_type, offset);
}
pub fn handle_event(&self, event: Event) {
self.lock_model().handle_event(event);
}
pub fn set_parameter(&self, parameter: LiveParameter, value: f64) {
self.lock_model().set_parameter(parameter, value);
}
pub fn set_key_pressure(&self, id: SourceId, value: f64) {
self.lock_model().set_key_pressure(id, value.as_u8());
}
pub fn toggle_tuning_mode(&self) {
let mut model = self.lock_model();
model.tuning_mode.toggle();
model.retune();
}
pub fn toggle_envelope_type(&self) {
let mut model = self.lock_model();
let backend = &mut model.backend_mut();
backend.toggle_envelope_type();
backend.send_status();
}
pub fn toggle_synth_mode(&self) {
let mut model = self.lock_model();
model.curr_backend += 1;
model.curr_backend %= model.backends.len();
model.backend_mut().send_status();
}
pub fn toggle_parameter(&self, parameter: LiveParameter) {
self.lock_model().toggle_parameter(parameter);
}
pub fn inc_program(&self) {
let mut model = self.lock_model();
let backend = &mut model.backend_mut();
backend.program_change(Box::new(|p| p.saturating_add(1)));
backend.send_status();
}
pub fn dec_program(&self) {
let mut model = self.lock_model();
let backend = &mut model.backend_mut();
backend.program_change(Box::new(|p| p.saturating_sub(1)));
backend.send_status();
}
pub fn change_ref_note_by(&self, delta: i32) {
let mut model = self.lock_model();
let mut kbm_root = model.kbm.kbm_root();
kbm_root = kbm_root.shift_ref_key_by(delta);
model.kbm.set_kbm_root(kbm_root);
model.retune();
}
pub fn change_root_offset_by(&self, delta: i32) {
let mut model = self.lock_model();
let mut kbm_root = model.kbm.kbm_root();
kbm_root.root_offset += delta;
model.kbm.set_kbm_root(kbm_root);
model.retune();
}
pub fn take_snapshot(&self, target: &mut PianoEngineSnapshot) {
target.clone_from(&self.lock_model())
}
fn lock_model(&self) -> MutexGuard<PianoEngineModel> {
self.model.lock().unwrap()
}
}
impl PianoEngineModel {
fn handle_midi_event(&mut self, message_type: ChannelMessageType, offset: MultiChannelOffset) {
match message_type {
ChannelMessageType::NoteOff { key, velocity }
| ChannelMessageType::NoteOn {
key,
velocity: velocity @ 0,
} => {
let piano_key = offset.get_piano_key(key);
self.handle_event(Event::Released(SourceId::Midi(piano_key), velocity));
}
ChannelMessageType::NoteOn { key, velocity } => {
let piano_key = offset.get_piano_key(key);
if let Some(degree) = self.kbm.scale_degree_of(piano_key) {
self.handle_event(Event::Pressed(
SourceId::Midi(piano_key),
Location::Degree(degree),
velocity,
));
}
}
ChannelMessageType::PolyphonicKeyPressure { key, pressure } => {
let piano_key = offset.get_piano_key(key);
self.set_key_pressure(SourceId::Midi(piano_key), pressure);
}
ChannelMessageType::ControlChange { controller, value } => {
for backend in &mut self.backends {
backend.control_change(controller, value);
}
for parameter in self.mapper.resolve_ccn(controller) {
self.set_parameter_without_backends_update(parameter, value.as_f64());
}
}
ChannelMessageType::ProgramChange { program } => {
self.set_program(program);
}
ChannelMessageType::ChannelPressure { pressure } => {
self.set_parameter(LiveParameter::ChannelPressure, pressure);
}
ChannelMessageType::PitchBendChange { value } => self.pitch_bend(value),
}
}
fn handle_event(&mut self, event: Event) {
match event {
Event::Pressed(id, location, velocity) => {
let (degree, pitch) = self.degree_and_pitch(location);
self.backend_mut().start(id, degree, pitch, velocity);
let backend = self.curr_backend;
self.pressed_keys.insert(id, PressedKey { backend, pitch });
}
Event::Moved(id, location) => {
if self.storage.is_active(LiveParameter::Legato) {
let (degree, pitch) = self.degree_and_pitch(location);
let (pressed_keys, backends) =
(&mut self.snapshot.pressed_keys, &mut self.backends);
if let Some(pressed_key) = pressed_keys.get_mut(&id) {
let backend = &mut backends[pressed_key.backend];
backend.update_pitch(id, degree, pitch, 100);
if backend.has_legato() {
pressed_key.pitch = pitch;
}
}
}
}
Event::Released(id, velocity) => {
for backend in &mut self.backends {
backend.stop(id, velocity);
}
self.pressed_keys.remove(&id);
}
}
}
fn degree_and_pitch(&self, location: Location) -> (i32, Pitch) {
let tuning = (&self.scl, self.kbm.kbm_root());
match location {
Location::Pitch(pitch) => {
let degree = tuning.find_by_pitch(pitch).approx_value;
match self.tuning_mode {
TuningMode::Continuous => (degree, pitch),
TuningMode::Fixed => (degree, tuning.pitch_of(degree)),
}
}
Location::Degree(degree) => (degree, tuning.pitch_of(degree)),
}
}
fn set_program(&mut self, program: u8) {
let backend = &mut self.backend_mut();
backend.program_change(Box::new(move |_| usize::from(program)));
backend.send_status();
}
fn toggle_parameter(&mut self, parameter: LiveParameter) {
if self.storage.is_active(parameter) {
self.set_parameter(parameter, 0.0);
} else {
self.set_parameter(parameter, 1.0);
}
}
fn set_parameter(&mut self, parameter: LiveParameter, value: impl ParameterValue) {
self.set_parameter_without_backends_update(parameter, value.as_f64());
let value = value.as_u8();
match parameter {
LiveParameter::ChannelPressure => {
for backend in &mut self.backends {
backend.channel_pressure(value);
}
}
_ => {
if let Some(ccn) = self.mapper.get_ccn(parameter) {
for backend in &mut self.backends {
backend.control_change(ccn, value);
}
}
}
}
}
fn set_parameter_without_backends_update(&mut self, parameter: LiveParameter, value: f64) {
self.storage.set_parameter(parameter, value);
self.storage_updates.send(self.storage).unwrap();
}
fn set_key_pressure(&mut self, id: SourceId, pressure: u8) {
for backend in &mut self.backends {
backend.update_pressure(id, pressure);
}
}
fn pitch_bend(&mut self, value: i16) {
self.storage
.set_parameter(LiveParameter::PitchBend, f64::from(value) / 8192.0);
self.storage_updates.send(self.storage).unwrap();
for backend in &mut self.backends {
backend.pitch_bend(value);
}
}
fn retune(&mut self) {
let kbm_root = self.kbm.kbm_root();
let tuning_mode = self.tuning_mode;
for backend in &mut self.backends {
match tuning_mode {
TuningMode::Fixed => backend.set_tuning((&self.scl, kbm_root)),
TuningMode::Continuous => backend.set_no_tuning(),
}
}
self.backend_mut().send_status();
}
}
pub trait Backend<S>: Send {
fn set_tuning(&mut self, tuning: (&Scl, KbmRoot));
fn set_no_tuning(&mut self);
fn send_status(&mut self);
fn start(&mut self, id: S, degree: i32, pitch: Pitch, velocity: u8);
fn update_pitch(&mut self, id: S, degree: i32, pitch: Pitch, velocity: u8);
fn update_pressure(&mut self, id: S, pressure: u8);
fn stop(&mut self, id: S, velocity: u8);
fn program_change(&mut self, update_fn: Box<dyn FnMut(usize) -> usize + Send>);
fn control_change(&mut self, controller: u8, value: u8);
fn channel_pressure(&mut self, pressure: u8);
fn pitch_bend(&mut self, value: i16);
fn toggle_envelope_type(&mut self);
fn has_legato(&self) -> bool;
}
impl PianoEngineModel {
pub fn backend_mut(&mut self) -> &mut dyn Backend<SourceId> {
let curr_backend = self.curr_backend;
self.backends[curr_backend].as_mut()
}
}
pub struct NoAudio<I> {
info_sender: Sender<I>,
}
impl<I> NoAudio<I> {
pub fn new(info_sender: Sender<I>) -> Self {
Self { info_sender }
}
}
impl<E, I: From<()> + Send> Backend<E> for NoAudio<I> {
fn set_tuning(&mut self, _tuning: (&Scl, KbmRoot)) {}
fn set_no_tuning(&mut self) {}
fn send_status(&mut self) {
self.info_sender.send(().into()).unwrap();
}
fn start(&mut self, _id: E, _degree: i32, _pitch: Pitch, _velocity: u8) {}
fn update_pitch(&mut self, _id: E, _degree: i32, _pitch: Pitch, _velocity: u8) {}
fn update_pressure(&mut self, _id: E, _pressure: u8) {}
fn stop(&mut self, _id: E, _velocity: u8) {}
fn program_change(&mut self, _update_fn: Box<dyn FnMut(usize) -> usize + Send>) {}
fn control_change(&mut self, _controller: u8, _value: u8) {}
fn channel_pressure(&mut self, _pressure: u8) {}
fn pitch_bend(&mut self, _value: i16) {}
fn toggle_envelope_type(&mut self) {}
fn has_legato(&self) -> bool {
true
}
}