Feat: tweak and fix from last night workshop
Some checks failed
Deploy Website / deploy (push) Failing after 4m46s

This commit is contained in:
2026-02-04 09:37:29 +01:00
parent 5579708f69
commit 2097997372
9 changed files with 151 additions and 65 deletions

View File

@@ -531,6 +531,7 @@ impl KeyCache {
pub(crate) struct SequencerState {
audio_state: AudioState,
pattern_cache: PatternCache,
pending_updates: HashMap<(usize, usize), PatternSnapshot>,
runs_counter: RunsCounter,
step_traces: Arc<StepTracesMap>,
event_count: usize,
@@ -559,6 +560,7 @@ impl SequencerState {
Self {
audio_state: AudioState::new(),
pattern_cache: PatternCache::new(),
pending_updates: HashMap::new(),
runs_counter: RunsCounter::new(),
step_traces: Arc::new(HashMap::new()),
event_count: 0,
@@ -596,7 +598,14 @@ impl SequencerState {
pattern,
data,
} => {
self.pattern_cache.set(bank, pattern, data);
let id = PatternId { bank, pattern };
let is_active = self.audio_state.active_patterns.contains_key(&id);
let has_cache = self.pattern_cache.get(bank, pattern).is_some();
if is_active && has_cache {
self.pending_updates.insert((bank, pattern), data);
} else {
self.pattern_cache.set(bank, pattern, data);
}
}
SeqCommand::PatternStart {
bank,
@@ -652,6 +661,10 @@ impl SequencerState {
}
}
SeqCommand::StopAll => {
// Flush pending updates so cache stays current for future launches
for ((bank, pattern), snapshot) in self.pending_updates.drain() {
self.pattern_cache.set(bank, pattern, snapshot);
}
self.audio_state.active_patterns.clear();
self.audio_state.pending_starts.clear();
self.audio_state.pending_stops.clear();
@@ -715,6 +728,10 @@ impl SequencerState {
Arc::make_mut(&mut self.step_traces).retain(|&(bank, pattern, _), _| {
bank != pending.id.bank || pattern != pending.id.pattern
});
let key = (pending.id.bank, pending.id.pattern);
if let Some(snapshot) = self.pending_updates.remove(&key) {
self.pattern_cache.set(key.0, key.1, snapshot);
}
}
self.audio_state.pending_starts.clear();
self.audio_state.prev_beat = -1.0;
@@ -773,6 +790,11 @@ impl SequencerState {
Arc::make_mut(&mut self.step_traces).retain(|&(bank, pattern, _), _| {
bank != pending.id.bank || pattern != pending.id.pattern
});
// Flush pending update so cache stays current for future launches
let key = (pending.id.bank, pending.id.pattern);
if let Some(snapshot) = self.pending_updates.remove(&key) {
self.pattern_cache.set(key.0, key.1, snapshot);
}
self.buf_stopped.push(pending.id);
}
}
@@ -921,6 +943,14 @@ impl SequencerState {
}
}
// Apply deferred updates for patterns that just completed an iteration
for completed_id in &self.buf_completed_iterations {
let key = (completed_id.bank, completed_id.pattern);
if let Some(snapshot) = self.pending_updates.remove(&key) {
self.pattern_cache.set(key.0, key.1, snapshot);
}
}
result
}
@@ -1872,8 +1902,9 @@ mod tests {
let ap = state.audio_state.active_patterns.get(&pid(0, 0)).unwrap();
assert_eq!(ap.step_index, 0);
// Update pattern to length 2 while running — step_index wraps via modulo
// beat=1.25: beat_int=5, prev=4, fires 1 step. step_index=0%2=0 fires, advances to 1
// Update pattern to length 2 while running — deferred until iteration boundary
// beat=1.25: update is deferred (pattern active), still length 4
// step_index=0 fires, advances to 1
state.tick(tick_with(
vec![SeqCommand::PatternUpdate {
bank: 0,
@@ -1883,10 +1914,21 @@ mod tests {
1.25,
));
let ap = state.audio_state.active_patterns.get(&pid(0, 0)).unwrap();
assert_eq!(ap.step_index, 1); // still length 4
// Advance through remaining steps of original length-4 pattern
state.tick(tick_at(1.5, true)); // step 1→2
state.tick(tick_at(1.75, true)); // step 2→3
state.tick(tick_at(2.0, true)); // step 3→wraps to 0, iteration completes, update applies
// Now length=2 is applied. Next tick uses new length.
// beat=2.25: step 0 fires, advances to 1
state.tick(tick_at(2.25, true));
let ap = state.audio_state.active_patterns.get(&pid(0, 0)).unwrap();
assert_eq!(ap.step_index, 1);
// beat=1.5: beat_int=6, prev=5, step fires. step_index=1 fires, wraps to 0
state.tick(tick_at(1.5, true));
// beat=2.5: step 1 fires, wraps to 0 (length 2)
state.tick(tick_at(2.5, true));
let ap = state.audio_state.active_patterns.get(&pid(0, 0)).unwrap();
assert_eq!(ap.step_index, 0);
}