#include "PianoRollDisplay.h" PianoRollDisplay::PianoRollDisplay(SliceManager& sm) : sliceManager(sm) { startTimerHz(20); } PianoRollDisplay::~PianoRollDisplay() { stopTimer(); } void PianoRollDisplay::timerCallback() { repaint(); } void PianoRollDisplay::setActiveSlice(int index) { activeSlice = index; } void PianoRollDisplay::setSelectedSlice(int index) { selectedSlice = index; } void PianoRollDisplay::setTotalSamples(int total) { totalSamples = total; } void PianoRollDisplay::setOnKeyClick(std::function callback) { onKeyClick = std::move(callback); } juce::Colour PianoRollDisplay::sliceColour(int sliceIndex) const { juce::Colour colours[] = { juce::Colour(0xffff6b6b), juce::Colour(0xff4ecdc4), juce::Colour(0xffffe66d), juce::Colour(0xffa8e6cf), juce::Colour(0xff8b94ff), juce::Colour(0xffff8b94), juce::Colour(0xffdda0dd), juce::Colour(0xff87ceeb), juce::Colour(0xff98fb98), juce::Colour(0xffffb347), juce::Colour(0xffc39bd3), juce::Colour(0xff76d7c4) }; return colours[sliceIndex % 12]; } int PianoRollDisplay::rowToSliceIndex(int row) const { const int baseMidiNote = 60; int midiNote = baseMidiNote + (numRows - 1 - row); const auto& slices = sliceManager.getSlices(); for (int i = 0; i < sliceManager.getNumSlices(); ++i) { if (slices[static_cast(i)].midiNote == midiNote) return i; } return -1; } void PianoRollDisplay::paint(juce::Graphics& g) { auto bounds = getLocalBounds(); float w = static_cast(bounds.getWidth()); float h = static_cast(bounds.getHeight()); float rowHeight = h / static_cast(numRows); g.fillAll(juce::Colour(0xff1a1a2e)); const auto& slices = sliceManager.getSlices(); int numSlices = sliceManager.getNumSlices(); int total = totalSamples; if (total <= 0 && numSlices > 0) total = slices[static_cast(numSlices - 1)].endSample; // Draw grid lines for (int row = 0; row < numRows; ++row) { float y = static_cast(row) * rowHeight; g.setColour(juce::Colour(0x20ffffff)); g.drawHorizontalLine(static_cast(y), 0.0f, w); } g.drawHorizontalLine(static_cast(h), 0.0f, w); // Draw slice blocks if (numSlices > 0 && total > 0) { for (int i = 0; i < numSlices; ++i) { const auto& slice = slices[static_cast(i)]; const int baseMidiNote = 60; int noteRow = slice.midiNote - baseMidiNote; int row = numRows - 1 - noteRow; float xFrac = static_cast(slice.startSample) / static_cast(total); float endFrac = static_cast(slice.endSample) / static_cast(total); float x = xFrac * w; float bw = (endFrac - xFrac) * w; float y = static_cast(row) * rowHeight + 2.0f; float bh = rowHeight - 4.0f; bw = juce::jmax(bw, 2.0f); juce::Colour col = sliceColour(i); bool isActive = (i == activeSlice); bool isSelected = (i == selectedSlice); g.setColour(isActive ? col.brighter(0.3f) : col.withAlpha(0.8f)); g.fillRoundedRectangle(x + 1.0f, y, bw - 2.0f, bh, 3.0f); if (isSelected && !isActive) { g.setColour(col.brighter(0.5f)); g.drawRoundedRectangle(x + 1.0f, y, bw - 2.0f, bh, 3.0f, 2.0f); } else { g.setColour(col.brighter(0.5f).withAlpha(0.6f)); g.drawRoundedRectangle(x + 1.0f, y, bw - 2.0f, bh, 3.0f, 1.0f); } if (bw > 16.0f) { g.setColour(juce::Colours::white); g.setFont(10.0f); g.drawText(juce::String(i + 1), static_cast(x + 3.0f), static_cast(y), static_cast(bw - 6.0f), static_cast(bh), juce::Justification::centred); } } } } void PianoRollDisplay::resized() {} void PianoRollDisplay::mouseDown(const juce::MouseEvent& e) { float rowHeight = static_cast(getHeight()) / static_cast(numRows); int row = static_cast(static_cast(e.getPosition().getY()) / rowHeight); row = juce::jlimit(0, numRows - 1, row); pressedKey = row; repaint(); int sliceIdx = rowToSliceIndex(row); if (sliceIdx >= 0 && onKeyClick) onKeyClick(sliceIdx); } void PianoRollDisplay::mouseUp(const juce::MouseEvent&) { pressedKey = -1; repaint(); }