mirror of
https://codeberg.org/armin/monostep.git
synced 2026-09-01 04:10:46 +02:00
Prototype
This commit is contained in:
parent
c117906a7f
commit
4271fc34bb
14 changed files with 950 additions and 169 deletions
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
using namespace monostep;
|
||||
|
||||
SequencerMatrix::SequencerMatrix (MonoStepAudioProcessor& processorRef)
|
||||
SequencerMatrix::SequencerMatrix (MonostepAudioProcessor& processorRef)
|
||||
: processor (processorRef)
|
||||
{
|
||||
}
|
||||
|
|
@ -22,13 +22,19 @@ int SequencerMatrix::semitoneForRow (int row) const
|
|||
|
||||
juce::Rectangle<int> SequencerMatrix::matrixBounds() const
|
||||
{
|
||||
const int w = getWidth();
|
||||
const int h = getHeight();
|
||||
|
||||
if (w > labelW && h > headerH)
|
||||
{
|
||||
cellW = (float) (w - labelW) / (float) numSteps;
|
||||
cellH = (float) (h - headerH) / (float) (numRows + 2);
|
||||
}
|
||||
|
||||
const int gridW = juce::roundToInt (cellW * numSteps);
|
||||
const int gridH = juce::roundToInt (cellH * (numRows + 1));
|
||||
const int gridH = juce::roundToInt (cellH * (numRows + 2));
|
||||
|
||||
const int x = juce::roundToInt (labelW + (getWidth() - labelW - gridW) / 2.0f);
|
||||
const int y = juce::roundToInt (headerH + (getHeight() - headerH - gridH) / 2.0f);
|
||||
|
||||
return { x, y, gridW, gridH };
|
||||
return { juce::roundToInt (labelW), juce::roundToInt (headerH), gridW, gridH };
|
||||
}
|
||||
|
||||
void SequencerMatrix::setSelectedStep (int stepIdx)
|
||||
|
|
@ -65,9 +71,16 @@ void SequencerMatrix::paint (juce::Graphics& g)
|
|||
g.fillRect (bounds.toFloat());
|
||||
|
||||
// selected column highlight
|
||||
g.setColour (colours::selected.withAlpha (0.07f));
|
||||
g.setColour (colours::selected.withAlpha (0.02f));
|
||||
g.fillRect (ox + selectedStep * cellW, oy, cellW, (float) bounds.getHeight());
|
||||
|
||||
// play position column highlight (drawn before cells so notes stay vivid)
|
||||
if (playPos >= 0)
|
||||
{
|
||||
g.setColour (colours::accent.withAlpha (0.04f));
|
||||
g.fillRect (ox + playPos * cellW, oy, cellW, (float) bounds.getHeight());
|
||||
}
|
||||
|
||||
// grid lines
|
||||
g.setColour (colours::grid);
|
||||
|
||||
|
|
@ -77,7 +90,7 @@ void SequencerMatrix::paint (juce::Graphics& g)
|
|||
g.drawVerticalLine (juce::roundToInt (x), oy, oy + (float) bounds.getHeight());
|
||||
}
|
||||
|
||||
for (int i = 0; i <= numRows + 1; ++i)
|
||||
for (int i = 0; i <= numRows + 2; ++i)
|
||||
{
|
||||
const float y = oy + i * cellH;
|
||||
g.drawHorizontalLine (juce::roundToInt (y), ox, ox + (float) bounds.getWidth());
|
||||
|
|
@ -107,14 +120,6 @@ void SequencerMatrix::paint (juce::Graphics& g)
|
|||
g.setColour ((i % 4 == 0 && i < seqLen) ? colours::text : colours::textDim);
|
||||
g.setFont (font (10.0f));
|
||||
g.drawText (juce::String (i + 1), juce::roundToInt (cx - 8.0f), juce::roundToInt (cy - 6.0f), 16, 12, juce::Justification::centred);
|
||||
|
||||
if (i == selectedStep)
|
||||
{
|
||||
g.setColour (colours::accent);
|
||||
juce::Path tri;
|
||||
tri.addTriangle (cx - 4.0f, cy - 8.0f, cx + 4.0f, cy - 8.0f, cx, cy);
|
||||
g.fillPath (tri);
|
||||
}
|
||||
}
|
||||
|
||||
// pitch labels
|
||||
|
|
@ -152,10 +157,10 @@ void SequencerMatrix::paint (juce::Graphics& g)
|
|||
const float radius = 3.0f;
|
||||
|
||||
// cell body
|
||||
juce::ColourGradient grad (isSelected ? colours::accent : colours::activeCell,
|
||||
cell.getTopLeft(),
|
||||
isSelected ? colours::accent2 : colours::activeCell.darker (0.6f),
|
||||
cell.getBottomRight(),
|
||||
juce::ColourGradient grad (colours::accent,
|
||||
cell.getTopLeft().translated (cell.getWidth() * 0.5f, 0.0f),
|
||||
isSelected ? colours::accent.brighter (0.12f) : colours::accent.darker (0.35f),
|
||||
cell.getBottomLeft().translated (cell.getWidth() * 0.5f, 0.0f),
|
||||
false);
|
||||
|
||||
g.setGradientFill (grad);
|
||||
|
|
@ -215,6 +220,39 @@ void SequencerMatrix::paint (juce::Graphics& g)
|
|||
g.fillPath (tri);
|
||||
}
|
||||
|
||||
// --- accent row (bottom row) ----------------------------------------------
|
||||
const float accentY = oy + (numRows + 1) * cellH;
|
||||
|
||||
g.setColour (colours::accentDim);
|
||||
g.drawHorizontalLine (juce::roundToInt (accentY - 1.0f), ox, ox + (float) bounds.getWidth());
|
||||
|
||||
g.setFont (font (9.0f));
|
||||
g.setColour (colours::textDim);
|
||||
g.drawText ("ACCENT",
|
||||
juce::roundToInt (labelW * 0.1f), juce::roundToInt (accentY + cellH * 0.5f - 6.0f),
|
||||
juce::roundToInt (labelW * 0.8f), 12,
|
||||
juce::Justification::centredRight);
|
||||
|
||||
for (int i = 0; i < numSteps; ++i)
|
||||
{
|
||||
if (! processor.getSequencer().step (i).accent)
|
||||
continue;
|
||||
|
||||
const auto cell = juce::Rectangle<float> (ox + i * cellW + 1.5f, accentY + 1.5f,
|
||||
cellW - 3.0f, cellH - 3.0f);
|
||||
|
||||
g.setColour (colours::accent2);
|
||||
g.fillRoundedRectangle (cell, 3.0f);
|
||||
|
||||
const float ax = cell.getX() + cell.getWidth() * 0.5f;
|
||||
const float ay = cell.getY() + cell.getHeight() * 0.5f;
|
||||
|
||||
g.setColour (colours::bg);
|
||||
juce::Path tri;
|
||||
tri.addTriangle (ax - 2.0f, ay - 2.5f, ax + 2.0f, ay - 2.5f, ax, ay + 2.5f);
|
||||
g.fillPath (tri);
|
||||
}
|
||||
|
||||
// --- dim columns beyond the pattern length ---------------------------------
|
||||
if (seqLen < numSteps)
|
||||
{
|
||||
|
|
@ -222,21 +260,6 @@ void SequencerMatrix::paint (juce::Graphics& g)
|
|||
g.fillRect (ox + seqLen * cellW, oy,
|
||||
(numSteps - seqLen) * cellW, (float) bounds.getHeight());
|
||||
}
|
||||
|
||||
// --- play position marker ---------------------------------------------------
|
||||
if (playPos >= 0)
|
||||
{
|
||||
const float px = ox + playPos * cellW + cellW * 0.5f;
|
||||
|
||||
g.setColour (colours::playMarker.withAlpha (0.65f));
|
||||
g.drawVerticalLine (juce::roundToInt (px - 1.0f), oy, oy + (float) bounds.getHeight());
|
||||
g.drawVerticalLine (juce::roundToInt (px), oy, oy + (float) bounds.getHeight());
|
||||
|
||||
g.setColour (colours::playMarker);
|
||||
juce::Path tri;
|
||||
tri.addTriangle (px - 3.0f, oy - headerH + 2.0f, px + 3.0f, oy - headerH + 2.0f, px, oy - 1.0f);
|
||||
g.fillPath (tri);
|
||||
}
|
||||
}
|
||||
|
||||
SequencerMatrix::CellRect SequencerMatrix::cellAt (const juce::MouseEvent& e) const
|
||||
|
|
@ -248,9 +271,9 @@ SequencerMatrix::CellRect SequencerMatrix::cellAt (const juce::MouseEvent& e) co
|
|||
|
||||
const bool hit = bounds.contains (e.getPosition())
|
||||
&& col >= 0 && col < numSteps
|
||||
&& row >= 0 && row < numRows + 1;
|
||||
&& row >= -1 && row < numRows + 2;
|
||||
|
||||
return { juce::jlimit (0, numSteps - 1, col), juce::jlimit (0, numRows, row), hit };
|
||||
return { juce::jlimit (0, numSteps - 1, col), juce::jlimit (0, numRows + 1, row), hit };
|
||||
}
|
||||
|
||||
void SequencerMatrix::mouseDown (const juce::MouseEvent& e)
|
||||
|
|
@ -260,11 +283,32 @@ void SequencerMatrix::mouseDown (const juce::MouseEvent& e)
|
|||
if (! cell.hit)
|
||||
return;
|
||||
|
||||
if (cell.row == -1)
|
||||
{
|
||||
selectedStep = cell.step;
|
||||
if (onStepSelected)
|
||||
onStepSelected (cell.step);
|
||||
return;
|
||||
}
|
||||
|
||||
selectedStep = cell.step;
|
||||
|
||||
if (onStepSelected)
|
||||
onStepSelected (cell.step);
|
||||
|
||||
if (cell.row == numRows + 1)
|
||||
{
|
||||
accentDragging = true;
|
||||
accentLastStep = cell.step;
|
||||
startedAccentActive = processor.getSequencer().step (cell.step).accent;
|
||||
|
||||
if (onAccentChanged)
|
||||
onAccentChanged (cell.step, ! startedAccentActive);
|
||||
|
||||
repaint();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cell.row == numRows)
|
||||
{
|
||||
slideDragging = true;
|
||||
|
|
@ -299,13 +343,12 @@ void SequencerMatrix::mouseDown (const juce::MouseEvent& e)
|
|||
|
||||
void SequencerMatrix::mouseDrag (const juce::MouseEvent& e)
|
||||
{
|
||||
const auto cell = cellAt (e);
|
||||
if (! cell.hit || cell.row == -1)
|
||||
return;
|
||||
|
||||
if (fineDragging)
|
||||
{
|
||||
const auto cell = cellAt (e);
|
||||
|
||||
if (! cell.hit)
|
||||
return;
|
||||
|
||||
// one matrix row (one semitone) maps to 4 cents of fine tuning
|
||||
const float cents = juce::jlimit (-50.0f, 50.0f,
|
||||
fineStartCents + (fineRefRow - cell.row) * 4.0f);
|
||||
|
|
@ -325,8 +368,6 @@ void SequencerMatrix::mouseDrag (const juce::MouseEvent& e)
|
|||
|
||||
if (slideDragging)
|
||||
{
|
||||
const auto cell = cellAt (e);
|
||||
|
||||
if (cell.hit && cell.row == numRows && cell.step != slideLastStep)
|
||||
{
|
||||
slideLastStep = cell.step;
|
||||
|
|
@ -340,10 +381,26 @@ void SequencerMatrix::mouseDrag (const juce::MouseEvent& e)
|
|||
return;
|
||||
}
|
||||
|
||||
if (accentDragging)
|
||||
{
|
||||
if (cell.hit && cell.row == numRows + 1 && cell.step != accentLastStep)
|
||||
{
|
||||
accentLastStep = cell.step;
|
||||
|
||||
if (onAccentChanged)
|
||||
onAccentChanged (cell.step, ! startedAccentActive);
|
||||
|
||||
repaint();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (! dragging)
|
||||
return;
|
||||
|
||||
const auto cell = cellAt (e);
|
||||
if (cell.row >= numRows)
|
||||
return;
|
||||
|
||||
if (cell.hit && cell.row < numRows && cell.row != lastRow)
|
||||
{
|
||||
|
|
@ -352,6 +409,8 @@ void SequencerMatrix::mouseDrag (const juce::MouseEvent& e)
|
|||
|
||||
const int pitch = semitoneForRow (cell.row);
|
||||
|
||||
const auto& s = processor.getSequencer().step (cell.step);
|
||||
|
||||
if (startedActive)
|
||||
{
|
||||
if (onNoteChanged)
|
||||
|
|
@ -384,6 +443,12 @@ void SequencerMatrix::mouseUp (const juce::MouseEvent& e)
|
|||
return;
|
||||
}
|
||||
|
||||
if (accentDragging)
|
||||
{
|
||||
accentDragging = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (! dragging)
|
||||
return;
|
||||
|
||||
|
|
@ -391,7 +456,7 @@ void SequencerMatrix::mouseUp (const juce::MouseEvent& e)
|
|||
|
||||
const auto cell = cellAt (e);
|
||||
|
||||
if (! cell.hit || cell.step != dragStep || cell.row >= numRows)
|
||||
if (! cell.hit || cell.step != dragStep || cell.row >= numRows || cell.row == -1)
|
||||
return;
|
||||
|
||||
const int pitch = semitoneForRow (cell.row);
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
#include <JuceHeader.h>
|
||||
|
||||
class MonoStepAudioProcessor;
|
||||
class MonostepAudioProcessor;
|
||||
|
||||
class SequencerMatrix final : public juce::Component
|
||||
{
|
||||
public:
|
||||
explicit SequencerMatrix (MonoStepAudioProcessor& processor);
|
||||
explicit SequencerMatrix (MonostepAudioProcessor& processor);
|
||||
~SequencerMatrix() override = default;
|
||||
|
||||
std::function<void (int stepIdx)> onStepSelected;
|
||||
|
|
@ -15,6 +15,7 @@ public:
|
|||
std::function<void (int stepIdx, int semitone)> onNoteChanged;
|
||||
std::function<void (int stepIdx, float cents)> onFineChanged;
|
||||
std::function<void (int stepIdx, bool slide)> onSlideChanged;
|
||||
std::function<void (int stepIdx, bool accent)> onAccentChanged;
|
||||
|
||||
void setSelectedStep (int stepIdx);
|
||||
int getSelectedStep() const { return selectedStep; }
|
||||
|
|
@ -39,14 +40,15 @@ private:
|
|||
int rowForSemitone (int semitone) const;
|
||||
int semitoneForRow (int row) const;
|
||||
|
||||
MonoStepAudioProcessor& processor;
|
||||
MonostepAudioProcessor& processor;
|
||||
|
||||
float cellW = 24.0f;
|
||||
float cellH = 17.0f;
|
||||
float labelW = 40.0f;
|
||||
float headerH = 24.0f;
|
||||
float headerPad = 10.0f;
|
||||
|
||||
mutable float cellW = 24.0f;
|
||||
mutable float cellH = 17.0f;
|
||||
|
||||
int selectedStep = 0;
|
||||
int playPos = -1;
|
||||
|
||||
|
|
@ -64,4 +66,8 @@ private:
|
|||
bool slideDragging = false;
|
||||
bool startedSlideActive = false;
|
||||
int slideLastStep = -1;
|
||||
|
||||
bool accentDragging = false;
|
||||
bool startedAccentActive = false;
|
||||
int accentLastStep = -1;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue