Feat: tweak and fix from last night workshop
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:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user