Skip to content

Commit d4c80b8

Browse files
tucktuckg00seclaude
andcommitted
v0.8.11: code cleanup, HeaderBar always visible, lazy chop real-time slices
Priority 1 bugs: - getTailLengthSeconds() returns 5.0 (max ADSR release); was 0 — VST contract violation - GrainEngine BPM calc: keep double arithmetic, cast at end (was discarding precision) - Voice muteGroup default changed from 0 to 1 to match slice creation default - AdsrEnvelope: document 0.00015f (~-76 dB) and 1.01f (asymptotic curve) magic numbers Design cleanup: - UIHelpers.h (new): computeDragValue(), computeZoomFactor(), UILayout::waveformVerticalScale - HeaderBar: extract paintRow1/paintRow2; use UIHelpers::computeDragValue() - SliceControlBar: use UIHelpers::computeDragValue() (eliminate duplication) - ScrollZoomBar: use UIHelpers::computeZoomFactor() - WaveformView: encapsulate sliceDrawMode; CacheKey struct; extract paint helpers; use UIHelpers - ActionPanel/PluginEditor: use WaveformView::setSliceDrawMode/isSliceDrawModeActive() - PluginEditor: scaleDirty bool replaces -1.0f sentinel - VoicePool: add const getVoice() overload; document audio constants Features: - HeaderBar now always shows parameter controls (Row1+Row2) even with no sample loaded; waveform area already shows drop hint so HeaderBar was redundant - Lazy chop: slices now appear in the UI immediately on each MIDI note press instead of waiting until lazy chop is stopped (uiSnapshotDirty was gated behind midiSelectsSlice) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 0f87ab4 commit d4c80b8

17 files changed

Lines changed: 476 additions & 463 deletions

src/PluginEditor.cpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,10 +150,7 @@ bool IntersectEditor::keyPressed (const juce::KeyPress& key)
150150
// A — Add Slice mode
151151
if (code == 'A')
152152
{
153-
waveformView.sliceDrawMode = ! waveformView.sliceDrawMode;
154-
waveformView.setMouseCursor (waveformView.sliceDrawMode
155-
? juce::MouseCursor::IBeamCursor
156-
: juce::MouseCursor::NormalCursor);
153+
waveformView.setSliceDrawMode (! waveformView.isSliceDrawModeActive());
157154
repaint();
158155
return true;
159156
}
@@ -275,10 +272,11 @@ void IntersectEditor::timerCallback()
275272
viewportChanged = true;
276273
}
277274

278-
// Check if scale changed (lastScale starts at -1 so first tick always applies)
275+
// Check if scale changed; scaleDirty forces application on first timer tick
279276
float scale = processor.apvts.getRawParameterValue (ParamIds::uiScale)->load();
280-
if (scale != lastScale)
277+
if (scaleDirty || scale != lastScale)
281278
{
279+
scaleDirty = false;
282280
lastScale = scale;
283281
setTransform (juce::AffineTransform::scale (scale));
284282
IntersectLookAndFeel::setMenuScale (scale);

src/PluginEditor.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ class IntersectEditor : public juce::AudioProcessorEditor,
3030
void loadUserSettings();
3131

3232
IntersectProcessor& processor;
33-
float lastScale = -1.0f; // sentinel so first timer tick always applies
33+
float lastScale = 1.0f; // last applied scale value, compared each tick
34+
bool scaleDirty = true; // forces scale application on first timer tick
3435
float lastZoom = -1.0f;
3536
float lastScroll = -1.0f;
3637
int timerHz = 30;

src/PluginProcessor.cpp

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -905,12 +905,11 @@ void IntersectProcessor::processMidi (const juce::MidiBuffer& midi)
905905
{
906906
// Any MIDI note places a chop boundary at the playhead
907907
int newSliceIdx = lazyChop.onNote (note, voicePool, sliceManager);
908-
if (midiSelectsSlice.load (std::memory_order_relaxed) && newSliceIdx >= 0)
908+
if (newSliceIdx >= 0)
909909
{
910-
const int previous = sliceManager.selectedSlice.load (std::memory_order_relaxed);
911-
sliceManager.selectedSlice.store (newSliceIdx, std::memory_order_relaxed);
912-
if (previous != newSliceIdx)
913-
uiSnapshotDirty.store (true, std::memory_order_release);
910+
uiSnapshotDirty.store (true, std::memory_order_release);
911+
if (midiSelectsSlice.load (std::memory_order_relaxed))
912+
sliceManager.selectedSlice.store (newSliceIdx, std::memory_order_relaxed);
914913
}
915914
}
916915
else

src/PluginProcessor.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class IntersectProcessor : public juce::AudioProcessor
2727
const juce::String getName() const override { return "INTERSECT"; }
2828
bool acceptsMidi() const override { return true; }
2929
bool producesMidi() const override { return true; }
30-
double getTailLengthSeconds() const override { return 0.0; }
30+
double getTailLengthSeconds() const override { return 5.0; } // max ADSR release is 5000 ms
3131

3232
int getNumPrograms() override { return 1; }
3333
int getCurrentProgram() override { return 0; }

src/audio/AdsrEnvelope.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ class AdsrEnvelope
6262
case Release:
6363
// Multiply by the per-sample decay factor = exp(-5 / (releaseTime * sr))
6464
level *= (1.0f - releaseCoeff);
65-
if (level < 0.00015f)
65+
if (level < 0.00015f) // ≈ -76 dB, treated as inaudible; avoids infinite exponential decay
6666
{
6767
level = 0.0f;
6868
state = Done;

src/audio/GrainEngine.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@ float GrainEngine::calcStretchBpm (int startSample, int endSample, float timeUni
44
{
55
double durationSec = (endSample - startSample) / sampleRate;
66
float beats = timeUnitBars * 4.0f; // 4 beats per bar
7-
return durationSec > 0.0 ? (beats / (float) durationSec) * 60.0f : 120.0f;
7+
return durationSec > 0.0 ? static_cast<float> ((double) beats / durationSec * 60.0) : 120.0f;
88
}

src/audio/Voice.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ struct Voice
2626
int startSample = 0;
2727
int endSample = 0;
2828
bool pingPong = false;
29-
int muteGroup = 0;
29+
int muteGroup = 1; // default matches slice creation default; always overwritten in startVoice()
3030
bool looping = false;
3131
float volume = 1.0f;
3232
bool releaseTail = false;

src/audio/VoicePool.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@
99
#include <algorithm>
1010
#include <cmath>
1111

12-
static constexpr int kStretchBlockSize = 128;
13-
static constexpr int kMaxStretchInputSamples = 8192;
12+
static constexpr int kStretchBlockSize = 128; // required block size for Signalsmith Stretch processing
13+
static constexpr int kMaxStretchInputSamples = 8192; // max pre-roll/input feed size (empirically tuned)
1414
static constexpr int kMaxBungeeInputFrames = 8192;
1515
static constexpr int kMaxBungeeOutputFrames = 8192;
1616

src/audio/VoicePool.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class VoicePool
6868
int getMaxActiveVoices() const { return maxActive; }
6969

7070
Voice& getVoice (int idx) { return voices[idx]; }
71+
const Voice& getVoice (int idx) const { return voices[idx]; }
7172

7273
void startShiftPreview (int startSample, int bufferSize, double sr, const SampleData& sd);
7374
void stopShiftPreview();

src/ui/ActionPanel.cpp

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,7 @@ ActionPanel::ActionPanel (IntersectProcessor& p, WaveformView& wv)
2525
}
2626

2727
addSliceBtn.onClick = [this] {
28-
waveformView.sliceDrawMode = ! waveformView.sliceDrawMode;
29-
waveformView.setMouseCursor (waveformView.sliceDrawMode
30-
? juce::MouseCursor::IBeamCursor
31-
: juce::MouseCursor::NormalCursor);
28+
waveformView.setSliceDrawMode (! waveformView.isSliceDrawModeActive());
3229
repaint();
3330
};
3431

@@ -152,7 +149,7 @@ void ActionPanel::paint (juce::Graphics& g)
152149
updateSnapButtonAppearance (processor.snapToZeroCrossing.load());
153150

154151
// Highlight +SLC if in draw mode
155-
if (waveformView.sliceDrawMode)
152+
if (waveformView.isSliceDrawModeActive())
156153
{
157154
g.setColour (getTheme().accent.withAlpha (0.25f));
158155
g.fillRect (addSliceBtn.getBounds());

0 commit comments

Comments
 (0)