Files
InstaDrums/Source/WaveformDisplay.cpp
hariel1985 4cc22e0bf0 Initial commit: InstaDrums VST3 drum sampler plugin
- 12-pad drum sampler with 4x3 grid (expandable by 4)
- Velocity layers with round-robin (Salamander-style filename parsing)
- Rhythm Engine-style GUI: pad grid (left), sample editor (right top),
  FX panel (right bottom), master panel (bottom)
- Waveform thumbnails on pads + large waveform in sample editor
- ADSR envelope, pitch, pan per pad
- Drag & drop sample/folder loading
- Kit save/load (.drumkit XML presets)
- Load Folder with smart name matching (kick, snare, hihat, etc.)
- Choke groups, one-shot/polyphonic mode
- Dark modern LookAndFeel with neon accent colors
- Built with JUCE framework, CMake, MSVC 2022

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 10:59:31 +01:00

107 sor
3.8 KiB
C++

#include "WaveformDisplay.h"
#include "LookAndFeel.h"
WaveformDisplay::WaveformDisplay() {}
void WaveformDisplay::setBuffer (const juce::AudioBuffer<float>* buffer, double sampleRate)
{
audioBuffer = buffer;
bufferSampleRate = sampleRate;
repaint();
}
void WaveformDisplay::paint (juce::Graphics& g)
{
auto bounds = getLocalBounds().toFloat();
// Background
g.setColour (InstaDrumsLookAndFeel::bgDark.darker (0.3f));
g.fillRoundedRectangle (bounds, 4.0f);
if (audioBuffer == nullptr || audioBuffer->getNumSamples() == 0)
return;
const int numSamples = audioBuffer->getNumSamples();
const int startSample = (int) (startPos * numSamples);
const int endSample = (int) (endPos * numSamples);
const int visibleSamples = std::max (1, endSample - startSample);
const float width = bounds.getWidth();
const float height = bounds.getHeight();
const float midY = bounds.getCentreY();
// Draw waveform
juce::Path wavePath;
const float* data = audioBuffer->getReadPointer (0);
for (int x = 0; x < (int) width; ++x)
{
int sampleIndex = startSample + (int) ((float) x / width * visibleSamples);
sampleIndex = juce::jlimit (0, numSamples - 1, sampleIndex);
// Find min/max in a small range for better visualization
int blockSize = std::max (1, visibleSamples / (int) width);
float minVal = 1.0f, maxVal = -1.0f;
for (int j = 0; j < blockSize && (sampleIndex + j) < numSamples; ++j)
{
float v = data[sampleIndex + j];
minVal = std::min (minVal, v);
maxVal = std::max (maxVal, v);
}
float topY = midY - maxVal * (height * 0.45f);
float botY = midY - minVal * (height * 0.45f);
if (x == 0)
wavePath.startNewSubPath ((float) x + bounds.getX(), topY);
wavePath.lineTo ((float) x + bounds.getX(), topY);
if (x == (int) width - 1)
{
// Close the path by going back along bottom
for (int bx = (int) width - 1; bx >= 0; --bx)
{
int si = startSample + (int) ((float) bx / width * visibleSamples);
si = juce::jlimit (0, numSamples - 1, si);
float mn = 1.0f;
for (int j = 0; j < blockSize && (si + j) < numSamples; ++j)
mn = std::min (mn, data[si + j]);
float by = midY - mn * (height * 0.45f);
wavePath.lineTo ((float) bx + bounds.getX(), by);
}
wavePath.closeSubPath();
}
}
// Fill waveform
g.setColour (waveColour.withAlpha (0.5f));
g.fillPath (wavePath);
g.setColour (waveColour.withAlpha (0.9f));
g.strokePath (wavePath, juce::PathStrokeType (1.0f));
// Draw ADSR overlay
if (showADSR)
{
float totalSeconds = (float) numSamples / (float) bufferSampleRate;
float ax = adsrA / totalSeconds;
float dx = adsrD / totalSeconds;
float sx = 0.4f; // sustain portion
float rx = adsrR / totalSeconds;
float total = ax + dx + sx + rx;
// Normalize to width
juce::Path adsrPath;
float x0 = bounds.getX();
float w = bounds.getWidth();
adsrPath.startNewSubPath (x0, bounds.getBottom());
adsrPath.lineTo (x0 + (ax / total) * w, bounds.getY() + 4); // attack peak
adsrPath.lineTo (x0 + ((ax + dx) / total) * w, midY - (adsrS - 0.5f) * height * 0.8f); // decay to sustain
adsrPath.lineTo (x0 + ((ax + dx + sx) / total) * w, midY - (adsrS - 0.5f) * height * 0.8f); // sustain hold
adsrPath.lineTo (x0 + w, bounds.getBottom()); // release to 0
g.setColour (InstaDrumsLookAndFeel::accent.withAlpha (0.7f));
g.strokePath (adsrPath, juce::PathStrokeType (2.0f));
}
}