Feat: begin slight refactoring

This commit is contained in:
2026-02-01 12:38:48 +01:00
parent 5b4a6ddd14
commit c356aebfde
39 changed files with 4699 additions and 3168 deletions

View File

@@ -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)),