mirror of
https://codeberg.org/armin/rmx-19.git
synced 2026-10-11 16:51:55 +02:00
Add delay tempo sync, rack faceplate, README, docs
- Add host-tempo-synced delay: FX screen gains a 4th SYNC row for rhythmic divisions (1/16 .. 1/1) that lock delay time to DAW tempo - Embed rack faceplate background (b2.png) via juce binary data - Add offline dev probe (rmx_probe) for GUI-less MIDI testing - Allow overriding JUCE_ROOT when configuring the build - Write comprehensive README describing the instrument, UI, MIDI, DSP, and build/install steps
This commit is contained in:
parent
d63db3af8a
commit
2e870380d4
17 changed files with 1413 additions and 324 deletions
|
|
@ -1,5 +1,6 @@
|
|||
#include "LcdPanel.h"
|
||||
#include "BuildInfo.h"
|
||||
#include <cstring>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 5x7 dot-matrix font. Each glyph is 5 columns x 7 rows, bit 6 (0x40) of each
|
||||
|
|
@ -78,6 +79,94 @@ static const Entry table[] =
|
|||
|
||||
} // namespace
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// 3x5 compact font (denser layout, reads like a small real-world LCD). Each
|
||||
// glyph is 3 columns x 5 rows, bits 0..2 (bit 2 is the leftmost dot).
|
||||
// ---------------------------------------------------------------------------
|
||||
namespace {
|
||||
|
||||
static constexpr int MW = 3, MH = 5;
|
||||
|
||||
struct MEntry { int key; const juce::uint8* rows; };
|
||||
|
||||
static const juce::uint8 mA_[MH] = { 0b010, 0b101, 0b111, 0b101, 0b101 };
|
||||
static const juce::uint8 mB_[MH] = { 0b110, 0b101, 0b110, 0b101, 0b110 };
|
||||
static const juce::uint8 mC_[MH] = { 0b011, 0b100, 0b100, 0b100, 0b011 };
|
||||
static const juce::uint8 mD_[MH] = { 0b110, 0b101, 0b101, 0b101, 0b110 };
|
||||
static const juce::uint8 mE_[MH] = { 0b111, 0b100, 0b110, 0b100, 0b111 };
|
||||
static const juce::uint8 mF_[MH] = { 0b111, 0b100, 0b110, 0b100, 0b100 };
|
||||
static const juce::uint8 mG_[MH] = { 0b011, 0b100, 0b101, 0b101, 0b011 };
|
||||
static const juce::uint8 mH_[MH] = { 0b101, 0b101, 0b111, 0b101, 0b101 };
|
||||
static const juce::uint8 mI_[MH] = { 0b111, 0b010, 0b010, 0b010, 0b111 };
|
||||
static const juce::uint8 mJ_[MH] = { 0b111, 0b001, 0b001, 0b101, 0b010 };
|
||||
static const juce::uint8 mK_[MH] = { 0b101, 0b110, 0b100, 0b110, 0b101 };
|
||||
static const juce::uint8 mL_[MH] = { 0b100, 0b100, 0b100, 0b100, 0b111 };
|
||||
static const juce::uint8 mM_[MH] = { 0b101, 0b111, 0b111, 0b101, 0b101 };
|
||||
static const juce::uint8 mN_[MH] = { 0b110, 0b101, 0b101, 0b101, 0b101 };
|
||||
static const juce::uint8 mO_[MH] = { 0b010, 0b101, 0b101, 0b101, 0b010 };
|
||||
static const juce::uint8 mP_[MH] = { 0b110, 0b101, 0b110, 0b100, 0b100 };
|
||||
static const juce::uint8 mQ_[MH] = { 0b010, 0b101, 0b101, 0b110, 0b011 };
|
||||
static const juce::uint8 mR_[MH] = { 0b110, 0b101, 0b110, 0b110, 0b101 };
|
||||
static const juce::uint8 mS_[MH] = { 0b011, 0b100, 0b010, 0b001, 0b110 };
|
||||
static const juce::uint8 mT_[MH] = { 0b111, 0b010, 0b010, 0b010, 0b010 };
|
||||
static const juce::uint8 mU_[MH] = { 0b101, 0b101, 0b101, 0b101, 0b111 };
|
||||
static const juce::uint8 mV_[MH] = { 0b101, 0b101, 0b101, 0b101, 0b010 };
|
||||
static const juce::uint8 mW_[MH] = { 0b101, 0b111, 0b101, 0b111, 0b101 };
|
||||
static const juce::uint8 mX_[MH] = { 0b101, 0b101, 0b010, 0b101, 0b101 };
|
||||
static const juce::uint8 mY_[MH] = { 0b101, 0b101, 0b010, 0b010, 0b010 };
|
||||
static const juce::uint8 mZ_[MH] = { 0b111, 0b001, 0b010, 0b100, 0b111 };
|
||||
static const juce::uint8 mn0_[MH] = { 0b010, 0b101, 0b101, 0b101, 0b010 };
|
||||
static const juce::uint8 mn1_[MH] = { 0b011, 0b001, 0b001, 0b001, 0b011 };
|
||||
static const juce::uint8 mn2_[MH] = { 0b110, 0b001, 0b010, 0b100, 0b111 };
|
||||
static const juce::uint8 mn3_[MH] = { 0b110, 0b001, 0b110, 0b001, 0b110 };
|
||||
static const juce::uint8 mn4_[MH] = { 0b101, 0b101, 0b111, 0b001, 0b001 };
|
||||
static const juce::uint8 mn5_[MH] = { 0b111, 0b100, 0b110, 0b001, 0b110 };
|
||||
static const juce::uint8 mn6_[MH] = { 0b010, 0b100, 0b110, 0b101, 0b010 };
|
||||
static const juce::uint8 mn7_[MH] = { 0b111, 0b001, 0b001, 0b010, 0b010 };
|
||||
static const juce::uint8 mn8_[MH] = { 0b010, 0b101, 0b010, 0b101, 0b010 };
|
||||
static const juce::uint8 mn9_[MH] = { 0b010, 0b101, 0b011, 0b001, 0b010 };
|
||||
static const juce::uint8 msp_[MH] = { 0, 0, 0, 0, 0 };
|
||||
static const juce::uint8 mdt_[MH] = { 0, 0, 0, 0b011, 0b011 };
|
||||
static const juce::uint8 mco_[MH] = { 0, 0, 0, 0b110, 0b100 };
|
||||
static const juce::uint8 mcl_[MH] = { 0, 0b010, 0, 0b010, 0 };
|
||||
static const juce::uint8 mmi_[MH] = { 0, 0, 0b111, 0, 0 };
|
||||
static const juce::uint8 mpl_[MH] = { 0, 0b010, 0b111, 0b010, 0 };
|
||||
static const juce::uint8 msl_[MH] = { 0b001, 0b001, 0b010, 0b100, 0b100 };
|
||||
static const juce::uint8 mpc_[MH] = { 0b101, 0b001, 0b010, 0b100, 0b101 };
|
||||
static const juce::uint8 map_[MH] = { 0b010, 0b010, 0, 0, 0 };
|
||||
static const juce::uint8 mlt_[MH] = { 0b100, 0b010, 0b001, 0b010, 0b100 };
|
||||
static const juce::uint8 mgt_[MH] = { 0b001, 0b010, 0b100, 0b010, 0b001 };
|
||||
static const juce::uint8 meq_[MH] = { 0, 0b111, 0, 0b111, 0 };
|
||||
static const juce::uint8 mhs_[MH] = { 0b010, 0b111, 0b111, 0b111, 0b010 };
|
||||
static const juce::uint8 mst_[MH] = { 0, 0, 0, 0, 0b111 };
|
||||
static const juce::uint8 mex_[MH] = { 0b010, 0b010, 0b010, 0, 0b010 };
|
||||
static const juce::uint8 mas_[MH] = { 0, 0b101, 0b010, 0b101, 0 };
|
||||
|
||||
static const MEntry mtable[] =
|
||||
{
|
||||
{ 'A', mA_ }, { 'B', mB_ }, { 'C', mC_ }, { 'D', mD_ }, { 'E', mE_ },
|
||||
{ 'F', mF_ }, { 'G', mG_ }, { 'H', mH_ }, { 'I', mI_ }, { 'J', mJ_ },
|
||||
{ 'K', mK_ }, { 'L', mL_ }, { 'M', mM_ }, { 'N', mN_ }, { 'O', mO_ },
|
||||
{ 'P', mP_ }, { 'Q', mQ_ }, { 'R', mR_ }, { 'S', mS_ }, { 'T', mT_ },
|
||||
{ 'U', mU_ }, { 'V', mV_ }, { 'W', mW_ }, { 'X', mX_ }, { 'Y', mY_ },
|
||||
{ 'Z', mZ_ }, { '0', mn0_ }, { '1', mn1_ }, { '2', mn2_ }, { '3', mn3_ },
|
||||
{ '4', mn4_ }, { '5', mn5_ }, { '6', mn6_ }, { '7', mn7_ }, { '8', mn8_ },
|
||||
{ '9', mn9_ }, { ' ', msp_ }, { '.', mdt_ }, { ',', mco_ }, { ':', mcl_ },
|
||||
{ '-', mmi_ }, { '+', mpl_ }, { '/', msl_ }, { '%', mpc_ }, { '\'', map_ },
|
||||
{ '<', mlt_ }, { '>', mgt_ }, { '=', meq_ }, { '#', mhs_ }, { '_', mst_ },
|
||||
{ '!', mex_ }, { '*', mas_ },
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
const juce::uint8* LcdPanel::glyphM (int key)
|
||||
{
|
||||
if (key >= 'a' && key <= 'z') key = key - 'a' + 'A';
|
||||
for (const auto& e : mtable)
|
||||
if (e.key == key) return e.rows;
|
||||
return msp_;
|
||||
}
|
||||
|
||||
const juce::uint8* LcdPanel::glyphFor (int key)
|
||||
{
|
||||
if (key >= 'a' && key <= 'z') key = key - 'a' + 'A';
|
||||
|
|
@ -91,7 +180,10 @@ const juce::uint8* LcdPanel::glyphFor (int key)
|
|||
LcdPanel::LcdPanel (RMX19AudioProcessor& p)
|
||||
: proc (p)
|
||||
{
|
||||
dotImage = juce::Image (juce::Image::ARGB, gridW, gridH, true);
|
||||
dotImage = juce::Image (juce::Image::ARGB, gridW, gridH, false);
|
||||
displayImage = juce::Image (juce::Image::ARGB, gridW * 2, gridH * 2, false);
|
||||
backdrop = juce::Image (juce::Image::ARGB, gridW * 2, gridH * 2, false);
|
||||
buildBackdrop();
|
||||
splashUntil = juce::Time::getMillisecondCounter() + 1500;
|
||||
lastMidi = proc.getLastMidiTime();
|
||||
startTimerHz (22);
|
||||
|
|
@ -104,21 +196,36 @@ LcdPanel::~LcdPanel()
|
|||
|
||||
void LcdPanel::timerCallback()
|
||||
{
|
||||
float l, r; proc.getMeters (l, r);
|
||||
meterL = std::max (l, meterL * 0.5f);
|
||||
meterR = std::max (r, meterR * 0.5f);
|
||||
bool needsRedraw = (screen == Screen::Splash);
|
||||
|
||||
if (proc.getLastMidiTime() != lastMidi)
|
||||
{
|
||||
lastMidi = proc.getLastMidiTime();
|
||||
powerGlow = 1.0f;
|
||||
needsRedraw = true;
|
||||
}
|
||||
powerGlow = std::max (0.0f, powerGlow - 0.04f);
|
||||
const float oldGlow = powerGlow;
|
||||
powerGlow = std::max (0.0f, powerGlow - 0.05f);
|
||||
if (powerGlow >= 0.05f || oldGlow >= 0.05f) needsRedraw = true;
|
||||
|
||||
if (screen == Screen::Splash && juce::Time::getMillisecondCounter() > splashUntil)
|
||||
{
|
||||
screen = Screen::Home;
|
||||
needsRedraw = true;
|
||||
}
|
||||
|
||||
setDirty();
|
||||
if (screen == Screen::Home)
|
||||
{
|
||||
const int v = proc.getActiveVoices();
|
||||
if (v != lastVoices)
|
||||
{
|
||||
lastVoices = v;
|
||||
needsRedraw = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsRedraw)
|
||||
setDirty();
|
||||
}
|
||||
|
||||
void LcdPanel::paint (juce::Graphics& g)
|
||||
|
|
@ -132,7 +239,6 @@ void LcdPanel::paint (juce::Graphics& g)
|
|||
g.drawRect (b, 2);
|
||||
|
||||
// inner LCD glass (black)
|
||||
const float scale = (float) b.getWidth() / (float) gridW;
|
||||
const int cell = 2;
|
||||
const int w = gridW * cell;
|
||||
const int h = gridH * cell;
|
||||
|
|
@ -141,26 +247,150 @@ void LcdPanel::paint (juce::Graphics& g)
|
|||
g.setColour (juce::Colour (0xff020703));
|
||||
g.fillRect (ox, oy, w, h);
|
||||
|
||||
if (contentDirty)
|
||||
{
|
||||
rebuild();
|
||||
contentDirty = false;
|
||||
}
|
||||
|
||||
g.drawImageAt (displayImage, ox, oy);
|
||||
}
|
||||
|
||||
void LcdPanel::buildBackdrop()
|
||||
{
|
||||
const int W = displayImage.getWidth();
|
||||
const int H = displayImage.getHeight();
|
||||
juce::Graphics dg (backdrop);
|
||||
|
||||
// dark glass base
|
||||
dg.fillAll (juce::Colour (0xff020703));
|
||||
|
||||
// even, faint backlight (brightest toward the middle of the glass)
|
||||
{
|
||||
const float cx = W * 0.5f, cy = H * 0.5f;
|
||||
juce::ColourGradient backlight (juce::Colour::fromRGBA (0x28, 0x5a, 0x3c, 22), cx, cy,
|
||||
juce::Colour::fromRGBA (0x18, 0x2e, 0x20, 0), W * 0.38f, cy, false);
|
||||
dg.setGradientFill (backlight);
|
||||
dg.fillRect (backdrop.getBounds());
|
||||
}
|
||||
|
||||
// glass reflection sheen across the top
|
||||
{
|
||||
juce::ColourGradient sheen (juce::Colour::fromRGBA (0xff, 0xff, 0xff, 16), 0.0f, 2.0f,
|
||||
juce::Colour::fromRGBA (0xff, 0xff, 0xff, 0), 0.0f, 42.0f, false);
|
||||
dg.setGradientFill (sheen);
|
||||
dg.fillRect (0, 0, W, 42);
|
||||
}
|
||||
|
||||
// subtle vignette so the glass reads as a luminous surface
|
||||
{
|
||||
juce::ColourGradient eT (juce::Colour::fromRGBA (0x00, 0x08, 0x04, 60), 0, 0,
|
||||
juce::Colour::fromRGBA (0x00, 0x08, 0x04, 0), 0, 10, false);
|
||||
dg.setGradientFill (eT); dg.fillRect (0, 0, W, 10);
|
||||
|
||||
juce::ColourGradient eB (juce::Colour::fromRGBA (0x00, 0x08, 0x04, 60), 0, (float) H,
|
||||
juce::Colour::fromRGBA (0x00, 0x08, 0x04, 0), 0, (float) H - 10, false);
|
||||
dg.setGradientFill (eB); dg.fillRect (0, H - 10, W, 10);
|
||||
|
||||
juce::ColourGradient eL (juce::Colour::fromRGBA (0x00, 0x08, 0x04, 36), 0, 0,
|
||||
juce::Colour::fromRGBA (0x00, 0x08, 0x04, 0), 10, 0, false);
|
||||
dg.setGradientFill (eL); dg.fillRect (0, 0, 10, H);
|
||||
|
||||
juce::ColourGradient eR (juce::Colour::fromRGBA (0x00, 0x08, 0x04, 36), (float) W, 0,
|
||||
juce::Colour::fromRGBA (0x00, 0x08, 0x04, 0), (float) W - 10, 0, false);
|
||||
dg.setGradientFill (eR); dg.fillRect (W - 10, 0, 10, H);
|
||||
}
|
||||
|
||||
// faint "ghost" dot for every (unlit) segment, exactly like a passive matrix LCD
|
||||
for (int y = 0; y < gridH; ++y)
|
||||
for (int x = 0; x < gridW; ++x)
|
||||
{
|
||||
const float cx = x * 2.0f + 1.0f;
|
||||
const float cy = y * 2.0f + 1.0f;
|
||||
dg.setColour (juce::Colour::fromRGBA (0x00, 0x88, 0x55, 6));
|
||||
dg.fillEllipse (cx - 0.55f, cy - 0.55f, 1.1f, 1.1f);
|
||||
}
|
||||
}
|
||||
|
||||
void LcdPanel::rebuild()
|
||||
{
|
||||
// Image::clear() hard-zeros pixels; Graphics::fillAll(transparent black) only blends
|
||||
// and leaves stale content behind.
|
||||
dotImage.clear (dotImage.getBounds());
|
||||
{
|
||||
juce::Graphics dg (dotImage);
|
||||
dg.fillAll (juce::Colours::black);
|
||||
drawContent (dg);
|
||||
}
|
||||
|
||||
for (int y = 0; y < gridH; ++y)
|
||||
const int W = displayImage.getWidth();
|
||||
const int H = displayImage.getHeight();
|
||||
|
||||
// start from the pre-rendered base layer (backlight, sheen, vignette, ghost dots)
|
||||
{
|
||||
for (int x = 0; x < gridW; ++x)
|
||||
juce::Image::BitmapData src (backdrop, juce::Image::BitmapData::readOnly);
|
||||
juce::Image::BitmapData dst (displayImage, juce::Image::BitmapData::writeOnly);
|
||||
for (int y = 0; y < H; ++y)
|
||||
std::memcpy (dst.getLinePointer (y), src.getLinePointer (y), (size_t) W * 4u);
|
||||
}
|
||||
|
||||
// blend the lit segments in as crisp round dots with just a hint of bloom.
|
||||
// The core is near-solid so glyphs stay sharp; the glow is faint and tight
|
||||
// so it can't wash the dark gaps between rows/columns.
|
||||
constexpr float coreR2 = 0.36f; // bright core radius ~0.60 cell
|
||||
constexpr float glowR2 = 1.10f; // faint bloom radius ~1.05 cells
|
||||
{
|
||||
juce::Image::BitmapData cells (dotImage, juce::Image::BitmapData::readOnly);
|
||||
juce::Image::BitmapData out (displayImage, juce::Image::BitmapData::readWrite);
|
||||
|
||||
for (int cy = 0; cy < gridH; ++cy)
|
||||
{
|
||||
const auto px = dotImage.getPixelAt (x, y);
|
||||
const float lvl = px.getAlpha() / 255.0f;
|
||||
if (lvl <= 0.0f) continue;
|
||||
const juce::Colour c = juce::Colour::fromFloatRGBA (0.04f + 0.02f * powerGlow,
|
||||
0.96f * lvl, 0.25f * lvl, 1.0f);
|
||||
g.setColour (c);
|
||||
g.fillRect (ox + x * cell, oy + y * cell, cell, cell);
|
||||
for (int cx = 0; cx < gridW; ++cx)
|
||||
{
|
||||
const float lvl = cells.getPixelColour (cx, cy).getAlpha() / 255.0f;
|
||||
if (lvl <= 0.02f) continue;
|
||||
|
||||
const float ccx = cx * 2.0f + 1.0f;
|
||||
const float ccy = cy * 2.0f + 1.0f;
|
||||
const int x0 = cx * 2 - 1, x1 = cx * 2 + 3;
|
||||
const int y0 = cy * 2 - 1, y1 = cy * 2 + 3;
|
||||
|
||||
for (int py = y0; py <= y1; ++py)
|
||||
{
|
||||
if (py < 0 || py >= H) continue;
|
||||
for (int px = x0; px <= x1; ++px)
|
||||
{
|
||||
if (px < 0 || px >= W) continue;
|
||||
|
||||
const float dx = (px - ccx) * 0.5f;
|
||||
const float dy = (py - ccy) * 0.5f;
|
||||
const float d2 = dx * dx + dy * dy;
|
||||
if (d2 > glowR2) continue;
|
||||
|
||||
float a;
|
||||
juce::uint8 r, g, b;
|
||||
if (d2 <= coreR2)
|
||||
{
|
||||
a = (0.85f + 0.15f * (1.0f - d2 / coreR2)) * lvl;
|
||||
r = 0xdf; g = 0xff; b = 0xe6;
|
||||
}
|
||||
else
|
||||
{
|
||||
a = (1.0f - (d2 - coreR2) / (glowR2 - coreR2)) * 0.16f * lvl;
|
||||
r = 0x90; g = 0xff; b = 0xa8;
|
||||
}
|
||||
if (a <= 0.002f) continue;
|
||||
|
||||
const juce::Colour base = out.getPixelColour (px, py);
|
||||
const float ia = 1.0f - a;
|
||||
out.setPixelColour (px, py, juce::Colour (
|
||||
(juce::uint8) juce::jmin (255.0f, (float) base.getRed() * ia + r * a),
|
||||
(juce::uint8) juce::jmin (255.0f, (float) base.getGreen() * ia + g * a),
|
||||
(juce::uint8) juce::jmin (255.0f, (float) base.getBlue() * ia + b * a)));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(void) scale;
|
||||
}
|
||||
|
||||
void LcdPanel::drawContent (juce::Graphics& g)
|
||||
|
|
@ -175,7 +405,6 @@ void LcdPanel::drawContent (juce::Graphics& g)
|
|||
case Screen::System: drawSystem (g); break;
|
||||
case Screen::Info: drawInfo (g); break;
|
||||
}
|
||||
drawFooter (g);
|
||||
}
|
||||
|
||||
void LcdPanel::px (juce::Graphics& g, int x, int y, float intensity)
|
||||
|
|
@ -205,6 +434,24 @@ void LcdPanel::pxText (juce::Graphics& g, int x, int y, const juce::String& text
|
|||
}
|
||||
}
|
||||
|
||||
void LcdPanel::pxTextM (juce::Graphics& g, int x, int y, const juce::String& text, float intensity)
|
||||
{
|
||||
int cx = x;
|
||||
for (int i = 0; i < text.length(); ++i)
|
||||
{
|
||||
const int key = text[i];
|
||||
const juce::uint8* rows = glyphM (key);
|
||||
for (int row = 0; row < MH; ++row)
|
||||
{
|
||||
const juce::uint8 bits = rows[row];
|
||||
for (int col = 0; col < MW; ++col)
|
||||
if (bits & (1u << (MW - 1 - col)))
|
||||
px (g, cx + col, y + row, intensity);
|
||||
}
|
||||
cx += MW + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void LcdPanel::drawFilled (juce::Graphics& g, int x, int y, int w, int h, float intensity)
|
||||
{
|
||||
for (int yy = 0; yy < h; ++yy)
|
||||
|
|
@ -212,6 +459,12 @@ void LcdPanel::drawFilled (juce::Graphics& g, int x, int y, int w, int h, float
|
|||
px (g, x + xx, y + yy, intensity);
|
||||
}
|
||||
|
||||
// full-width selection bar: lit row behind a dimmed-to-transparent glyph
|
||||
void LcdPanel::drawSelBar (juce::Graphics& g, int y)
|
||||
{
|
||||
drawFilled (g, 2, y, gridW - 4, 6, 0.55f);
|
||||
}
|
||||
|
||||
void LcdPanel::drawHBar (juce::Graphics& g, int x, int y, int w, double level, float intensity)
|
||||
{
|
||||
const int filled = juce::roundToInt (w * juce::jlimit (0.0, 1.0, level));
|
||||
|
|
@ -222,26 +475,23 @@ void LcdPanel::drawFooter (juce::Graphics& g)
|
|||
{
|
||||
juce::String branch = juce::String (BUILD_GIT_BRANCH);
|
||||
if (branch.length() > 6) branch = branch.substring (0, 6);
|
||||
const juce::String git = branch + "@" + juce::String (BUILD_GIT_COMMIT) + juce::String (BUILD_GIT_DIRTY);
|
||||
const juce::String dt = juce::String (BUILD_DATE).substring (5) + " " + juce::String (BUILD_TIME);
|
||||
const juce::String git = branch + "@" + juce::String (BUILD_GIT_COMMIT) + juce::String (BUILD_GIT_DIRTY);
|
||||
|
||||
drawFilled (g, 0, 61, gridW, 1, 0.4f);
|
||||
pxText (g, 2, 63, git, 0.75f);
|
||||
drawFilled (g, 2, 57, gridW - 4, 1, 0.35f);
|
||||
pxTextM (g, 4, 58, git, 0.75f);
|
||||
|
||||
const auto w = dt.length() * (NW + 1) - 1;
|
||||
pxText (g, gridW - w - 2, 63, dt, 0.75f);
|
||||
|
||||
(void) powerGlow;
|
||||
const juce::String dt = juce::String (BUILD_DATE) + " " + juce::String (BUILD_TIME);
|
||||
const auto w = dt.length() * (MW + 1) - 1;
|
||||
pxTextM (g, gridW - w - 4, 58, dt, 0.75f);
|
||||
}
|
||||
|
||||
void LcdPanel::drawSplash (juce::Graphics& g)
|
||||
{
|
||||
pxText (g, 46, 6, "* RMX-19 *", 1.0f);
|
||||
pxText (g, 30, 18, "RACK SYNTHESIZER", 0.9f);
|
||||
drawFilled (g, 10, 30, gridW - 20, 1, 0.5f);
|
||||
pxText (g, 44, 34, "INITIALIZING", 0.8f);
|
||||
pxText (g, 34, 45, "DSP READY", 0.75f);
|
||||
pxText (g, 56, 56, "REV.B", 0.6f);
|
||||
pxTextM (g, 58, 8, "* RMX-19 *", 1.0f);
|
||||
pxTextM (g, 28, 20, "RACK SYNTHESIZER", 0.9f);
|
||||
drawFilled (g, 10, 31, gridW - 20, 1, 0.5f);
|
||||
pxTextM (g, 44, 36, "INITIALIZING", 0.8f);
|
||||
pxTextM (g, 34, 46, "DSP READY", 0.75f);
|
||||
}
|
||||
|
||||
void LcdPanel::drawHome (juce::Graphics& g)
|
||||
|
|
@ -251,86 +501,59 @@ void LcdPanel::drawHome (juce::Graphics& g)
|
|||
|
||||
const juce::String num = juce::String (patch + 1).paddedLeft ('0', 2);
|
||||
pxText (g, 2, 3, num, 1.0f);
|
||||
pxText (g, 4, 15, p.name, 1.0f);
|
||||
pxText (g, 4, 28, p.category, 0.8f);
|
||||
pxText (g, 14, 3, p.name, 1.0f);
|
||||
pxTextM (g, 4, 17, p.category, 0.9f);
|
||||
|
||||
drawFilled (g, 4, 42, 88, 1, 0.5f);
|
||||
drawHBar (g, 4, 43, 88, proc.getActiveVoices() / 16.0, 0.9f);
|
||||
|
||||
pxText (g, 116, 42, "L", 0.6f);
|
||||
pxText (g, 138, 42, "R", 0.6f);
|
||||
drawFilled (g, 122, 40, 1, 8, 0.5f);
|
||||
drawHBar (g, 123, 41, 12, meterL, 1.0f);
|
||||
drawHBar (g, 123, 44, 12, meterR, 1.0f);
|
||||
drawFilled (g, 123, 40, 12, 1, 0.4f);
|
||||
drawFilled (g, 123, 47, 12, 1, 0.4f);
|
||||
drawFilled (g, 4, 28, 88, 1, 0.5f);
|
||||
drawHBar (g, 4, 29, 88, proc.getActiveVoices() / 16.0, 0.9f);
|
||||
}
|
||||
|
||||
void LcdPanel::drawMenu (juce::Graphics& g)
|
||||
{
|
||||
static const char* items[] = {
|
||||
"PATCH SELECT", "OSC SELECT", "FILTER SETUP", "AMP ENVELOPE",
|
||||
"FILT ENVELOPE", "MODULATION", "EFFECTS", "MASTER", "SYSTEM", "ABOUT"
|
||||
"PATCH", "VOICE", "FILTER", "ENVELOPE", "LFO", "FX", "MASTER", "ABOUT"
|
||||
};
|
||||
const int rows = (int) juce::numElementsInArray (items);
|
||||
const int visible = 6;
|
||||
const int top = juce::jmax (0, juce::jmin (cursor - 3, rows - visible));
|
||||
for (int i = 0; i < visible; ++i)
|
||||
for (int i = 0; i < rows; ++i)
|
||||
{
|
||||
const int idx = top + i;
|
||||
if (idx >= rows) break;
|
||||
const int y = 2 + i * 10 + 1;
|
||||
const bool sel = (idx == cursor);
|
||||
pxText (g, 4, y, items[idx], sel ? 1.0f : 0.6f);
|
||||
if (sel)
|
||||
{
|
||||
drawFilled (g, 1, y, 1, NH, 1.0f);
|
||||
pxText (g, 142, y, ">", 1.0f);
|
||||
}
|
||||
const int y = 2 + i * 7;
|
||||
const bool sel = (i == cursor);
|
||||
if (sel) drawSelBar (g, y - 1);
|
||||
pxTextM (g, 6, y, items[i], sel ? 1.0f : 0.72f);
|
||||
}
|
||||
drawFilled (g, 151, 8, 1, 48, 0.4f);
|
||||
const double frac = rows > 1 ? cursor / (double) (rows - 1) : 0.0;
|
||||
const int thumbY = 8 + (int) (frac * 40.0);
|
||||
drawFilled (g, 150, thumbY - 2, 3, 4, 0.8f);
|
||||
pxText (g, 4, 62, "UP/DN MOVE OK OPEN", 0.55f);
|
||||
}
|
||||
|
||||
void LcdPanel::drawPatchList (juce::Graphics& g)
|
||||
{
|
||||
const int bankSize = proc.getBank().size();
|
||||
const int visible = 6;
|
||||
const int top = juce::jmax (0, juce::jmin (patchCursor - 2, bankSize - visible));
|
||||
const int visible = 8;
|
||||
const int top = juce::jmax (0, juce::jmin (patchCursor - 3, bankSize - visible));
|
||||
for (int i = 0; i < visible; ++i)
|
||||
{
|
||||
const int idx = top + i;
|
||||
if (idx >= bankSize) break;
|
||||
const auto& p = proc.getBank().get (idx);
|
||||
const int y = 2 + i * 10 + 1;
|
||||
const int y = 2 + i * 7;
|
||||
const bool sel = (idx == patchCursor);
|
||||
pxText (g, 4, y, juce::String (idx + 1).paddedLeft ('0', 2) + " " + juce::String (p.name),
|
||||
sel ? 1.0f : 0.6f);
|
||||
if (sel) drawFilled (g, 1, y, 1, NH, 1.0f);
|
||||
if (idx == proc.getPatchIndex()) pxText (g, 138, y, "*", 0.9f);
|
||||
if (sel) drawSelBar (g, y - 1);
|
||||
pxTextM (g, 6, y, juce::String (idx + 1).paddedLeft ('0', 2) + " " + juce::String (p.name),
|
||||
sel ? 1.0f : 0.72f);
|
||||
if (idx == proc.getPatchIndex())
|
||||
pxTextM (g, gridW - 7, y, "*", sel ? 1.0f : 0.9f);
|
||||
}
|
||||
drawFilled (g, 140, 8, 1, 48, 0.4f);
|
||||
const double frac = bankSize > 1 ? patchCursor / (double) (bankSize - 1) : 0.0;
|
||||
const int thumbY = 8 + (int) (frac * 40.0);
|
||||
drawFilled (g, 139, thumbY - 2, 3, 4, 0.8f);
|
||||
pxText (g, 4, 62, "UP/DN BROWSE OK LOAD", 0.55f);
|
||||
}
|
||||
|
||||
void LcdPanel::drawParamScreen (juce::Graphics& g)
|
||||
{
|
||||
pxText (g, 2, 2, paramScreenTitle (paramScreenIdx), 1.0f);
|
||||
drawFilled (g, 2, 9, gridW - 4, 1, 0.35f);
|
||||
pxTextM (g, 3, 1, paramScreenTitle (paramScreenIdx), 1.0f);
|
||||
drawFilled (g, 3, 6, gridW - 6, 1, 0.35f);
|
||||
|
||||
static const prm::Id voice[] = { prm::Id::osc1Wave, prm::Id::osc2Wave, prm::Id::detune, prm::Id::oscMix };
|
||||
static const prm::Id filter[] = { prm::Id::fltType, prm::Id::fltCutoff, prm::Id::fltRes, prm::Id::fltEnv, prm::Id::fltKey };
|
||||
static const prm::Id ampEnv[] = { prm::Id::ampA, prm::Id::ampD, prm::Id::ampS, prm::Id::ampR };
|
||||
static const prm::Id fltEnv[] = { prm::Id::fenvA, prm::Id::fenvD, prm::Id::fenvS, prm::Id::fenvR };
|
||||
static const prm::Id mod[] = { prm::Id::lfoRate, prm::Id::lfoShape, prm::Id::lfoDepth, prm::Id::lfoTarget };
|
||||
static const prm::Id envMt[] = { prm::Id::ampA, prm::Id::ampD, prm::Id::ampS, prm::Id::ampR, prm::Id::fenvA, prm::Id::fenvD, prm::Id::fenvS, prm::Id::fenvR };
|
||||
static const prm::Id lfo[] = { prm::Id::lfoRate, prm::Id::lfoShape, prm::Id::lfoDepth, prm::Id::lfoTarget };
|
||||
static const prm::Id fx[] = { prm::Id::fxType, prm::Id::fxAmt, prm::Id::fxTime };
|
||||
static const prm::Id master[] = { prm::Id::vol, prm::Id::pan, prm::Id::poly, prm::Id::glide, prm::Id::tune };
|
||||
static const prm::Id master[] = { prm::Id::vol, prm::Id::pan, prm::Id::poly, prm::Id::glide, prm::Id::tune, prm::Id::hq };
|
||||
|
||||
const prm::Id* rows = nullptr;
|
||||
int count = 0;
|
||||
|
|
@ -338,39 +561,47 @@ void LcdPanel::drawParamScreen (juce::Graphics& g)
|
|||
{
|
||||
case 0: rows = voice; count = (int) juce::numElementsInArray (voice); break;
|
||||
case 1: rows = filter; count = (int) juce::numElementsInArray (filter); break;
|
||||
case 2: rows = ampEnv; count = (int) juce::numElementsInArray (ampEnv); break;
|
||||
case 3: rows = fltEnv; count = (int) juce::numElementsInArray (fltEnv); break;
|
||||
case 4: rows = mod; count = (int) juce::numElementsInArray (mod); break;
|
||||
case 5: rows = fx; count = (int) juce::numElementsInArray (fx); break;
|
||||
case 6: rows = master; count = (int) juce::numElementsInArray (master); break;
|
||||
case 2: rows = envMt; count = (int) juce::numElementsInArray (envMt); break;
|
||||
case 3: rows = lfo; count = (int) juce::numElementsInArray (lfo); break;
|
||||
case 4: rows = fx; count = (int) juce::numElementsInArray (fx); break;
|
||||
case 5: rows = master; count = (int) juce::numElementsInArray (master); break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
const int visible = 5;
|
||||
const int top = juce::jmax (0, juce::jmin (cursor - 2, count - visible));
|
||||
const int visible = 7;
|
||||
const int top = juce::jmax (0, juce::jmin (cursor - 3, count - visible));
|
||||
for (int i = 0; i < visible; ++i)
|
||||
{
|
||||
const int ri = top + i;
|
||||
if (ri >= count) break;
|
||||
|
||||
const int y = 8 + i * 7;
|
||||
const bool sel = (ri == cursor);
|
||||
|
||||
// FX screen has an extra "SYNC" (delay tempo sync) row after the 3 fx params
|
||||
if (paramScreenIdx == 4 && ri >= (int) juce::numElementsInArray (fx))
|
||||
{
|
||||
const int syncIdx = (int) proc.apvts.getRawParameterValue ("delaysync")->load();
|
||||
if (sel) drawSelBar (g, y - 1);
|
||||
pxTextM (g, 6, y, "SYNC", sel ? 1.0f : 0.75f);
|
||||
const juce::String val = prm::delaySyncName (syncIdx);
|
||||
const auto w = val.length() * (MW + 1) - 1;
|
||||
pxTextM (g, gridW - w - 5, y, val, sel ? 1.0f : 0.85f);
|
||||
continue;
|
||||
}
|
||||
|
||||
const prm::Id id = rows[ri];
|
||||
const auto& d = prm::desc (id);
|
||||
const float cur = proc.apvts.getRawParameterValue (d.id)->load();
|
||||
const auto* par = proc.apvts.getParameter (d.id);
|
||||
const float v01 = par->convertTo0to1 (cur);
|
||||
|
||||
const int y = 12 + i * 10;
|
||||
const bool sel = (ri == cursor);
|
||||
pxText (g, 3, y, d.shortName, 0.55f);
|
||||
pxText (g, 22, y, prm::formatParam (id, cur), sel ? 1.0f : 0.7f);
|
||||
drawFilled (g, 22, y + 6, 130, 1, 0.15f);
|
||||
drawHBar (g, 22, y + 6, 130, v01, sel ? 1.0f : 0.5f);
|
||||
if (sel)
|
||||
{
|
||||
drawFilled (g, 0, y, 1, NH, 1.0f);
|
||||
pxText (g, 148, y, "<>", 1.0f);
|
||||
}
|
||||
if (sel) drawSelBar (g, y - 1);
|
||||
|
||||
pxTextM (g, 6, y, d.name, sel ? 1.0f : 0.75f);
|
||||
|
||||
const juce::String val = prm::formatParam (id, cur);
|
||||
const auto w = val.length() * (MW + 1) - 1;
|
||||
pxTextM (g, gridW - w - 5, y, val, sel ? 1.0f : 0.85f);
|
||||
}
|
||||
pxText (g, 4, 62, "LEFT/RIGHT EDIT OK BACK", 0.55f);
|
||||
}
|
||||
|
||||
void LcdPanel::drawSystem (juce::Graphics& g)
|
||||
|
|
@ -378,22 +609,22 @@ void LcdPanel::drawSystem (juce::Graphics& g)
|
|||
static const char* items[] = { "FACTORY INIT", "ALL NOTES OFF", "MIDI TEST", "BACK" };
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
const int y = 2 + i * 10 + 1;
|
||||
const int y = 2 + i * 7;
|
||||
const bool sel = (i == cursor);
|
||||
pxText (g, 4, y, items[i], sel ? 1.0f : 0.6f);
|
||||
if (sel) drawFilled (g, 1, y, 1, NH, 1.0f);
|
||||
if (sel) drawSelBar (g, y - 1);
|
||||
pxTextM (g, 6, y, items[i], sel ? 1.0f : 0.72f);
|
||||
}
|
||||
if (cursor == 2 && powerGlow > 0.5f)
|
||||
pxText (g, 4, 50, "MIDI RX " + juce::String (lastMidi ? "ACTIVE" : "IDLE"), 0.6f);
|
||||
pxTextM (g, 6, 50, "MIDI RX " + juce::String (lastMidi ? "ACTIVE" : "IDLE"), 0.6f);
|
||||
}
|
||||
|
||||
void LcdPanel::drawInfo (juce::Graphics& g)
|
||||
{
|
||||
pxText (g, 2, 3, "RMX-19 RACK SYNTH", 0.9f);
|
||||
pxText (g, 2, 15, "GIT " + juce::String (BUILD_GIT_BRANCH) + " " + BUILD_GIT_COMMIT + BUILD_GIT_DIRTY, 0.85f);
|
||||
pxText (g, 2, 27, "BUILT " + juce::String (BUILD_DATE) + " " + BUILD_TIME, 0.85f);
|
||||
pxText (g, 2, 39, "ARM64 JUCE VERSION " + juce::String (JUCE_MAJOR_VERSION) + "." + juce::String (JUCE_MINOR_VERSION), 0.7f);
|
||||
pxText (g, 2, 51, "MIDI NOTE + CC CONTROL", 0.7f);
|
||||
pxTextM (g, 3, 3, "RMX-19 RACK SYNTH BY A. JENEWEIN", 0.95f);
|
||||
pxTextM (g, 3, 13, "GIT " + juce::String (BUILD_GIT_BRANCH) + " " + juce::String (BUILD_GIT_COMMIT) + BUILD_GIT_DIRTY, 0.85f);
|
||||
pxTextM (g, 3, 23, "BUILT " + juce::String (BUILD_DATE) + " " + BUILD_TIME, 0.85f);
|
||||
pxTextM (g, 3, 33, "ARM64 JUCE " + juce::String (JUCE_MAJOR_VERSION) + "." + juce::String (JUCE_MINOR_VERSION), 0.7f);
|
||||
pxTextM (g, 3, 43, "MIDI NOTE + CC CONTROL", 0.7f);
|
||||
}
|
||||
|
||||
// ---- editing helpers ------------------------------------------------------
|
||||
|
|
@ -402,7 +633,7 @@ int LcdPanel::rowCountForScreen() const
|
|||
{
|
||||
switch (screen)
|
||||
{
|
||||
case Screen::Menu: return 10;
|
||||
case Screen::Menu: return 8;
|
||||
case Screen::System: return 4;
|
||||
default: return 0;
|
||||
}
|
||||
|
|
@ -414,11 +645,10 @@ int LcdPanel::paramCountForScreen (int idx) const
|
|||
{
|
||||
case 0: return 4;
|
||||
case 1: return 5;
|
||||
case 2: return 4;
|
||||
case 2: return 8;
|
||||
case 3: return 4;
|
||||
case 4: return 4;
|
||||
case 5: return 3;
|
||||
case 6: return 5;
|
||||
case 5: return 5;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -427,20 +657,19 @@ const char* LcdPanel::paramScreenTitle (int idx) const
|
|||
{
|
||||
switch (idx)
|
||||
{
|
||||
case 0: return "OSCILLATOR";
|
||||
case 0: return "VOICE";
|
||||
case 1: return "FILTER";
|
||||
case 2: return "AMP ENVELOPE";
|
||||
case 3: return "FILT ENVELOPE";
|
||||
case 4: return "MODULATION";
|
||||
case 5: return "EFFECTS";
|
||||
case 6: return "MASTER";
|
||||
case 2: return "ENVELOPE";
|
||||
case 3: return "LFO";
|
||||
case 4: return "FX";
|
||||
case 5: return "MASTER";
|
||||
default: return "";
|
||||
}
|
||||
}
|
||||
|
||||
void LcdPanel::openParamScreen (int idx)
|
||||
{
|
||||
paramScreenIdx = juce::jlimit (0, 6, idx);
|
||||
paramScreenIdx = juce::jlimit (0, 5, idx);
|
||||
cursor = 0;
|
||||
enterScreen (Screen::Param);
|
||||
}
|
||||
|
|
@ -464,6 +693,17 @@ void LcdPanel::moveCursor (int d)
|
|||
}
|
||||
}
|
||||
|
||||
void LcdPanel::stepDelaySync (int dir)
|
||||
{
|
||||
auto* par = dynamic_cast<juce::AudioParameterInt*> (proc.apvts.getParameter ("delaysync"));
|
||||
if (par == nullptr) return;
|
||||
const int cur = (int) proc.apvts.getRawParameterValue ("delaysync")->load();
|
||||
const int nv = juce::jlimit (0, prm::delaySyncCount - 1, cur + dir);
|
||||
if (nv != cur)
|
||||
par->setValueNotifyingHost (juce::NormalisableRange<double> (0.0, (double) (prm::delaySyncCount - 1)).convertTo0to1 ((double) nv));
|
||||
setDirty();
|
||||
}
|
||||
|
||||
void LcdPanel::stepParam (prm::Id id, int dir, bool coarse)
|
||||
{
|
||||
auto* par = dynamic_cast<juce::AudioParameterFloat*> (proc.apvts.getParameter (prm::desc (id).id));
|
||||
|
|
@ -480,33 +720,56 @@ static prm::Id paramForCursor (int screenIdx, int row)
|
|||
{
|
||||
static const prm::Id voice[] = { prm::Id::osc1Wave, prm::Id::osc2Wave, prm::Id::detune, prm::Id::oscMix };
|
||||
static const prm::Id filter[] = { prm::Id::fltType, prm::Id::fltCutoff, prm::Id::fltRes, prm::Id::fltEnv, prm::Id::fltKey };
|
||||
static const prm::Id ampEnv[] = { prm::Id::ampA, prm::Id::ampD, prm::Id::ampS, prm::Id::ampR };
|
||||
static const prm::Id fltEnv[] = { prm::Id::fenvA, prm::Id::fenvD, prm::Id::fenvS, prm::Id::fenvR };
|
||||
static const prm::Id mod[] = { prm::Id::lfoRate, prm::Id::lfoShape, prm::Id::lfoDepth, prm::Id::lfoTarget };
|
||||
static const prm::Id envMt[] = { prm::Id::ampA, prm::Id::ampD, prm::Id::ampS, prm::Id::ampR, prm::Id::fenvA, prm::Id::fenvD, prm::Id::fenvS, prm::Id::fenvR };
|
||||
static const prm::Id lfo[] = { prm::Id::lfoRate, prm::Id::lfoShape, prm::Id::lfoDepth, prm::Id::lfoTarget };
|
||||
static const prm::Id fx[] = { prm::Id::fxType, prm::Id::fxAmt, prm::Id::fxTime };
|
||||
static const prm::Id master[] = { prm::Id::vol, prm::Id::pan, prm::Id::poly, prm::Id::glide, prm::Id::tune };
|
||||
static const prm::Id master[] = { prm::Id::vol, prm::Id::pan, prm::Id::poly, prm::Id::glide, prm::Id::tune, prm::Id::hq };
|
||||
|
||||
switch (screenIdx)
|
||||
{
|
||||
case 0: return voice[row];
|
||||
case 1: return filter[row];
|
||||
case 2: return ampEnv[row];
|
||||
case 3: return fltEnv[row];
|
||||
case 4: return mod[row];
|
||||
case 5: return fx[row];
|
||||
case 6: return master[row];
|
||||
case 2: return envMt[row];
|
||||
case 3: return lfo[row];
|
||||
case 4: return fx[row];
|
||||
case 5: return master[row];
|
||||
default: return prm::Id::vol;
|
||||
}
|
||||
}
|
||||
|
||||
void LcdPanel::adjustCurrent (int dir, bool coarse)
|
||||
{
|
||||
if (screen == Screen::Param)
|
||||
stepParam (paramForCursor (paramScreenIdx, cursor), dir, coarse);
|
||||
if (screen != Screen::Param) return;
|
||||
// FX screen: row 3 is the delay-sync selector (not a prm::Id)
|
||||
if (paramScreenIdx == 4 && cursor >= 3)
|
||||
{
|
||||
stepDelaySync (dir);
|
||||
return;
|
||||
}
|
||||
stepParam (paramForCursor (paramScreenIdx, cursor), dir, coarse);
|
||||
}
|
||||
|
||||
void LcdPanel::navUp() { moveCursor (-1); }
|
||||
void LcdPanel::navDown() { moveCursor (1); }
|
||||
void LcdPanel::selectPatchCyclic (int dir)
|
||||
{
|
||||
const int n = proc.getBank().size();
|
||||
if (n <= 0) return;
|
||||
const int next = (proc.getPatchIndex() + dir + n) % n;
|
||||
if (next != proc.getPatchIndex())
|
||||
proc.selectPatch (next);
|
||||
setDirty();
|
||||
}
|
||||
|
||||
void LcdPanel::navUp() { if (screen == Screen::Home) { selectPatchCyclic (-1); return; } moveCursor (-1); applyPatchOnBrowserMove(); }
|
||||
void LcdPanel::navDown() { if (screen == Screen::Home) { selectPatchCyclic (1); return; } moveCursor (1); applyPatchOnBrowserMove(); }
|
||||
|
||||
void LcdPanel::applyPatchOnBrowserMove()
|
||||
{
|
||||
if (screen == Screen::Patch)
|
||||
{
|
||||
proc.selectPatch (patchCursor);
|
||||
setDirty();
|
||||
}
|
||||
}
|
||||
void LcdPanel::navLeft() { adjustCurrent (-1, false); }
|
||||
void LcdPanel::navRight() { adjustCurrent (1, false); }
|
||||
|
||||
|
|
@ -526,15 +789,13 @@ void LcdPanel::navEnter()
|
|||
case 4: openParamScreen (3); break;
|
||||
case 5: openParamScreen (4); break;
|
||||
case 6: openParamScreen (5); break;
|
||||
case 7: openParamScreen (6); break;
|
||||
case 8: enterScreen (Screen::System); break;
|
||||
case 9: enterScreen (Screen::Info); break;
|
||||
case 7: enterScreen (Screen::Info); break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
case Screen::Patch:
|
||||
proc.selectPatch (patchCursor);
|
||||
enterScreen (Screen::Home);
|
||||
enterScreen (Screen::Menu);
|
||||
break;
|
||||
case Screen::System:
|
||||
switch (cursor)
|
||||
|
|
@ -542,14 +803,17 @@ void LcdPanel::navEnter()
|
|||
case 0: proc.getBank().writeTo (0, proc.apvts); proc.selectPatch (0); break;
|
||||
case 1: proc.getEngine().allNotesOff (true); break;
|
||||
case 2: break;
|
||||
case 3: enterScreen (Screen::Home); break;
|
||||
case 3: enterScreen (Screen::Menu); break;
|
||||
default: break;
|
||||
}
|
||||
break;
|
||||
case Screen::Info:
|
||||
case Screen::Param:
|
||||
break; // stay in the submenu; left/right edits the highlighted param
|
||||
case Screen::Info:
|
||||
enterScreen (Screen::Menu);
|
||||
break;
|
||||
default:
|
||||
enterScreen (Screen::Home);
|
||||
enterScreen (Screen::Menu);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue