fix: freeze the scope when the transport is stopped

FL keeps calling process() with silence while stopped/paused, so the scope
scrolled silence instead of holding. Gate the plot's bucket advance on
context.transport().playing, so it freezes on stop/pause and resumes on play.
Bar meters still fall to silence as before. Update README thread-safety notes
for the ScopeRing feed and transport gating.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Mikkeli Matlock
2026-06-24 16:25:08 +09:00
parent 9a60b0ea72
commit 514fd964f6
2 changed files with 34 additions and 18 deletions
+25 -15
View File
@@ -171,7 +171,7 @@ impl Plugin for Codename206 {
&mut self,
buffer: &mut Buffer,
_aux: &mut AuxiliaryBuffers,
_context: &mut impl ProcessContext<Self>,
context: &mut impl ProcessContext<Self>,
) -> ProcessStatus {
let lookahead = self.lookahead_samples();
@@ -198,6 +198,9 @@ impl Plugin for Codename206 {
// Only do the (cheap) metering work when the editor is actually open.
let metering = self.params.editor_state.is_open();
// The host keeps calling process() with silence while stopped/paused (FL does), so the
// scope is gated on the transport actually playing — otherwise it would scroll silence.
let playing = context.transport().playing;
let num_samples = buffer.samples();
let mut lvl_l = [0.0f32; meters::NUM_CHANNELS];
let mut lvl_r = [0.0f32; meters::NUM_CHANNELS];
@@ -246,9 +249,11 @@ impl Plugin for Codename206 {
lvl_l[b] = lvl_l[b].max(out_l);
lvl_r[b] = lvl_r[b].max(out_r);
gr[b] = gr[b].max(g);
self.scope_in[b] = self.scope_in[b].max(in_mono);
self.scope_out[b] = self.scope_out[b].max(out_l.max(out_r));
self.scope_gr[b] = self.scope_gr[b].max(g);
if playing {
self.scope_in[b] = self.scope_in[b].max(in_mono);
self.scope_out[b] = self.scope_out[b].max(out_l.max(out_r));
self.scope_gr[b] = self.scope_gr[b].max(g);
}
}
}
@@ -272,18 +277,23 @@ impl Plugin for Codename206 {
lvl_r[ALL] = lvl_r[ALL].max(out_r);
gr[ALL] = gr[ALL].max(g);
lim_gr = lim_gr.max(self.limiter.gain_reduction_db());
self.scope_in[ALL] = self.scope_in[ALL].max(in_mono);
self.scope_out[ALL] = self.scope_out[ALL].max(out_l.max(out_r));
self.scope_gr[ALL] = self.scope_gr[ALL].max(g);
// Emit a plot bucket every scope_bucket_len samples (~BUCKET_HZ).
self.scope_samples += 1;
if self.scope_samples >= self.scope_bucket_len {
self.meters.scope.push(&self.scope_in, &self.scope_out, &self.scope_gr);
self.scope_in = [0.0; meters::NUM_CHANNELS];
self.scope_out = [0.0; meters::NUM_CHANNELS];
self.scope_gr = [0.0; meters::NUM_CHANNELS];
self.scope_samples = 0;
// Only advance the scope while the transport is playing, so it freezes (rather than
// scrolling silence) when the host is paused/stopped but still calling process().
if playing {
self.scope_in[ALL] = self.scope_in[ALL].max(in_mono);
self.scope_out[ALL] = self.scope_out[ALL].max(out_l.max(out_r));
self.scope_gr[ALL] = self.scope_gr[ALL].max(g);
// Emit a plot bucket every scope_bucket_len samples (~BUCKET_HZ).
self.scope_samples += 1;
if self.scope_samples >= self.scope_bucket_len {
self.meters.scope.push(&self.scope_in, &self.scope_out, &self.scope_gr);
self.scope_in = [0.0; meters::NUM_CHANNELS];
self.scope_out = [0.0; meters::NUM_CHANNELS];
self.scope_gr = [0.0; meters::NUM_CHANNELS];
self.scope_samples = 0;
}
}
}