Skip to content

Commit

Permalink
Improve sample draw algorithm (#1234)
Browse files Browse the repository at this point in the history
* Improve sample draw algorithm

1. Better in high zoom
2. Cleaner zero crossing handling
3. Makes more sense in the code

* Few more drawing tweaks
  • Loading branch information
baconpaul authored Sep 1, 2024
1 parent ee14045 commit 9a10fc5
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 56 deletions.
119 changes: 67 additions & 52 deletions src-ui/app/edit-screen/components/mapping-pane/SampleWaveform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ SampleWaveform::SampleWaveform(VariantDisplay *d) : display(d), HasEditor(d->edi

void SampleWaveform::rebuildHotZones()
{
rebuildLegacyPathForSample();
rebuildEnvelopePaths();
static constexpr int hotZoneSize{10};
auto &v = display->variantView.variants[display->selectedVariation];
auto samp = editor->sampleManager.getSample(v.sampleID);
Expand Down Expand Up @@ -124,7 +124,7 @@ int SampleWaveform::xPixelForSample(int64_t samplePos, bool doClamp)
}
}

void SampleWaveform::rebuildLegacyPathForSample()
void SampleWaveform::rebuildEnvelopePaths()
{
if (display->selectedVariation < 0 || display->selectedVariation > scxt::maxVariantsPerZone)
return;
Expand All @@ -141,12 +141,13 @@ void SampleWaveform::rebuildLegacyPathForSample()
* OK so we have pctStart and zoomFactor so whats that in sample space
*/
auto l = samp->getSampleLength();
auto startSample = std::clamp((int)std::floor(l * pctStart), 0, (int)l);
int samplePad{10}; // a fudge for very very high zooms stops start glitches
auto startSample = std::clamp((int)std::floor(l * pctStart) - samplePad, 0, (int)l);
auto numSamples = (int)std::ceil(1.f * l / zoomFactor);
auto endSample = std::clamp(startSample + numSamples, 0, (int)l);
auto endSample = std::clamp(startSample + numSamples + 2 * samplePad, 0, (int)l);

std::vector<std::pair<size_t, float>> topLine, bottomLine;
auto fac = 1.0 * numSamples / r.getWidth();
auto fac = std::max(1.0 * numSamples / r.getWidth(), 1.0);

auto downSampleForUI = [startSample, endSample, fac, &topLine, &bottomLine](auto *data) {
using T = std::remove_pointer_t<decltype(data)>;
Expand All @@ -172,18 +173,11 @@ void SampleWaveform::rebuildLegacyPathForSample()
{
if (c + fac < s)
{
// nmx are in -1..1 coordinates. We want to go to 0..1 coordinates
// and opposite
mx = std::max(T(0), mx);
mn = std::min(T(0), mn);
double nmx = mx * 1.0 / normFactor;
double nmn = mn * 1.0 / normFactor;

double pmx = (-nmx + 1) * 0.5;
double pmn = (-nmn + 1) * 0.5;

topLine.emplace_back(s, pmx);
bottomLine.emplace_back(s, pmn);
topLine.emplace_back(s, nmx);
bottomLine.emplace_back(s, nmn);

c += fac;
ct++;
Expand All @@ -210,49 +204,73 @@ void SampleWaveform::rebuildLegacyPathForSample()
jassertfalse;
}

juce::Path res;

bool first{true};
upperEnvelope = juce::Path();
lowerEnvelope = juce::Path();
upperEnvelope.startNewSubPath(0, getHeight() / 2);
lowerEnvelope.startNewSubPath(getWidth(), getHeight() / 2);
for (auto &[smp, val] : topLine)
upperFill = juce::Path();
upperStroke = juce::Path();
lowerFill = juce::Path();
lowerStroke = juce::Path();

auto sVToPx = [this](float val) {
// val is -1..1 so move it to 0..1 inverted
auto nval = (-val + 1) * 0.5;
// then scale it by height
return nval * getHeight();
};
for (const auto &[smp, val] : topLine)
{
auto pos = xPixelForSample(smp);
auto uval = std::max(val, 0.0f);

if (first)
{
upperStroke.startNewSubPath(pos, sVToPx(val));
upperFill.startNewSubPath(pos, sVToPx(uval));
first = false;
}
else
{
upperStroke.lineTo(pos, sVToPx(val));
upperFill.lineTo(pos, sVToPx(uval));
}
}

for (const auto &[smp, val] : bottomLine)
{
auto pos = xPixelForSample(smp);
auto lval = std::min(val, 0.0f);
if (first)
{
res.startNewSubPath(pos, val * r.getHeight());
lowerStroke.startNewSubPath(pos, sVToPx(val));
lowerFill.startNewSubPath(pos, sVToPx(lval));
first = false;
}
else
{
res.lineTo(pos, val * r.getHeight());
lowerStroke.lineTo(pos, sVToPx(val));
lowerFill.lineTo(pos, sVToPx(lval));
}
upperEnvelope.lineTo(pos, val * r.getHeight());
}

std::reverse(bottomLine.begin(), bottomLine.end());
std::reverse(topLine.begin(), topLine.end());

first = true;
for (auto &[smp, val] : bottomLine)
for (const auto &[smp, val] : bottomLine)
{
auto pos = xPixelForSample(smp);
lowerEnvelope.lineTo(pos, val * r.getHeight());
auto uval = std::max(val, 0.f);
upperFill.lineTo(pos, sVToPx(uval));
}

upperEnvelope.lineTo(getWidth(), getHeight() / 2);
lowerEnvelope.lineTo(0, getHeight() / 2);

for (auto &[smp, val] : bottomLine)
for (const auto &[smp, val] : topLine)
{
auto pos = xPixelForSample(smp);

res.lineTo(pos, val * r.getHeight());
auto uval = std::min(val, 0.f);
lowerFill.lineTo(pos, sVToPx(uval));
}
res.closeSubPath();

legacyPath = res;
upperFill.closeSubPath();
lowerFill.closeSubPath();
}

void SampleWaveform::mouseDown(const juce::MouseEvent &e)
Expand Down Expand Up @@ -340,13 +358,10 @@ void SampleWaveform::paint(juce::Graphics &g)
return;
}

auto l = samp->getSampleLength();
g.setColour(editor->themeColor(theme::ColorMap::grid_secondary));
g.drawHorizontalLine(getHeight() / 2, 0, getWidth());

/*g.setColour(juce::Colours::red);
g.fillPath(upperEnvelope);
g.setColour(juce::Colours::blue);
g.fillPath(lowerEnvelope);
*/
auto l = samp->getSampleLength();

auto ssp = xPixelForSample(v.startSample);
auto esp = xPixelForSample(v.endSample);
Expand All @@ -373,10 +388,10 @@ void SampleWaveform::paint(juce::Graphics &g)
g.reduceClipRegion(cr);

g.setGradientFill(gTop);
g.fillPath(upperEnvelope);
g.fillPath(upperFill);

g.setGradientFill(gBot);
g.fillPath(lowerEnvelope);
g.fillPath(lowerFill);
}
if (esp <= getWidth())
{
Expand All @@ -385,10 +400,10 @@ void SampleWaveform::paint(juce::Graphics &g)
g.reduceClipRegion(cr);

g.setGradientFill(gTop);
g.fillPath(upperEnvelope);
g.fillPath(upperFill);

g.setGradientFill(gBot);
g.fillPath(lowerEnvelope);
g.fillPath(lowerFill);
}

auto spC = std::clamp(ssp, 0, getWidth());
Expand All @@ -399,14 +414,14 @@ void SampleWaveform::paint(juce::Graphics &g)
g.reduceClipRegion(cr);

g.setGradientFill(sTop);
g.fillPath(upperEnvelope);
g.fillPath(upperFill);

g.setGradientFill(sBot);
g.fillPath(lowerEnvelope);
g.fillPath(lowerFill);

g.setColour(a1a);
g.strokePath(upperEnvelope, juce::PathStrokeType(1));
g.strokePath(lowerEnvelope, juce::PathStrokeType(1));
g.strokePath(upperStroke, juce::PathStrokeType(1));
g.strokePath(lowerStroke, juce::PathStrokeType(1));
}

if (v.loopActive)
Expand All @@ -427,14 +442,14 @@ void SampleWaveform::paint(juce::Graphics &g)
g.reduceClipRegion(dr);

g.setGradientFill(lTop);
g.fillPath(upperEnvelope);
g.fillPath(upperFill);

g.setGradientFill(lBot);
g.fillPath(lowerEnvelope);
g.fillPath(lowerFill);

g.setColour(a1a);
g.strokePath(upperEnvelope, juce::PathStrokeType(1));
g.strokePath(lowerEnvelope, juce::PathStrokeType(1));
g.strokePath(upperStroke, juce::PathStrokeType(1));
g.strokePath(lowerStroke, juce::PathStrokeType(1));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ struct SampleWaveform : juce::Component, HasEditor, sst::jucegui::components::Zo
{
if (isVisible())
{
rebuildLegacyPathForSample();
rebuildEnvelopePaths();
repaint();
}
}
Expand All @@ -78,9 +78,8 @@ struct SampleWaveform : juce::Component, HasEditor, sst::jucegui::components::Zo

void rebuildPaths();

juce::Path legacyPath;
juce::Path upperEnvelope, lowerEnvelope;
void rebuildLegacyPathForSample();
juce::Path upperStroke, lowerStroke, upperFill, lowerFill;
void rebuildEnvelopePaths();

int64_t sampleForXPixel(float xpos);
int xPixelForSample(int64_t samplePos, bool doClamp = true);
Expand Down

0 comments on commit 9a10fc5

Please sign in to comment.