Feat: begin slight refactoring
Some checks failed
Deploy Website / deploy (push) Failing after 4m46s
Some checks failed
Deploy Website / deploy (push) Failing after 4m46s
This commit is contained in:
@@ -10,7 +10,9 @@ use std::time::Duration;
|
||||
use thread_priority::{set_current_thread_priority, ThreadPriority};
|
||||
|
||||
use super::LinkState;
|
||||
use crate::model::{CcMemory, Dictionary, ExecutionTrace, Rng, ScriptEngine, StepContext, Value, Variables};
|
||||
use crate::model::{
|
||||
CcAccess, Dictionary, ExecutionTrace, Rng, ScriptEngine, StepContext, Value, Variables,
|
||||
};
|
||||
use crate::model::{LaunchQuantization, SyncMode, MAX_BANKS, MAX_PATTERNS};
|
||||
use crate::state::LiveKeyState;
|
||||
|
||||
@@ -55,16 +57,50 @@ pub enum AudioCommand {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum MidiCommand {
|
||||
NoteOn { device: u8, channel: u8, note: u8, velocity: u8 },
|
||||
NoteOff { device: u8, channel: u8, note: u8 },
|
||||
CC { device: u8, channel: u8, cc: u8, value: u8 },
|
||||
PitchBend { device: u8, channel: u8, value: u16 },
|
||||
Pressure { device: u8, channel: u8, value: u8 },
|
||||
ProgramChange { device: u8, channel: u8, program: u8 },
|
||||
Clock { device: u8 },
|
||||
Start { device: u8 },
|
||||
Stop { device: u8 },
|
||||
Continue { device: u8 },
|
||||
NoteOn {
|
||||
device: u8,
|
||||
channel: u8,
|
||||
note: u8,
|
||||
velocity: u8,
|
||||
},
|
||||
NoteOff {
|
||||
device: u8,
|
||||
channel: u8,
|
||||
note: u8,
|
||||
},
|
||||
CC {
|
||||
device: u8,
|
||||
channel: u8,
|
||||
cc: u8,
|
||||
value: u8,
|
||||
},
|
||||
PitchBend {
|
||||
device: u8,
|
||||
channel: u8,
|
||||
value: u16,
|
||||
},
|
||||
Pressure {
|
||||
device: u8,
|
||||
channel: u8,
|
||||
value: u8,
|
||||
},
|
||||
ProgramChange {
|
||||
device: u8,
|
||||
channel: u8,
|
||||
program: u8,
|
||||
},
|
||||
Clock {
|
||||
device: u8,
|
||||
},
|
||||
Start {
|
||||
device: u8,
|
||||
},
|
||||
Stop {
|
||||
device: u8,
|
||||
},
|
||||
Continue {
|
||||
device: u8,
|
||||
},
|
||||
}
|
||||
|
||||
pub enum SeqCommand {
|
||||
@@ -233,7 +269,7 @@ pub struct SequencerConfig {
|
||||
pub audio_sample_pos: Arc<AtomicU64>,
|
||||
pub sample_rate: Arc<std::sync::atomic::AtomicU32>,
|
||||
pub lookahead_ms: Arc<std::sync::atomic::AtomicU32>,
|
||||
pub cc_memory: Option<CcMemory>,
|
||||
pub cc_access: Option<Arc<dyn CcAccess>>,
|
||||
#[cfg(feature = "desktop")]
|
||||
pub mouse_x: Arc<AtomicU32>,
|
||||
#[cfg(feature = "desktop")]
|
||||
@@ -253,7 +289,11 @@ pub fn spawn_sequencer(
|
||||
live_keys: Arc<LiveKeyState>,
|
||||
nudge_us: Arc<AtomicI64>,
|
||||
config: SequencerConfig,
|
||||
) -> (SequencerHandle, Receiver<AudioCommand>, Receiver<MidiCommand>) {
|
||||
) -> (
|
||||
SequencerHandle,
|
||||
Receiver<AudioCommand>,
|
||||
Receiver<MidiCommand>,
|
||||
) {
|
||||
let (cmd_tx, cmd_rx) = bounded::<SeqCommand>(64);
|
||||
let (audio_tx, audio_rx) = bounded::<AudioCommand>(256);
|
||||
let (midi_tx, midi_rx) = bounded::<MidiCommand>(256);
|
||||
@@ -291,7 +331,7 @@ pub fn spawn_sequencer(
|
||||
config.audio_sample_pos,
|
||||
config.sample_rate,
|
||||
config.lookahead_ms,
|
||||
config.cc_memory,
|
||||
config.cc_access,
|
||||
#[cfg(feature = "desktop")]
|
||||
mouse_x,
|
||||
#[cfg(feature = "desktop")]
|
||||
@@ -510,12 +550,17 @@ pub(crate) struct SequencerState {
|
||||
speed_overrides: HashMap<(usize, usize), f64>,
|
||||
key_cache: KeyCache,
|
||||
buf_audio_commands: Vec<TimestampedCommand>,
|
||||
cc_memory: Option<CcMemory>,
|
||||
cc_access: Option<Arc<dyn CcAccess>>,
|
||||
active_notes: HashMap<(u8, u8, u8), ActiveNote>,
|
||||
}
|
||||
|
||||
impl SequencerState {
|
||||
pub fn new(variables: Variables, dict: Dictionary, rng: Rng, cc_memory: Option<CcMemory>) -> Self {
|
||||
pub fn new(
|
||||
variables: Variables,
|
||||
dict: Dictionary,
|
||||
rng: Rng,
|
||||
cc_access: Option<Arc<dyn CcAccess>>,
|
||||
) -> Self {
|
||||
let script_engine = ScriptEngine::new(Arc::clone(&variables), dict, rng);
|
||||
Self {
|
||||
audio_state: AudioState::new(),
|
||||
@@ -529,7 +574,7 @@ impl SequencerState {
|
||||
speed_overrides: HashMap::new(),
|
||||
key_cache: KeyCache::new(),
|
||||
buf_audio_commands: Vec::new(),
|
||||
cc_memory,
|
||||
cc_access,
|
||||
active_notes: HashMap::new(),
|
||||
}
|
||||
}
|
||||
@@ -781,7 +826,7 @@ impl SequencerState {
|
||||
speed: speed_mult,
|
||||
fill,
|
||||
nudge_secs,
|
||||
cc_memory: self.cc_memory.clone(),
|
||||
cc_access: self.cc_access.clone(),
|
||||
#[cfg(feature = "desktop")]
|
||||
mouse_x,
|
||||
#[cfg(feature = "desktop")]
|
||||
@@ -943,7 +988,7 @@ fn sequencer_loop(
|
||||
audio_sample_pos: Arc<AtomicU64>,
|
||||
sample_rate: Arc<std::sync::atomic::AtomicU32>,
|
||||
lookahead_ms: Arc<std::sync::atomic::AtomicU32>,
|
||||
cc_memory: Option<CcMemory>,
|
||||
cc_access: Option<Arc<dyn CcAccess>>,
|
||||
#[cfg(feature = "desktop")] mouse_x: Arc<AtomicU32>,
|
||||
#[cfg(feature = "desktop")] mouse_y: Arc<AtomicU32>,
|
||||
#[cfg(feature = "desktop")] mouse_down: Arc<AtomicU32>,
|
||||
@@ -952,7 +997,7 @@ fn sequencer_loop(
|
||||
|
||||
let _ = set_current_thread_priority(ThreadPriority::Max);
|
||||
|
||||
let mut seq_state = SequencerState::new(variables, dict, rng, cc_memory);
|
||||
let mut seq_state = SequencerState::new(variables, dict, rng, cc_access);
|
||||
|
||||
loop {
|
||||
let mut commands = Vec::new();
|
||||
@@ -1002,8 +1047,15 @@ fn sequencer_loop(
|
||||
if let Some((midi_cmd, dur)) = parse_midi_command(&tsc.cmd) {
|
||||
match midi_tx.load().try_send(midi_cmd.clone()) {
|
||||
Ok(()) => {
|
||||
if let (MidiCommand::NoteOn { device, channel, note, .. }, Some(dur_secs)) =
|
||||
(&midi_cmd, dur)
|
||||
if let (
|
||||
MidiCommand::NoteOn {
|
||||
device,
|
||||
channel,
|
||||
note,
|
||||
..
|
||||
},
|
||||
Some(dur_secs),
|
||||
) = (&midi_cmd, dur)
|
||||
{
|
||||
let dur_us = (dur_secs * 1_000_000.0) as i64;
|
||||
seq_state.active_notes.insert(
|
||||
@@ -1037,28 +1089,41 @@ fn sequencer_loop(
|
||||
|
||||
if output.flush_midi_notes {
|
||||
for ((device, channel, note), _) in seq_state.active_notes.drain() {
|
||||
let _ = midi_tx.load().try_send(MidiCommand::NoteOff { device, channel, note });
|
||||
let _ = midi_tx.load().try_send(MidiCommand::NoteOff {
|
||||
device,
|
||||
channel,
|
||||
note,
|
||||
});
|
||||
}
|
||||
// Send MIDI panic (CC 123 = All Notes Off) on all 16 channels for all devices
|
||||
for dev in 0..4u8 {
|
||||
for chan in 0..16u8 {
|
||||
let _ = midi_tx
|
||||
.load()
|
||||
.try_send(MidiCommand::CC { device: dev, channel: chan, cc: 123, value: 0 });
|
||||
let _ = midi_tx.load().try_send(MidiCommand::CC {
|
||||
device: dev,
|
||||
channel: chan,
|
||||
cc: 123,
|
||||
value: 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
seq_state.active_notes.retain(|&(device, channel, note), active| {
|
||||
let should_release = current_time_us >= active.off_time_us;
|
||||
let timed_out = (current_time_us - active.start_time_us) > MAX_NOTE_DURATION_US;
|
||||
seq_state
|
||||
.active_notes
|
||||
.retain(|&(device, channel, note), active| {
|
||||
let should_release = current_time_us >= active.off_time_us;
|
||||
let timed_out = (current_time_us - active.start_time_us) > MAX_NOTE_DURATION_US;
|
||||
|
||||
if should_release || timed_out {
|
||||
let _ = midi_tx.load().try_send(MidiCommand::NoteOff { device, channel, note });
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
if should_release || timed_out {
|
||||
let _ = midi_tx.load().try_send(MidiCommand::NoteOff {
|
||||
device,
|
||||
channel,
|
||||
note,
|
||||
});
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(t) = output.new_tempo {
|
||||
@@ -1081,7 +1146,8 @@ fn parse_midi_command(cmd: &str) -> Option<(MidiCommand, Option<f64>)> {
|
||||
}
|
||||
|
||||
let find_param = |key: &str| -> Option<&str> {
|
||||
parts.iter()
|
||||
parts
|
||||
.iter()
|
||||
.position(|&s| s == key)
|
||||
.and_then(|i| parts.get(i + 1).copied())
|
||||
};
|
||||
@@ -1124,19 +1190,40 @@ fn parse_midi_command(cmd: &str) -> Option<(MidiCommand, Option<f64>)> {
|
||||
// /midi/bend/<value>/chan/<chan>/dev/<dev>
|
||||
let value: u16 = parts.get(2)?.parse().ok()?;
|
||||
let chan: u8 = find_param("chan")?.parse().ok()?;
|
||||
Some((MidiCommand::PitchBend { device, channel: chan, value }, None))
|
||||
Some((
|
||||
MidiCommand::PitchBend {
|
||||
device,
|
||||
channel: chan,
|
||||
value,
|
||||
},
|
||||
None,
|
||||
))
|
||||
}
|
||||
"pressure" => {
|
||||
// /midi/pressure/<value>/chan/<chan>/dev/<dev>
|
||||
let value: u8 = parts.get(2)?.parse().ok()?;
|
||||
let chan: u8 = find_param("chan")?.parse().ok()?;
|
||||
Some((MidiCommand::Pressure { device, channel: chan, value }, None))
|
||||
Some((
|
||||
MidiCommand::Pressure {
|
||||
device,
|
||||
channel: chan,
|
||||
value,
|
||||
},
|
||||
None,
|
||||
))
|
||||
}
|
||||
"program" => {
|
||||
// /midi/program/<value>/chan/<chan>/dev/<dev>
|
||||
let program: u8 = parts.get(2)?.parse().ok()?;
|
||||
let chan: u8 = find_param("chan")?.parse().ok()?;
|
||||
Some((MidiCommand::ProgramChange { device, channel: chan, program }, None))
|
||||
Some((
|
||||
MidiCommand::ProgramChange {
|
||||
device,
|
||||
channel: chan,
|
||||
program,
|
||||
},
|
||||
None,
|
||||
))
|
||||
}
|
||||
"clock" => Some((MidiCommand::Clock { device }, None)),
|
||||
"start" => Some((MidiCommand::Start { device }, None)),
|
||||
|
||||
Reference in New Issue
Block a user