mirror of
https://codeberg.org/armin/rmx-19.git
synced 2026-10-11 16:51:55 +02:00
re-structure menu, darken UI
This commit is contained in:
parent
b03dc2e0c4
commit
4b44841049
16 changed files with 1217 additions and 304 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -2,3 +2,7 @@ build/
|
||||||
*.vst3/
|
*.vst3/
|
||||||
*.component/
|
*.component/
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
*.kra
|
||||||
|
*autosave*
|
||||||
|
*.swp
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "LcdPanel.h"
|
#include "LcdPanel.h"
|
||||||
#include "BuildInfo.h"
|
#include "BuildInfo.h"
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// 5x7 dot-matrix font. Each glyph is 5 columns x 7 rows, bit 6 (0x40) of each
|
// 5x7 dot-matrix font. Each glyph is 5 columns x 7 rows, bit 6 (0x40) of each
|
||||||
|
|
@ -180,9 +181,9 @@ const juce::uint8* LcdPanel::glyphFor (int key)
|
||||||
LcdPanel::LcdPanel (RMX19AudioProcessor& p)
|
LcdPanel::LcdPanel (RMX19AudioProcessor& p)
|
||||||
: proc (p)
|
: proc (p)
|
||||||
{
|
{
|
||||||
dotImage = juce::Image (juce::Image::ARGB, gridW, gridH, false);
|
displayImage = juce::Image (juce::Image::ARGB, gridW * subsample, gridH * subsample, false);
|
||||||
displayImage = juce::Image (juce::Image::ARGB, gridW * 2, gridH * 2, false);
|
presentImage = juce::Image (juce::Image::ARGB, gridW * presentScale, gridH * presentScale, false);
|
||||||
backdrop = juce::Image (juce::Image::ARGB, gridW * 2, gridH * 2, false);
|
backdrop = juce::Image (juce::Image::ARGB, gridW * subsample, gridH * subsample, false);
|
||||||
buildBackdrop();
|
buildBackdrop();
|
||||||
splashUntil = juce::Time::getMillisecondCounter() + 1500;
|
splashUntil = juce::Time::getMillisecondCounter() + 1500;
|
||||||
lastMidi = proc.getLastMidiTime();
|
lastMidi = proc.getLastMidiTime();
|
||||||
|
|
@ -216,12 +217,8 @@ void LcdPanel::timerCallback()
|
||||||
|
|
||||||
if (screen == Screen::Home)
|
if (screen == Screen::Home)
|
||||||
{
|
{
|
||||||
const int v = proc.getActiveVoices();
|
lastVoices = proc.getActiveVoices();
|
||||||
if (v != lastVoices)
|
needsRedraw = true; // drive the MIDI blip + VU meter
|
||||||
{
|
|
||||||
lastVoices = v;
|
|
||||||
needsRedraw = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needsRedraw)
|
if (needsRedraw)
|
||||||
|
|
@ -239,13 +236,9 @@ void LcdPanel::paint (juce::Graphics& g)
|
||||||
g.drawRect (b, 2);
|
g.drawRect (b, 2);
|
||||||
|
|
||||||
// inner LCD glass (black)
|
// inner LCD glass (black)
|
||||||
const int cell = 2;
|
const auto rect = lcdImageRect();
|
||||||
const int w = gridW * cell;
|
|
||||||
const int h = gridH * cell;
|
|
||||||
const int ox = (b.getWidth() - w) / 2;
|
|
||||||
const int oy = (b.getHeight() - h) / 2 + 2;
|
|
||||||
g.setColour (juce::Colour (0xff020703));
|
g.setColour (juce::Colour (0xff020703));
|
||||||
g.fillRect (ox, oy, w, h);
|
g.fillRect (rect);
|
||||||
|
|
||||||
if (contentDirty)
|
if (contentDirty)
|
||||||
{
|
{
|
||||||
|
|
@ -253,7 +246,56 @@ void LcdPanel::paint (juce::Graphics& g)
|
||||||
contentDirty = false;
|
contentDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
g.drawImageAt (displayImage, ox, oy);
|
g.drawImage (presentImage,
|
||||||
|
rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight(),
|
||||||
|
0, 0, presentImage.getWidth(), presentImage.getHeight());
|
||||||
|
}
|
||||||
|
|
||||||
|
// the on-screen rectangle (component coords) the dot-matrix image occupies:
|
||||||
|
// exactly the presentation pixels, centered inside the LCD module (with a small
|
||||||
|
// extra downward shift to match the bezel). No scaling is applied so the dot
|
||||||
|
// matrix keeps its intended physical size.
|
||||||
|
juce::Rectangle<int> LcdPanel::lcdImageRect() const
|
||||||
|
{
|
||||||
|
const int w = presentImage.getWidth();
|
||||||
|
const int h = presentImage.getHeight();
|
||||||
|
const auto b = getLocalBounds();
|
||||||
|
return { (b.getWidth() - w) / 2, (b.getHeight() - h) / 2 + 2, w, h };
|
||||||
|
}
|
||||||
|
|
||||||
|
void LcdPanel::mouseDown (const juce::MouseEvent& e)
|
||||||
|
{
|
||||||
|
if (screen != Screen::Home)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// map the click into logical dot-matrix coordinates
|
||||||
|
const auto rect = lcdImageRect();
|
||||||
|
const float lx = (e.x - rect.getX()) * (float) gridW / (float) rect.getWidth();
|
||||||
|
const float ly = (e.y - rect.getY()) * (float) gridH / (float) rect.getHeight();
|
||||||
|
|
||||||
|
// patch number/name row on the home screen (5x7 font at y=3), shown as "<num>: <name>"
|
||||||
|
const auto& cur = proc.getBank().get (proc.getPatchIndex());
|
||||||
|
const int nameEnd = 2 + (4 + (int) juce::String (cur.name).length()) * (NW + 1) + 2;
|
||||||
|
if (ly < 0.0f || ly >= 11.0f) return;
|
||||||
|
if (lx < 0.0f || lx >= (float) nameEnd) return;
|
||||||
|
|
||||||
|
// native mouse menu: one submenu per category, ticking the loaded patch
|
||||||
|
juce::PopupMenu menu;
|
||||||
|
const auto& cats = proc.getBank().categories();
|
||||||
|
for (size_t c = 0; c < cats.names.size(); ++c)
|
||||||
|
{
|
||||||
|
juce::PopupMenu sub;
|
||||||
|
for (int pi : cats.patchIndices[(size_t) c])
|
||||||
|
{
|
||||||
|
const bool isCurrent = (pi == proc.getPatchIndex());
|
||||||
|
sub.addItem (proc.getBank().get (pi).name, true, isCurrent,
|
||||||
|
[this, pi] { proc.selectPatch (pi); setDirty(); });
|
||||||
|
}
|
||||||
|
menu.addSubMenu (cats.names[(size_t) c], sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
menu.showMenuAsync (juce::PopupMenu::Options().withTargetScreenArea (
|
||||||
|
juce::Rectangle<int> (e.getScreenX(), e.getScreenY(), 1, 1)));
|
||||||
}
|
}
|
||||||
|
|
||||||
void LcdPanel::buildBackdrop()
|
void LcdPanel::buildBackdrop()
|
||||||
|
|
@ -268,8 +310,9 @@ void LcdPanel::buildBackdrop()
|
||||||
// even, faint backlight (brightest toward the middle of the glass)
|
// even, faint backlight (brightest toward the middle of the glass)
|
||||||
{
|
{
|
||||||
const float cx = W * 0.5f, cy = H * 0.5f;
|
const float cx = W * 0.5f, cy = H * 0.5f;
|
||||||
juce::ColourGradient backlight (juce::Colour::fromRGBA (0x28, 0x5a, 0x3c, 22), cx, cy,
|
const float radius = std::min (W, H) * 2.5f;
|
||||||
juce::Colour::fromRGBA (0x18, 0x2e, 0x20, 0), W * 0.38f, cy, false);
|
juce::ColourGradient backlight (juce::Colour::fromRGBA (0x40, 0x80, 0x50, 40), cx, cy,
|
||||||
|
juce::Colour::fromRGBA (0x20, 0x40, 0x28, 0), cx + radius, cy, true);
|
||||||
dg.setGradientFill (backlight);
|
dg.setGradientFill (backlight);
|
||||||
dg.fillRect (backdrop.getBounds());
|
dg.fillRect (backdrop.getBounds());
|
||||||
}
|
}
|
||||||
|
|
@ -301,31 +344,25 @@ void LcdPanel::buildBackdrop()
|
||||||
dg.setGradientFill (eR); dg.fillRect (W - 10, 0, 10, H);
|
dg.setGradientFill (eR); dg.fillRect (W - 10, 0, 10, H);
|
||||||
}
|
}
|
||||||
|
|
||||||
// faint "ghost" dot for every (unlit) segment, exactly like a passive matrix LCD
|
// faint "ghost" dot for every (unlit) segment, exactly like a passive matrix LCD.
|
||||||
|
// Alpha is boosted since the 2:1 downsample to the presentation image halves it.
|
||||||
for (int y = 0; y < gridH; ++y)
|
for (int y = 0; y < gridH; ++y)
|
||||||
for (int x = 0; x < gridW; ++x)
|
for (int x = 0; x < gridW; ++x)
|
||||||
{
|
{
|
||||||
const float cx = x * 2.0f + 1.0f;
|
const float gx = (x * (float) subsample) + subsample * 0.5f;
|
||||||
const float cy = y * 2.0f + 1.0f;
|
const float gy = (y * (float) subsample) + subsample * 0.5f;
|
||||||
dg.setColour (juce::Colour::fromRGBA (0x00, 0x88, 0x55, 6));
|
const float r = subsample * 0.32f;
|
||||||
dg.fillEllipse (cx - 0.55f, cy - 0.55f, 1.1f, 1.1f);
|
dg.setColour (juce::Colour::fromRGBA (0x00, 0x88, 0x55, 14));
|
||||||
|
dg.fillEllipse (gx - r, gy - r, r * 2.0f, r * 2.0f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LcdPanel::rebuild()
|
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);
|
|
||||||
drawContent (dg);
|
|
||||||
}
|
|
||||||
|
|
||||||
const int W = displayImage.getWidth();
|
const int W = displayImage.getWidth();
|
||||||
const int H = displayImage.getHeight();
|
const int H = displayImage.getHeight();
|
||||||
|
|
||||||
// start from the pre-rendered base layer (backlight, sheen, vignette, ghost dots)
|
// start from the pre-rendered 3D glass base (backlight, sheen, vignette, ghost dots)
|
||||||
{
|
{
|
||||||
juce::Image::BitmapData src (backdrop, juce::Image::BitmapData::readOnly);
|
juce::Image::BitmapData src (backdrop, juce::Image::BitmapData::readOnly);
|
||||||
juce::Image::BitmapData dst (displayImage, juce::Image::BitmapData::writeOnly);
|
juce::Image::BitmapData dst (displayImage, juce::Image::BitmapData::writeOnly);
|
||||||
|
|
@ -333,63 +370,41 @@ void LcdPanel::rebuild()
|
||||||
std::memcpy (dst.getLinePointer (y), src.getLinePointer (y), (size_t) W * 4u);
|
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.
|
// paint the content dots directly onto the glass base; px() stamps a rounded,
|
||||||
// The core is near-solid so glyphs stay sharp; the glow is faint and tight
|
// anti-aliased dot for every lit segment
|
||||||
// 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::Graphics dg (displayImage);
|
||||||
juce::Image::BitmapData out (displayImage, juce::Image::BitmapData::readWrite);
|
drawContent (dg);
|
||||||
|
}
|
||||||
|
|
||||||
for (int cy = 0; cy < gridH; ++cy)
|
// exact integer box downsample (2:1) to the presentation resolution. Because
|
||||||
{
|
// each dot is still drawn large enough to touch its neighbours only at the
|
||||||
for (int cx = 0; cx < gridW; ++cx)
|
// final 2px/dot image, text reads as crisp, distinct dots.
|
||||||
|
const int PW = presentImage.getWidth(), PH = presentImage.getHeight();
|
||||||
|
const int kx = W / PW, ky = H / PH, n = kx * ky;
|
||||||
|
{
|
||||||
|
juce::Image::BitmapData src (displayImage, juce::Image::BitmapData::readOnly);
|
||||||
|
juce::Image::BitmapData dst (presentImage, juce::Image::BitmapData::writeOnly);
|
||||||
|
for (int py = 0; py < PH; ++py)
|
||||||
|
for (int px = 0; px < PW; ++px)
|
||||||
{
|
{
|
||||||
const float lvl = cells.getPixelColour (cx, cy).getAlpha() / 255.0f;
|
unsigned r = 0, g = 0, b = 0, a = 0;
|
||||||
if (lvl <= 0.02f) continue;
|
for (int yy = 0; yy < ky; ++yy)
|
||||||
|
|
||||||
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;
|
const juce::PixelARGB* srcRow =
|
||||||
for (int px = x0; px <= x1; ++px)
|
reinterpret_cast<const juce::PixelARGB*> (src.getPixelPointer (px * kx, py * ky + yy));
|
||||||
|
for (int xx = 0; xx < kx; ++xx)
|
||||||
{
|
{
|
||||||
if (px < 0 || px >= W) continue;
|
const juce::PixelARGB p = srcRow[xx];
|
||||||
|
r += p.getRed(); g += p.getGreen();
|
||||||
const float dx = (px - ccx) * 0.5f;
|
b += p.getBlue(); a += p.getAlpha();
|
||||||
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)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
juce::PixelARGB* dstRow =
|
||||||
|
reinterpret_cast<juce::PixelARGB*> (dst.getPixelPointer (px, py));
|
||||||
|
*dstRow = juce::PixelARGB ((juce::uint8) (a / n), (juce::uint8) (r / n),
|
||||||
|
(juce::uint8) (g / n), (juce::uint8) (b / n));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -397,23 +412,31 @@ void LcdPanel::drawContent (juce::Graphics& g)
|
||||||
{
|
{
|
||||||
switch (screen)
|
switch (screen)
|
||||||
{
|
{
|
||||||
case Screen::Splash: drawSplash (g); break;
|
case Screen::Splash: drawSplash (g); break;
|
||||||
case Screen::Home: drawHome (g); break;
|
case Screen::Home: drawHome (g); break;
|
||||||
case Screen::Menu: drawMenu (g); break;
|
case Screen::Menu: drawMenu (g); break;
|
||||||
case Screen::Patch: drawPatchList (g); break;
|
case Screen::Patches: drawPatches (g); break;
|
||||||
case Screen::Param: drawParamScreen (g); break;
|
case Screen::Patch: drawPatchList (g); break;
|
||||||
case Screen::System: drawSystem (g); break;
|
case Screen::Param: drawParamScreen (g); break;
|
||||||
case Screen::Info: drawInfo (g); break;
|
case Screen::System: drawSystem (g); break;
|
||||||
|
case Screen::Info: drawInfo (g); break;
|
||||||
|
case Screen::Category: drawCategory (g); break; // legacy
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stamp one lit segment as a solid full-cell dot at subsample resolution.
|
||||||
|
// Drawing the whole 4x4 subsample cell solid (instead of an anti-aliased circle)
|
||||||
|
// makes the 2:1 box-downsample produce a perfectly sharp 2px/dot block at full
|
||||||
|
// brightness. An AA circle here lands centred exactly on the corner of the four
|
||||||
|
// output pixels and gets smeared to ~half brightness — that read as soft text.
|
||||||
void LcdPanel::px (juce::Graphics& g, int x, int y, float intensity)
|
void LcdPanel::px (juce::Graphics& g, int x, int y, float intensity)
|
||||||
{
|
{
|
||||||
if (x < 0 || y < 0 || x >= gridW || y >= gridH) return;
|
if (x < 0 || y < 0 || x >= gridW || y >= gridH) return;
|
||||||
if (intensity <= 0.0f) return;
|
if (intensity <= 0.0f) return;
|
||||||
const int a = (int) (255.0f * juce::jlimit (0.0f, 1.0f, intensity));
|
const float s = (float) subsample;
|
||||||
g.setColour (juce::Colour::fromRGBA (255, 255, 255, a));
|
g.setColour (juce::Colour::fromRGBA (
|
||||||
g.fillRect (x, y, 1, 1);
|
0xa0, 0xff, 0xc0, (juce::uint8) (255.0f * juce::jlimit (0.0f, 1.0f, intensity))));
|
||||||
|
g.fillRect (x * s, y * s, s, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
void LcdPanel::pxText (juce::Graphics& g, int x, int y, const juce::String& text, float intensity)
|
void LcdPanel::pxText (juce::Graphics& g, int x, int y, const juce::String& text, float intensity)
|
||||||
|
|
@ -500,18 +523,46 @@ void LcdPanel::drawHome (juce::Graphics& g)
|
||||||
const auto& p = proc.getBank().get (patch);
|
const auto& p = proc.getBank().get (patch);
|
||||||
|
|
||||||
const juce::String num = juce::String (patch + 1).paddedLeft ('0', 2);
|
const juce::String num = juce::String (patch + 1).paddedLeft ('0', 2);
|
||||||
pxText (g, 2, 3, num, 1.0f);
|
pxText (g, 2, 3, num + ": " + juce::String (p.name), 1.0f);
|
||||||
pxText (g, 14, 3, p.name, 1.0f);
|
|
||||||
pxTextM (g, 4, 17, p.category, 0.9f);
|
pxTextM (g, 4, 17, p.category, 0.9f);
|
||||||
|
|
||||||
drawFilled (g, 4, 28, 88, 1, 0.5f);
|
drawFilled (g, 4, 28, 88, 1, 0.5f);
|
||||||
drawHBar (g, 4, 29, 88, proc.getActiveVoices() / 16.0, 0.9f);
|
drawHBar (g, 4, 29, 88, proc.getActiveVoices() / 16.0, 0.9f);
|
||||||
|
|
||||||
|
// arp indicator on home
|
||||||
|
if (auto* arpPar = proc.apvts.getRawParameterValue ("arpon"))
|
||||||
|
{
|
||||||
|
if (arpPar->load() >= 0.5f)
|
||||||
|
{
|
||||||
|
const int rateIdx = (int) proc.apvts.getRawParameterValue ("arprate")->load();
|
||||||
|
const int patIdx = (int) proc.apvts.getRawParameterValue ("arppattern")->load();
|
||||||
|
const juce::String arpTxt = "ARP " + juce::String (prm::arpRateName (rateIdx))
|
||||||
|
+ " " + juce::String (prm::arpPatternName (patIdx));
|
||||||
|
pxTextM (g, 96, 17, arpTxt, 0.85f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// bottom status strip: MIDI input blip + output VU meter
|
||||||
|
pxTextM (g, 4, 58, "MIDI IN", 0.6f);
|
||||||
|
drawFilled (g, 35, 60, 2, 3, juce::jmax (0.15f, powerGlow));
|
||||||
|
|
||||||
|
pxTextM (g, 50, 58, "OUT", 0.6f);
|
||||||
|
const int vw = 84;
|
||||||
|
const int vx = 68;
|
||||||
|
const int filled = juce::roundToInt (vw * juce::jlimit (0.0f, 1.0f, proc.getOutputLevel()));
|
||||||
|
if (filled > 0)
|
||||||
|
{
|
||||||
|
const int warm = juce::jmin (filled, vw / 2);
|
||||||
|
drawFilled (g, vx, 60, warm, 2, 0.65f);
|
||||||
|
drawFilled (g, vx + warm, 60, filled - warm, 2, 1.0f);
|
||||||
|
drawFilled (g, vx + filled - 1, 62, 1, 1, 0.4f); // leading-edge tick
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LcdPanel::drawMenu (juce::Graphics& g)
|
void LcdPanel::drawMenu (juce::Graphics& g)
|
||||||
{
|
{
|
||||||
static const char* items[] = {
|
static const char* items[] = {
|
||||||
"PATCH", "VOICE", "FILTER", "ENVELOPE", "LFO", "FX", "MASTER", "ABOUT"
|
"PATCHES", "VOICE", "FILTER", "ENVELOPE", "LFO", "FX", "ARP", "MASTER", "ABOUT"
|
||||||
};
|
};
|
||||||
const int rows = (int) juce::numElementsInArray (items);
|
const int rows = (int) juce::numElementsInArray (items);
|
||||||
for (int i = 0; i < rows; ++i)
|
for (int i = 0; i < rows; ++i)
|
||||||
|
|
@ -523,22 +574,67 @@ void LcdPanel::drawMenu (juce::Graphics& g)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LcdPanel::drawPatchList (juce::Graphics& g)
|
void LcdPanel::drawCategory (juce::Graphics& g)
|
||||||
{
|
{
|
||||||
const int bankSize = proc.getBank().size();
|
const auto& cats = proc.getBank().categories();
|
||||||
|
const int count = (int) cats.names.size() + 1; // +1 = SETTINGS row
|
||||||
const int visible = 8;
|
const int visible = 8;
|
||||||
const int top = juce::jmax (0, juce::jmin (patchCursor - 3, bankSize - visible));
|
const int top = juce::jmax (0, juce::jmin (catCursor - 3, count - visible));
|
||||||
for (int i = 0; i < visible; ++i)
|
for (int i = 0; i < visible; ++i)
|
||||||
{
|
{
|
||||||
const int idx = top + i;
|
const int idx = top + i;
|
||||||
if (idx >= bankSize) break;
|
if (idx >= count) break;
|
||||||
const auto& p = proc.getBank().get (idx);
|
const int y = 2 + i * 7;
|
||||||
|
const bool sel = (idx == catCursor);
|
||||||
|
if (sel) drawSelBar (g, y - 1);
|
||||||
|
if (idx == (int) cats.names.size())
|
||||||
|
{
|
||||||
|
pxTextM (g, 6, y, "SETTINGS", sel ? 1.0f : 0.72f);
|
||||||
|
drawFilled (g, 6, y - 1, gridW - 12, 1, 0.30f);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pxTextM (g, 6, y, cats.names[(size_t) idx], sel ? 1.0f : 0.72f);
|
||||||
|
const int pcount = (int) cats.patchIndices[(size_t) idx].size();
|
||||||
|
pxTextM (g, gridW - 16, y, juce::String (pcount), sel ? 0.85f : 0.55f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LcdPanel::drawPatches (juce::Graphics& g)
|
||||||
|
{
|
||||||
|
const auto& cats = proc.getBank().categories();
|
||||||
|
const int count = (int) cats.names.size();
|
||||||
|
const int visible = 8;
|
||||||
|
const int top = juce::jmax (0, juce::jmin (catCursor - 3, count - visible));
|
||||||
|
for (int i = 0; i < visible; ++i)
|
||||||
|
{
|
||||||
|
const int idx = top + i;
|
||||||
|
if (idx >= count) break;
|
||||||
|
const int y = 2 + i * 7;
|
||||||
|
const bool sel = (idx == catCursor);
|
||||||
|
if (sel) drawSelBar (g, y - 1);
|
||||||
|
pxTextM (g, 6, y, cats.names[(size_t) idx], sel ? 1.0f : 0.72f);
|
||||||
|
const int pcount = (int) cats.patchIndices[(size_t) idx].size();
|
||||||
|
pxTextM (g, gridW - 16, y, juce::String (pcount), sel ? 0.85f : 0.55f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LcdPanel::drawPatchList (juce::Graphics& g)
|
||||||
|
{
|
||||||
|
const int count = patchCountForCurrentView();
|
||||||
|
const int visible = 8;
|
||||||
|
const int top = juce::jmax (0, juce::jmin (patchCursor - 3, count - visible));
|
||||||
|
for (int i = 0; i < visible; ++i)
|
||||||
|
{
|
||||||
|
const int idx = top + i;
|
||||||
|
if (idx >= count) break;
|
||||||
|
const int pi = patchIndexAtCursor (idx);
|
||||||
|
const auto& p = proc.getBank().get (pi);
|
||||||
const int y = 2 + i * 7;
|
const int y = 2 + i * 7;
|
||||||
const bool sel = (idx == patchCursor);
|
const bool sel = (idx == patchCursor);
|
||||||
if (sel) drawSelBar (g, y - 1);
|
if (sel) drawSelBar (g, y - 1);
|
||||||
pxTextM (g, 6, y, juce::String (idx + 1).paddedLeft ('0', 2) + " " + juce::String (p.name),
|
pxTextM (g, 6, y, juce::String (idx + 1).paddedLeft ('0', 2) + " " + juce::String (p.name),
|
||||||
sel ? 1.0f : 0.72f);
|
sel ? 1.0f : 0.72f);
|
||||||
if (idx == proc.getPatchIndex())
|
if (pi == proc.getPatchIndex())
|
||||||
pxTextM (g, gridW - 7, y, "*", sel ? 1.0f : 0.9f);
|
pxTextM (g, gridW - 7, y, "*", sel ? 1.0f : 0.9f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -553,6 +649,7 @@ void LcdPanel::drawParamScreen (juce::Graphics& g)
|
||||||
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 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 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 fx[] = { prm::Id::fxType, prm::Id::fxAmt, prm::Id::fxTime };
|
||||||
|
static const prm::Id arp[] = { prm::Id::arpOn, prm::Id::arpRate, prm::Id::arpPattern, prm::Id::arpOct, prm::Id::arpGate };
|
||||||
static const prm::Id master[] = { prm::Id::vol, prm::Id::pan, prm::Id::poly, prm::Id::glide, prm::Id::tune, prm::Id::hq };
|
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;
|
const prm::Id* rows = nullptr;
|
||||||
|
|
@ -564,7 +661,8 @@ void LcdPanel::drawParamScreen (juce::Graphics& g)
|
||||||
case 2: rows = envMt; count = (int) juce::numElementsInArray (envMt); break;
|
case 2: rows = envMt; count = (int) juce::numElementsInArray (envMt); break;
|
||||||
case 3: rows = lfo; count = (int) juce::numElementsInArray (lfo); break;
|
case 3: rows = lfo; count = (int) juce::numElementsInArray (lfo); break;
|
||||||
case 4: rows = fx; count = (int) juce::numElementsInArray (fx); break;
|
case 4: rows = fx; count = (int) juce::numElementsInArray (fx); break;
|
||||||
case 5: rows = master; count = (int) juce::numElementsInArray (master); break;
|
case 5: rows = arp; count = (int) juce::numElementsInArray (arp); break;
|
||||||
|
case 6: rows = master; count = (int) juce::numElementsInArray (master); break;
|
||||||
default: return;
|
default: return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -633,8 +731,10 @@ int LcdPanel::rowCountForScreen() const
|
||||||
{
|
{
|
||||||
switch (screen)
|
switch (screen)
|
||||||
{
|
{
|
||||||
case Screen::Menu: return 8;
|
case Screen::Menu: return 9;
|
||||||
case Screen::System: return 4;
|
case Screen::Patches: return proc.getBank().categories().names.size();
|
||||||
|
case Screen::System: return 4;
|
||||||
|
case Screen::Category: return proc.getBank().categories().names.size() + 1; // +1 = SETTINGS row
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -647,8 +747,9 @@ int LcdPanel::paramCountForScreen (int idx) const
|
||||||
case 1: return 5;
|
case 1: return 5;
|
||||||
case 2: return 8;
|
case 2: return 8;
|
||||||
case 3: return 4;
|
case 3: return 4;
|
||||||
case 4: return 4;
|
case 4: return 4; // 3 FX params + SYNC row
|
||||||
case 5: return 5;
|
case 5: return 5; // ARP: on, rate, pattern, oct, gate
|
||||||
|
case 6: return 6;
|
||||||
default: return 0;
|
default: return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -662,33 +763,56 @@ const char* LcdPanel::paramScreenTitle (int idx) const
|
||||||
case 2: return "ENVELOPE";
|
case 2: return "ENVELOPE";
|
||||||
case 3: return "LFO";
|
case 3: return "LFO";
|
||||||
case 4: return "FX";
|
case 4: return "FX";
|
||||||
case 5: return "MASTER";
|
case 5: return "ARP";
|
||||||
|
case 6: return "MASTER";
|
||||||
default: return "";
|
default: return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LcdPanel::openParamScreen (int idx)
|
void LcdPanel::openParamScreen (int idx)
|
||||||
{
|
{
|
||||||
paramScreenIdx = juce::jlimit (0, 5, idx);
|
paramScreenIdx = juce::jlimit (0, 6, idx);
|
||||||
cursor = 0;
|
cursor = 0;
|
||||||
enterScreen (Screen::Param);
|
enterScreen (Screen::Param);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// cyclic cursor step: wrap around the list instead of clamping at the ends
|
||||||
|
static int cyclicStep (int cur, int count, int dir)
|
||||||
|
{
|
||||||
|
if (count <= 0) return 0;
|
||||||
|
int n = (cur + dir) % count;
|
||||||
|
if (n < 0) n += count;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
void LcdPanel::moveCursor (int d)
|
void LcdPanel::moveCursor (int d)
|
||||||
{
|
{
|
||||||
if (screen == Screen::Patch)
|
if (screen == Screen::Patches)
|
||||||
{
|
{
|
||||||
patchCursor = juce::jlimit (0, proc.getBank().size() - 1, patchCursor + d);
|
const int count = proc.getBank().categories().names.size();
|
||||||
|
catCursor = cyclicStep (catCursor, count, d);
|
||||||
|
setDirty();
|
||||||
|
}
|
||||||
|
else if (screen == Screen::Category)
|
||||||
|
{
|
||||||
|
const int count = proc.getBank().categories().names.size() + 1; // +1 = SETTINGS row
|
||||||
|
catCursor = cyclicStep (catCursor, count, d);
|
||||||
|
setDirty();
|
||||||
|
}
|
||||||
|
else if (screen == Screen::Patch)
|
||||||
|
{
|
||||||
|
const int count = patchCountForCurrentView();
|
||||||
|
patchCursor = cyclicStep (patchCursor, count, d);
|
||||||
setDirty();
|
setDirty();
|
||||||
}
|
}
|
||||||
else if (screen == Screen::Param)
|
else if (screen == Screen::Param)
|
||||||
{
|
{
|
||||||
cursor = juce::jlimit (0, paramCountForScreen (paramScreenIdx) - 1, cursor + d);
|
cursor = cyclicStep (cursor, paramCountForScreen (paramScreenIdx), d);
|
||||||
setDirty();
|
setDirty();
|
||||||
}
|
}
|
||||||
else if (rowCountForScreen() > 0)
|
else if (rowCountForScreen() > 0)
|
||||||
{
|
{
|
||||||
cursor = juce::jlimit (0, rowCountForScreen() - 1, cursor + d);
|
cursor = cyclicStep (cursor, rowCountForScreen(), d);
|
||||||
setDirty();
|
setDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -723,6 +847,7 @@ static prm::Id paramForCursor (int screenIdx, int row)
|
||||||
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 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 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 fx[] = { prm::Id::fxType, prm::Id::fxAmt, prm::Id::fxTime };
|
||||||
|
static const prm::Id arp[] = { prm::Id::arpOn, prm::Id::arpRate, prm::Id::arpPattern, prm::Id::arpOct, prm::Id::arpGate };
|
||||||
static const prm::Id master[] = { prm::Id::vol, prm::Id::pan, prm::Id::poly, prm::Id::glide, prm::Id::tune, prm::Id::hq };
|
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)
|
switch (screenIdx)
|
||||||
|
|
@ -732,7 +857,8 @@ static prm::Id paramForCursor (int screenIdx, int row)
|
||||||
case 2: return envMt[row];
|
case 2: return envMt[row];
|
||||||
case 3: return lfo[row];
|
case 3: return lfo[row];
|
||||||
case 4: return fx[row];
|
case 4: return fx[row];
|
||||||
case 5: return master[row];
|
case 5: return arp[row];
|
||||||
|
case 6: return master[row];
|
||||||
default: return prm::Id::vol;
|
default: return prm::Id::vol;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -766,36 +892,83 @@ void LcdPanel::applyPatchOnBrowserMove()
|
||||||
{
|
{
|
||||||
if (screen == Screen::Patch)
|
if (screen == Screen::Patch)
|
||||||
{
|
{
|
||||||
proc.selectPatch (patchCursor);
|
const int pi = patchIndexAtCursor (patchCursor);
|
||||||
|
if (pi >= 0 && pi < proc.getBank().size())
|
||||||
|
proc.selectPatch (pi);
|
||||||
setDirty();
|
setDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void LcdPanel::navLeft() { adjustCurrent (-1, false); }
|
void LcdPanel::navLeft() { adjustCurrent (-1, false); }
|
||||||
void LcdPanel::navRight() { adjustCurrent (1, false); }
|
void LcdPanel::navRight() { adjustCurrent (1, false); }
|
||||||
|
|
||||||
|
void LcdPanel::openPatchBrowser()
|
||||||
|
{
|
||||||
|
const auto& cats = proc.getBank().categories();
|
||||||
|
int start = 0;
|
||||||
|
const int cur = proc.getPatchIndex();
|
||||||
|
for (size_t c = 0; c < cats.patchIndices.size(); ++c)
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
for (int pi : cats.patchIndices[(size_t) c])
|
||||||
|
if (pi == cur) { found = true; break; }
|
||||||
|
if (found) { start = (int) c; break; }
|
||||||
|
}
|
||||||
|
catCursor = start;
|
||||||
|
enterScreen (Screen::Patches);
|
||||||
|
}
|
||||||
|
|
||||||
void LcdPanel::navEnter()
|
void LcdPanel::navEnter()
|
||||||
{
|
{
|
||||||
switch (screen)
|
switch (screen)
|
||||||
{
|
{
|
||||||
case Screen::Splash: enterScreen (Screen::Home); break;
|
case Screen::Splash: enterScreen (Screen::Home); break;
|
||||||
case Screen::Home: enterScreen (Screen::Menu); break;
|
case Screen::Home:
|
||||||
|
enterScreen (Screen::Menu);
|
||||||
|
break;
|
||||||
case Screen::Menu:
|
case Screen::Menu:
|
||||||
switch (cursor)
|
switch (cursor)
|
||||||
{
|
{
|
||||||
case 0: enterScreen (Screen::Patch); break;
|
case 0: openPatchBrowser(); break;
|
||||||
case 1: openParamScreen (0); break;
|
case 1: openParamScreen (0); break;
|
||||||
case 2: openParamScreen (1); break;
|
case 2: openParamScreen (1); break;
|
||||||
case 3: openParamScreen (2); break;
|
case 3: openParamScreen (2); break;
|
||||||
case 4: openParamScreen (3); break;
|
case 4: openParamScreen (3); break;
|
||||||
case 5: openParamScreen (4); break;
|
case 5: openParamScreen (4); break;
|
||||||
case 6: openParamScreen (5); break;
|
case 6: openParamScreen (5); break;
|
||||||
case 7: enterScreen (Screen::Info); break;
|
case 7: openParamScreen (6); break;
|
||||||
|
case 8: enterScreen (Screen::Info); break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case Screen::Patches:
|
||||||
|
{
|
||||||
|
const auto& cats = proc.getBank().categories();
|
||||||
|
if (catCursor >= 0 && catCursor < (int) cats.names.size())
|
||||||
|
{
|
||||||
|
buildFilteredPatches (catCursor);
|
||||||
|
patchCursor = 0;
|
||||||
|
enterScreen (Screen::Patch);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case Screen::Category:
|
||||||
|
{
|
||||||
|
const auto& cats = proc.getBank().categories();
|
||||||
|
if (catCursor >= 0 && catCursor < (int) cats.names.size())
|
||||||
|
{
|
||||||
|
buildFilteredPatches (catCursor);
|
||||||
|
patchCursor = 0;
|
||||||
|
enterScreen (Screen::Patch);
|
||||||
|
}
|
||||||
|
else if (catCursor == (int) cats.names.size())
|
||||||
|
{
|
||||||
|
enterScreen (Screen::Menu); // SETTINGS row -> main menu
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
case Screen::Patch:
|
case Screen::Patch:
|
||||||
proc.selectPatch (patchCursor);
|
proc.selectPatch (patchIndexAtCursor (patchCursor));
|
||||||
enterScreen (Screen::Menu);
|
enterScreen (Screen::Home); // back to the main screen showing the new patch
|
||||||
break;
|
break;
|
||||||
case Screen::System:
|
case Screen::System:
|
||||||
switch (cursor)
|
switch (cursor)
|
||||||
|
|
@ -820,7 +993,13 @@ void LcdPanel::navEnter()
|
||||||
|
|
||||||
void LcdPanel::navExit()
|
void LcdPanel::navExit()
|
||||||
{
|
{
|
||||||
if (screen == Screen::Menu)
|
if (screen == Screen::Patch)
|
||||||
|
enterScreen (Screen::Patches);
|
||||||
|
else if (screen == Screen::Patches)
|
||||||
|
enterScreen (Screen::Menu);
|
||||||
|
else if (screen == Screen::Category)
|
||||||
|
enterScreen (Screen::Menu); // legacy: go to main menu
|
||||||
|
else if (screen == Screen::Menu)
|
||||||
enterScreen (Screen::Home);
|
enterScreen (Screen::Home);
|
||||||
else if (screen != Screen::Home)
|
else if (screen != Screen::Home)
|
||||||
enterScreen (Screen::Menu);
|
enterScreen (Screen::Menu);
|
||||||
|
|
@ -838,3 +1017,30 @@ bool LcdPanel::keyTrap (const juce::KeyPress& k)
|
||||||
{ navExit(); return true; }
|
{ navExit(); return true; }
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int LcdPanel::patchCountForCurrentView() const
|
||||||
|
{
|
||||||
|
if (! filteredPatches.empty())
|
||||||
|
return (int) filteredPatches.size();
|
||||||
|
return proc.getBank().size();
|
||||||
|
}
|
||||||
|
|
||||||
|
int LcdPanel::patchIndexAtCursor (int viewIdx) const
|
||||||
|
{
|
||||||
|
if (viewIdx < 0) return 0;
|
||||||
|
if (! filteredPatches.empty())
|
||||||
|
{
|
||||||
|
if (viewIdx < (int) filteredPatches.size())
|
||||||
|
return filteredPatches[(size_t) viewIdx];
|
||||||
|
return filteredPatches.empty() ? 0 : filteredPatches.back();
|
||||||
|
}
|
||||||
|
return viewIdx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LcdPanel::buildFilteredPatches (int catIdx)
|
||||||
|
{
|
||||||
|
filteredPatches.clear();
|
||||||
|
const auto& cats = proc.getBank().categories();
|
||||||
|
if (catIdx >= 0 && catIdx < cats.names.size())
|
||||||
|
filteredPatches = cats.patchIndices[(size_t) catIdx];
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,28 +20,39 @@ public:
|
||||||
|
|
||||||
static constexpr int gridW = 160; // dot-matrix cell resolution
|
static constexpr int gridW = 160; // dot-matrix cell resolution
|
||||||
static constexpr int gridH = 64;
|
static constexpr int gridH = 64;
|
||||||
|
// internal supersampling: dots are rasterized as anti-aliased circles at 4x,
|
||||||
|
// then box-downsampled 2:1 onto the 2px/dot presentation image. Downsampling
|
||||||
|
// turned off = chunky squares; 2x direct = boxy edges. 4x gives genuine round,
|
||||||
|
// separated dots that survive the (fractional) uiScale upscale.
|
||||||
|
static constexpr int subsample = 4;
|
||||||
|
static constexpr int presentScale = 2; // presentation pixels per logical dot
|
||||||
|
|
||||||
bool keyTrap (const juce::KeyPress&);
|
bool keyTrap (const juce::KeyPress&);
|
||||||
|
|
||||||
|
void mouseDown (const juce::MouseEvent&) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class Screen { Splash, Home, Menu, Patch, Param, System, Info };
|
enum class Screen { Splash, Home, Menu, Patches, Patch, Param, System, Info, Category };
|
||||||
|
|
||||||
RMX19AudioProcessor& proc;
|
RMX19AudioProcessor& proc;
|
||||||
|
|
||||||
Screen screen = Screen::Splash;
|
Screen screen = Screen::Splash;
|
||||||
juce::uint32 splashUntil = 0;
|
juce::uint32 splashUntil = 0;
|
||||||
|
|
||||||
int cursor = 0; // menu / patch / param cursor
|
int cursor = 0; // menu / param / system / catCursor (when in Category)
|
||||||
int patchCursor = 0;
|
int patchCursor = 0; // patch cursor (global index)
|
||||||
int paramScreenIdx = 0; // which param screen is open
|
int paramScreenIdx = 0; // which param screen is open
|
||||||
|
int catCursor = 0; // category browser cursor
|
||||||
|
|
||||||
|
std::vector<int> filteredPatches; // patch indices for current category filter
|
||||||
|
|
||||||
float powerGlow = 0.0f;
|
float powerGlow = 0.0f;
|
||||||
int lastVoices = -1;
|
int lastVoices = -1;
|
||||||
juce::uint32 lastMidi = 0;
|
juce::uint32 lastMidi = 0;
|
||||||
|
|
||||||
juce::Image dotImage; // 160x64 content scratch buffer
|
juce::Image displayImage; // (gridW*subsample)x(gridH*subsample) render target: glass base + content dots
|
||||||
juce::Image displayImage; // 320x128 cached green-on-black result
|
juce::Image presentImage; // (gridW*presentScale)x(gridH*presentScale) box-downsampled for the panel
|
||||||
juce::Image backdrop; // 320x128 static base (backlight + ghost dots)
|
juce::Image backdrop; // (gridW*subsample)x(gridH*subsample) static 3D glass base (backlight + sheen + vignette + ghost dots)
|
||||||
bool contentDirty = true;
|
bool contentDirty = true;
|
||||||
|
|
||||||
static const juce::uint8* glyphFor (int key);
|
static const juce::uint8* glyphFor (int key);
|
||||||
|
|
@ -49,6 +60,7 @@ private:
|
||||||
|
|
||||||
void rebuild();
|
void rebuild();
|
||||||
void buildBackdrop();
|
void buildBackdrop();
|
||||||
|
juce::Rectangle<int> lcdImageRect() const;
|
||||||
|
|
||||||
void drawContent (juce::Graphics&);
|
void drawContent (juce::Graphics&);
|
||||||
void px (juce::Graphics&, int x, int y, float intensity);
|
void px (juce::Graphics&, int x, int y, float intensity);
|
||||||
|
|
@ -61,6 +73,8 @@ private:
|
||||||
void drawSplash (juce::Graphics&);
|
void drawSplash (juce::Graphics&);
|
||||||
void drawHome (juce::Graphics&);
|
void drawHome (juce::Graphics&);
|
||||||
void drawMenu (juce::Graphics&);
|
void drawMenu (juce::Graphics&);
|
||||||
|
void drawCategory (juce::Graphics&);
|
||||||
|
void drawPatches (juce::Graphics&);
|
||||||
void drawPatchList (juce::Graphics&);
|
void drawPatchList (juce::Graphics&);
|
||||||
void drawParamScreen (juce::Graphics&);
|
void drawParamScreen (juce::Graphics&);
|
||||||
void drawSystem (juce::Graphics&);
|
void drawSystem (juce::Graphics&);
|
||||||
|
|
@ -75,6 +89,9 @@ private:
|
||||||
void setDirty() { contentDirty = true; repaint(); }
|
void setDirty() { contentDirty = true; repaint(); }
|
||||||
void enterScreen (Screen s) { screen = s; cursor = 0; setDirty(); }
|
void enterScreen (Screen s) { screen = s; cursor = 0; setDirty(); }
|
||||||
|
|
||||||
|
// open the category/patch browser, positioned on the loaded patch's category
|
||||||
|
void openPatchBrowser();
|
||||||
|
|
||||||
void timerCallback() override;
|
void timerCallback() override;
|
||||||
|
|
||||||
int rowCountForScreen() const;
|
int rowCountForScreen() const;
|
||||||
|
|
@ -83,6 +100,9 @@ private:
|
||||||
void openParamScreen (int idx);
|
void openParamScreen (int idx);
|
||||||
const char* paramScreenTitle (int idx) const;
|
const char* paramScreenTitle (int idx) const;
|
||||||
int paramCountForScreen (int idx) const;
|
int paramCountForScreen (int idx) const;
|
||||||
|
int patchCountForCurrentView() const;
|
||||||
|
int patchIndexAtCursor (int viewIdx) const;
|
||||||
|
void buildFilteredPatches (int catIdx);
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LcdPanel)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LcdPanel)
|
||||||
};
|
};
|
||||||
|
|
@ -35,6 +35,11 @@ static const Desc table[num] = {
|
||||||
{ "glide", "PORTAMENTO", "GLD", 0, 2, 0, false, 0.02f, 0.2f },
|
{ "glide", "PORTAMENTO", "GLD", 0, 2, 0, false, 0.02f, 0.2f },
|
||||||
{ "tune", "MASTER TUNE", "TUN", -12, 12, 0, true, 1, 1 },
|
{ "tune", "MASTER TUNE", "TUN", -12, 12, 0, true, 1, 1 },
|
||||||
{ "hq", "HQ MODE", "HQ", 0, 1, 0, true, 1, 1 },
|
{ "hq", "HQ MODE", "HQ", 0, 1, 0, true, 1, 1 },
|
||||||
|
{ "arpon", "ARP ON", "ARP", 0, 1, 0, true, 1, 1 },
|
||||||
|
{ "arprate", "ARP RATE", "ART", 0, 7, 2, true, 1, 1 },
|
||||||
|
{ "arppattern", "ARP PATTERN", "APT", 0, 3, 0, true, 1, 1 },
|
||||||
|
{ "arpoct", "ARP OCTAVES", "AOC", 1, 4, 1, true, 1, 1 },
|
||||||
|
{ "arpgate", "ARP GATE", "AGT", 10, 100, 70, false, 1, 10 },
|
||||||
};
|
};
|
||||||
|
|
||||||
const Desc& desc (Id id) { return table[static_cast<int>(id)]; }
|
const Desc& desc (Id id) { return table[static_cast<int>(id)]; }
|
||||||
|
|
@ -95,6 +100,19 @@ const char* fxTypeName (int t)
|
||||||
case 3: return "SPACE"; default: return "OFF"; }
|
case 3: return "SPACE"; default: return "OFF"; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* arpRateName (int r)
|
||||||
|
{
|
||||||
|
switch (r) { case 0: return "1/16"; case 1: return "1/8T"; case 2: return "1/8 ";
|
||||||
|
case 3: return "1/4T"; case 4: return "1/4 "; case 5: return "1/2 ";
|
||||||
|
case 6: return "3/4 "; case 7: return "1/1 "; default: return "1/8 "; }
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* arpPatternName (int p)
|
||||||
|
{
|
||||||
|
switch (p) { case 0: return "UP"; case 1: return "DOWN"; case 2: return "UP/DN";
|
||||||
|
case 3: return "RANDOM"; default: return "UP"; }
|
||||||
|
}
|
||||||
|
|
||||||
const char* delaySyncName (int s)
|
const char* delaySyncName (int s)
|
||||||
{
|
{
|
||||||
switch (s)
|
switch (s)
|
||||||
|
|
@ -184,6 +202,11 @@ juce::String formatParam (Id id, float v)
|
||||||
return juce::String (t > 0 ? "+" : "") + juce::String (t) + "ST";
|
return juce::String (t > 0 ? "+" : "") + juce::String (t) + "ST";
|
||||||
}
|
}
|
||||||
case Id::hq: return v >= 0.5f ? "HQ" : "ECO";
|
case Id::hq: return v >= 0.5f ? "HQ" : "ECO";
|
||||||
|
case Id::arpOn: return v >= 0.5f ? "ON " : "OFF";
|
||||||
|
case Id::arpRate: return arpRateName ((int) v);
|
||||||
|
case Id::arpPattern: return arpPatternName ((int) v);
|
||||||
|
case Id::arpOct: return juce::String ((int) v) + " OCT";
|
||||||
|
case Id::arpGate: return juce::String ((int) v) + "%";
|
||||||
default: return "-----";
|
default: return "-----";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,11 @@ enum class Id : int {
|
||||||
lfoRate, lfoShape, lfoDepth, lfoTarget,
|
lfoRate, lfoShape, lfoDepth, lfoTarget,
|
||||||
fxType, fxAmt, fxTime,
|
fxType, fxAmt, fxTime,
|
||||||
vol, pan, poly, glide, tune,
|
vol, pan, poly, glide, tune,
|
||||||
hq
|
hq,
|
||||||
|
arpOn, arpRate, arpPattern, arpOct, arpGate
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr int num = 30;
|
constexpr int num = 35;
|
||||||
|
|
||||||
struct Desc {
|
struct Desc {
|
||||||
const char* id;
|
const char* id;
|
||||||
|
|
@ -39,6 +40,9 @@ const char* lfoShapeName (int s);
|
||||||
const char* lfoTargetName (int t);
|
const char* lfoTargetName (int t);
|
||||||
const char* fxTypeName (int t);
|
const char* fxTypeName (int t);
|
||||||
|
|
||||||
|
const char* arpRateName (int r);
|
||||||
|
const char* arpPatternName (int p);
|
||||||
|
|
||||||
constexpr int delaySyncCount = 11;
|
constexpr int delaySyncCount = 11;
|
||||||
const char* delaySyncName (int s); // "OFF", "1/16", ...
|
const char* delaySyncName (int s); // "OFF", "1/16", ...
|
||||||
double delaySyncBeats (int s); // beat multiple for sync delay
|
double delaySyncBeats (int s); // beat multiple for sync delay
|
||||||
|
|
|
||||||
|
|
@ -145,12 +145,13 @@ void RackEditor::paint (juce::Graphics& g)
|
||||||
|
|
||||||
void RackEditor::resized()
|
void RackEditor::resized()
|
||||||
{
|
{
|
||||||
// UI-scale dropdown lives in real (unscaled) coordinates, bottom right.
|
// everything renders in base 656×248 space, scaled by an affine transform
|
||||||
scaleBox.setBounds (getWidth() - 112, getHeight() - 30, 104, 20);
|
|
||||||
|
|
||||||
// everything else renders in base 656x248 space, scaled by an affine transform
|
|
||||||
const juce::AffineTransform tf = juce::AffineTransform::scale (uiScale);
|
const juce::AffineTransform tf = juce::AffineTransform::scale (uiScale);
|
||||||
|
|
||||||
|
// UI-scale dropdown now scales with the rest of the UI
|
||||||
|
scaleBox.setTransform (tf);
|
||||||
|
scaleBox.setBounds (baseW - 112, baseH - 30, 104, 20);
|
||||||
|
|
||||||
// LCD recessed module
|
// LCD recessed module
|
||||||
const juce::Rectangle<int> lcdArea (14, 24, 356, 168);
|
const juce::Rectangle<int> lcdArea (14, 24, 356, 168);
|
||||||
display.setTransform (tf);
|
display.setTransform (tf);
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,7 @@ static inline void dbgNote (const char* ev, int note, float vel)
|
||||||
}
|
}
|
||||||
|
|
||||||
class RMX19AudioProcessor::FxEngine
|
class RMX19AudioProcessor::FxEngine
|
||||||
{
|
{public:
|
||||||
public:
|
|
||||||
void prepare (double sampleRate, int maxSamples)
|
void prepare (double sampleRate, int maxSamples)
|
||||||
{
|
{
|
||||||
sr = sampleRate;
|
sr = sampleRate;
|
||||||
|
|
@ -93,6 +92,149 @@ private:
|
||||||
double chPh = 0.0;
|
double chPh = 0.0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Arpeggiator. Holds a set of pressed notes (from the real MIDI input) and
|
||||||
|
// emits a rhythmic sequence of generated notes to the synth engine, synced to
|
||||||
|
// the host tempo (or a free-running sample clock when not transport-locked).
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
class RMX19AudioProcessor::ArpEngine
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Event { int note; bool on; float vel; };
|
||||||
|
|
||||||
|
void reset() { stepPosSamples = 0.0; }
|
||||||
|
void setSampleRate (double s) { sr = s; }
|
||||||
|
|
||||||
|
// Advance the arpeggiator clock by numSamples and produce the note events
|
||||||
|
// (relative to block start) to feed into the synth engine.
|
||||||
|
void step (bool on, double bpm, bool playing,
|
||||||
|
int rateIdx, int patternIdx, int octaves, float gatePct,
|
||||||
|
int numSamples, std::vector<Event>& out)
|
||||||
|
{
|
||||||
|
if (! on)
|
||||||
|
{
|
||||||
|
finishNote (out);
|
||||||
|
stepPosSamples = 0.0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (heldNotes.isEmpty())
|
||||||
|
{
|
||||||
|
finishNote (out);
|
||||||
|
stepPosSamples = 0.0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// step length in samples
|
||||||
|
const double beats[8] = { 0.25, 1.0/3.0, 0.5, 2.0/3.0, 1.0, 2.0, 3.0, 4.0 };
|
||||||
|
const double stepBeats = beats[rateIdx & 7];
|
||||||
|
double stepSamples;
|
||||||
|
if (playing && bpm > 1.0)
|
||||||
|
stepSamples = (60.0 / bpm) * stepBeats * sr;
|
||||||
|
else
|
||||||
|
stepSamples = (stepBeats * 0.5) * sr; // fallback ~120bpm quarter note
|
||||||
|
|
||||||
|
if (stepSamples < 1.0) stepSamples = 1.0;
|
||||||
|
|
||||||
|
// Build note pool (sorted)
|
||||||
|
juce::Array<int> pool = heldNotes;
|
||||||
|
pool.sort();
|
||||||
|
const int poolSize = pool.size();
|
||||||
|
const int seqLen = poolSize * juce::jlimit (1, 4, octaves);
|
||||||
|
|
||||||
|
// absolute sample clock into the running sequence
|
||||||
|
int cursor = 0;
|
||||||
|
while (cursor < numSamples)
|
||||||
|
{
|
||||||
|
// the current step index (absolute count of completed steps)
|
||||||
|
const long stepIndex = (long) (stepPosSamples / stepSamples);
|
||||||
|
const double stepStart = stepIndex * stepSamples;
|
||||||
|
const double stepEnd = stepStart + stepSamples;
|
||||||
|
|
||||||
|
// trigger the note for this step at the start of the step
|
||||||
|
const int triggerSample = (int) (stepStart - stepPosSamples);
|
||||||
|
if (triggerSample >= 0 && triggerSample < numSamples)
|
||||||
|
{
|
||||||
|
const int patternIdxMod = patternIdx & 3;
|
||||||
|
const int playback = arpIndex (stepIndex, seqLen, patternIdxMod);
|
||||||
|
const int noteIdx = playback % poolSize;
|
||||||
|
const int oct = playback / poolSize;
|
||||||
|
const int note = pool[noteIdx] + oct * 12;
|
||||||
|
|
||||||
|
finishNote (out); // release previous arp note (if different handled below)
|
||||||
|
apNoteOn (note, lastVel > 0 ? lastVel : 0.85f, out);
|
||||||
|
arpNote = note;
|
||||||
|
arpNoteActive = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// gate-off within this step
|
||||||
|
const int gateSamples = (int) (stepSamples * juce::jlimit (0.02f, 1.0f, gatePct / 100.0f));
|
||||||
|
const int gateEnd = triggerSample + gateSamples;
|
||||||
|
|
||||||
|
// advance the clock through this step
|
||||||
|
double advance = stepEnd - stepPosSamples;
|
||||||
|
if (advance < 1.0)
|
||||||
|
{
|
||||||
|
stepPosSamples = stepEnd;
|
||||||
|
cursor = numSamples;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const int consumed = juce::jmin (numSamples - cursor, (int) advance);
|
||||||
|
stepPosSamples += consumed;
|
||||||
|
cursor += consumed;
|
||||||
|
|
||||||
|
// turn the note off after its gate time within the block
|
||||||
|
if (arpNoteActive && triggerSample >= 0 && gateEnd > triggerSample && gateEnd <= cursor)
|
||||||
|
{
|
||||||
|
apNoteOff (arpNote, out);
|
||||||
|
arpNoteActive = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void noteOn (int midiNote, float vel) { heldNotes.addIfNotAlreadyThere (midiNote); lastVel = vel; }
|
||||||
|
void noteOff (int midiNote) { heldNotes.removeAllInstancesOf (midiNote); }
|
||||||
|
void allNotesOff() { heldNotes.clear(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
juce::Array<int> heldNotes;
|
||||||
|
double sr = 48000.0;
|
||||||
|
double stepPosSamples = 0.0;
|
||||||
|
int arpNote = -1;
|
||||||
|
bool arpNoteActive = false;
|
||||||
|
float lastVel = 0.85f;
|
||||||
|
|
||||||
|
static int arpIndex (long i, int len, int pattern)
|
||||||
|
{
|
||||||
|
switch (pattern)
|
||||||
|
{
|
||||||
|
case 0: return (int) (i % len); // UP
|
||||||
|
case 1: return (int) ((len - 1) - (i % len)); // DOWN
|
||||||
|
case 2: // UP/DOWN
|
||||||
|
{
|
||||||
|
if (len <= 1) return 0;
|
||||||
|
const int period = len * 2 - 2;
|
||||||
|
const int t = (int) (i % period);
|
||||||
|
return t < len ? t : (len - 1) - (t - (len - 1));
|
||||||
|
}
|
||||||
|
default: return (int) (juce::Random::getSystemRandom().nextInt (len)); // RANDOM
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void apNoteOn (int note, float vel, std::vector<Event>& out) { out.push_back ({ note, true, vel }); }
|
||||||
|
void apNoteOff (int note, std::vector<Event>& out) { out.push_back ({ note, false, 0.0f }); }
|
||||||
|
|
||||||
|
void finishNote (std::vector<Event>& out)
|
||||||
|
{
|
||||||
|
if (arpNoteActive)
|
||||||
|
{
|
||||||
|
apNoteOff (arpNote, out);
|
||||||
|
arpNoteActive = false;
|
||||||
|
arpNote = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
RMX19AudioProcessor::RMX19AudioProcessor()
|
RMX19AudioProcessor::RMX19AudioProcessor()
|
||||||
|
|
@ -116,6 +258,8 @@ void RMX19AudioProcessor::prepareToPlay (double sampleRate, int samplesPerBlock)
|
||||||
engine.allNotesOff (true);
|
engine.allNotesOff (true);
|
||||||
fx = std::make_unique<FxEngine>();
|
fx = std::make_unique<FxEngine>();
|
||||||
fx->prepare (sampleRate, samplesPerBlock);
|
fx->prepare (sampleRate, samplesPerBlock);
|
||||||
|
arp = std::make_unique<ArpEngine>();
|
||||||
|
arp->setSampleRate (sampleRate);
|
||||||
osBuf.setSize (2, juce::nextPowerOfTwo (samplesPerBlock * 2) + 4, false, true);
|
osBuf.setSize (2, juce::nextPowerOfTwo (samplesPerBlock * 2) + 4, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,12 +267,16 @@ void RMX19AudioProcessor::releaseResources()
|
||||||
{
|
{
|
||||||
engine.allNotesOff (true);
|
engine.allNotesOff (true);
|
||||||
if (fx) fx->reset();
|
if (fx) fx->reset();
|
||||||
|
if (arp) arp->allNotesOff();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RMX19AudioProcessor::processMidi (juce::MidiBuffer& midi, const VParams& p)
|
void RMX19AudioProcessor::processMidi (juce::MidiBuffer& midi, const VParams& p)
|
||||||
{
|
{
|
||||||
(void) p;
|
(void) p;
|
||||||
constexpr double bendSemis = 2.0;
|
constexpr double bendSemis = 2.0;
|
||||||
|
bool arpOn = false;
|
||||||
|
if (auto* par = apvts.getRawParameterValue ("arpon"))
|
||||||
|
arpOn = par->load() >= 0.5f;
|
||||||
|
|
||||||
for (const auto meta : midi)
|
for (const auto meta : midi)
|
||||||
{
|
{
|
||||||
|
|
@ -137,12 +285,18 @@ void RMX19AudioProcessor::processMidi (juce::MidiBuffer& midi, const VParams& p)
|
||||||
|
|
||||||
if (msg.isNoteOn())
|
if (msg.isNoteOn())
|
||||||
{
|
{
|
||||||
engine.noteOn (msg.getNoteNumber(), msg.getFloatVelocity());
|
if (arpOn)
|
||||||
|
arp->noteOn (msg.getNoteNumber(), msg.getFloatVelocity());
|
||||||
|
else
|
||||||
|
engine.noteOn (msg.getNoteNumber(), msg.getFloatVelocity());
|
||||||
dbgNote ("ON", msg.getNoteNumber(), msg.getFloatVelocity());
|
dbgNote ("ON", msg.getNoteNumber(), msg.getFloatVelocity());
|
||||||
}
|
}
|
||||||
else if (msg.isNoteOff())
|
else if (msg.isNoteOff())
|
||||||
{
|
{
|
||||||
engine.noteOff (msg.getNoteNumber());
|
if (arpOn)
|
||||||
|
arp->noteOff (msg.getNoteNumber());
|
||||||
|
else
|
||||||
|
engine.noteOff (msg.getNoteNumber());
|
||||||
dbgNote ("OFF", msg.getNoteNumber(), -1.0f);
|
dbgNote ("OFF", msg.getNoteNumber(), -1.0f);
|
||||||
}
|
}
|
||||||
else if (msg.isPitchWheel())
|
else if (msg.isPitchWheel())
|
||||||
|
|
@ -210,6 +364,33 @@ void RMX19AudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::
|
||||||
|
|
||||||
processMidi (midi, p);
|
processMidi (midi, p);
|
||||||
|
|
||||||
|
// advance the arpeggiator and trigger its generated notes
|
||||||
|
if (arp)
|
||||||
|
{
|
||||||
|
const bool arpOn = p.arpOn >= 0.5f;
|
||||||
|
double playingBpm = 120.0;
|
||||||
|
bool playing = false;
|
||||||
|
if (auto* ph = getPlayHead())
|
||||||
|
if (auto pos = ph->getPosition())
|
||||||
|
if (pos->getIsPlaying())
|
||||||
|
{
|
||||||
|
playing = true;
|
||||||
|
if (pos->getBpm().hasValue())
|
||||||
|
playingBpm = juce::jlimit (20.0, 300.0, *pos->getBpm());
|
||||||
|
}
|
||||||
|
std::vector<ArpEngine::Event> ae;
|
||||||
|
arp->step (arpOn, playingBpm, playing,
|
||||||
|
(int) p.arpRate, (int) p.arpPattern, (int) p.arpOct,
|
||||||
|
p.arpGate, numSamples, ae);
|
||||||
|
for (const auto& e : ae)
|
||||||
|
{
|
||||||
|
if (e.on)
|
||||||
|
engine.noteOn (e.note, e.vel);
|
||||||
|
else
|
||||||
|
engine.noteOff (e.note);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const int chL = 0, chR = 1;
|
const int chL = 0, chR = 1;
|
||||||
float* L = buffer.getNumChannels() > 0 ? buffer.getWritePointer (juce::jmin (chL, numChannels - 1)) : nullptr;
|
float* L = buffer.getNumChannels() > 0 ? buffer.getWritePointer (juce::jmin (chL, numChannels - 1)) : nullptr;
|
||||||
float* R = buffer.getNumChannels() > 1 ? buffer.getWritePointer (juce::jmin (chR, numChannels - 1)) : nullptr;
|
float* R = buffer.getNumChannels() > 1 ? buffer.getWritePointer (juce::jmin (chR, numChannels - 1)) : nullptr;
|
||||||
|
|
@ -250,6 +431,20 @@ void RMX19AudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::
|
||||||
R[s] = oR;
|
R[s] = oR;
|
||||||
}
|
}
|
||||||
if (p.vol != 1.0f) buffer.applyGain (p.vol);
|
if (p.vol != 1.0f) buffer.applyGain (p.vol);
|
||||||
|
|
||||||
|
// Final output soft-limiter: prevent hard clipping while preserving dynamics
|
||||||
|
for (int c = 0; c < numChannels; ++c)
|
||||||
|
{
|
||||||
|
float* ch = buffer.getWritePointer (c);
|
||||||
|
for (int s = 0; s < numSamples; ++s)
|
||||||
|
{
|
||||||
|
float x = ch[s];
|
||||||
|
// Soft knee limiting at ~-1dBFS with smooth tanh curve
|
||||||
|
x = std::tanh (x * 0.89f) * 1.12f;
|
||||||
|
if (! std::isfinite (x)) x = 0.0f;
|
||||||
|
ch[s] = juce::jlimit (-1.0f, 1.0f, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (L != nullptr)
|
else if (L != nullptr)
|
||||||
{
|
{
|
||||||
|
|
@ -280,8 +475,32 @@ void RMX19AudioProcessor::processBlock (juce::AudioBuffer<float>& buffer, juce::
|
||||||
for (int c = 0; c < numChannels; ++c)
|
for (int c = 0; c < numChannels; ++c)
|
||||||
buffer.copyFrom (c, 0, dummy, 0, 0, numSamples);
|
buffer.copyFrom (c, 0, dummy, 0, 0, numSamples);
|
||||||
buffer.applyGain (p.vol);
|
buffer.applyGain (p.vol);
|
||||||
|
|
||||||
|
// Final output soft-limiter for mono path
|
||||||
|
for (int c = 0; c < numChannels; ++c)
|
||||||
|
{
|
||||||
|
float* ch = buffer.getWritePointer (c);
|
||||||
|
for (int s = 0; s < numSamples; ++s)
|
||||||
|
{
|
||||||
|
float x = ch[s];
|
||||||
|
x = std::tanh (x * 0.89f) * 1.12f;
|
||||||
|
if (! std::isfinite (x)) x = 0.0f;
|
||||||
|
ch[s] = juce::jlimit (-1.0f, 1.0f, x);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// output meter: instant attack, time-constant release so the LCD VU is readable
|
||||||
|
float peak = 0.0f;
|
||||||
|
for (int c = 0; c < numChannels; ++c)
|
||||||
|
{
|
||||||
|
const float* d = buffer.getReadPointer (c, 0);
|
||||||
|
for (int s = 0; s < numSamples; ++s)
|
||||||
|
peak = juce::jmax (peak, std::abs (d[s]));
|
||||||
|
}
|
||||||
|
const float blockSec = (float) numSamples / (float) std::max (1.0, getSampleRate());
|
||||||
|
outMeter = juce::jmax (peak, outMeter * std::exp (-blockSec / 0.25f));
|
||||||
|
|
||||||
cpu = 0.7f * cpu + 0.3f * (float) (juce::Time::getMillisecondCounter() - start);
|
cpu = 0.7f * cpu + 0.3f * (float) (juce::Time::getMillisecondCounter() - start);
|
||||||
|
|
||||||
// real-time health check (offline probe + debug): flag blocks near the limit
|
// real-time health check (offline probe + debug): flag blocks near the limit
|
||||||
|
|
|
||||||
|
|
@ -37,10 +37,11 @@ public:
|
||||||
|
|
||||||
PresetBank& getBank() { return bank; }
|
PresetBank& getBank() { return bank; }
|
||||||
SynthEngine& getEngine() { return engine; }
|
SynthEngine& getEngine() { return engine; }
|
||||||
int getPatchIndex() const noexcept { return patchIndex; }
|
int getPatchIndex() const noexcept { return patchIndex; }
|
||||||
void selectPatch (int i);
|
void selectPatch (int i);
|
||||||
int getActiveVoices() const noexcept { return engine.activeVoices(); }
|
int getActiveVoices() const noexcept { return engine.activeVoices(); }
|
||||||
juce::uint32 getLastMidiTime() const noexcept { return lastMidi; }
|
juce::uint32 getLastMidiTime() const noexcept { return lastMidi; }
|
||||||
|
float getOutputLevel() const noexcept { return outMeter; }
|
||||||
float getCpu() const noexcept { return cpu; }
|
float getCpu() const noexcept { return cpu; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
@ -51,12 +52,16 @@ private:
|
||||||
class FxEngine;
|
class FxEngine;
|
||||||
std::unique_ptr<FxEngine> fx;
|
std::unique_ptr<FxEngine> fx;
|
||||||
|
|
||||||
|
class ArpEngine;
|
||||||
|
std::unique_ptr<ArpEngine> arp;
|
||||||
|
|
||||||
PresetBank bank;
|
PresetBank bank;
|
||||||
SynthEngine engine;
|
SynthEngine engine;
|
||||||
int patchIndex = 0;
|
int patchIndex = 0;
|
||||||
int lastPatchParam = 0;
|
int lastPatchParam = 0;
|
||||||
juce::uint32 lastMidi = 0;
|
juce::uint32 lastMidi = 0;
|
||||||
float cpu = 0.0f;
|
float cpu = 0.0f;
|
||||||
|
float outMeter = 0.0f; // smoothed output peak, written on the audio thread
|
||||||
float dcL = 0.0f, dcR = 0.0f, dcPrevL = 0.0f, dcPrevR = 0.0f;
|
float dcL = 0.0f, dcR = 0.0f, dcPrevL = 0.0f, dcPrevR = 0.0f;
|
||||||
juce::AudioBuffer<float> osBuf; // internal 2x oversample target (HQ mode)
|
juce::AudioBuffer<float> osBuf; // internal 2x oversample target (HQ mode)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,291 +13,690 @@ enum { O_SIN = 0, O_TRI, O_SAW, O_PUL, O_NOI };
|
||||||
// 24 vol 25 pan 26 poly 27 glide 28 tune
|
// 24 vol 25 pan 26 poly 27 glide 28 tune
|
||||||
|
|
||||||
static const Patch defs[] = {
|
static const Patch defs[] = {
|
||||||
// 1 Warm Pad ---------------------------------------------------------
|
// == POLY PAD ==========================================================
|
||||||
|
// 1 Warm Pad
|
||||||
{ "Warm Pad", "POLY PAD", { O_SAW, O_SAW, 8, 55, 1, 2600, 0.20f, 60, 35,
|
{ "Warm Pad", "POLY PAD", { O_SAW, O_SAW, 8, 55, 1, 2600, 0.20f, 60, 35,
|
||||||
0.60f, 1.6f, 0.80f, 1.20f,
|
0.60f, 1.6f, 0.80f, 1.20f,
|
||||||
0.40f, 0.9f, 0.35f, 0.5f,
|
0.40f, 0.9f, 0.35f, 0.5f,
|
||||||
4.0f, 0, 0.18f, 0,
|
4.0f, 0, 0.18f, 0,
|
||||||
1, 0.32f, 0.30f,
|
1, 0.32f, 0.30f,
|
||||||
0.82f, 0.0f, 12, 0.0f, 0, 0 } },
|
0.82f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
// 2 Glass Pad --------------------------------------------------------
|
// 2 Glass Pad
|
||||||
{ "Glass Pad", "POLY PAD", { O_TRI, O_TRI, 12, 60, 2, 1400, 0.55f, 75, 30,
|
{ "Glass Pad", "POLY PAD", { O_TRI, O_TRI, 12, 60, 1, 1400, 0.55f, 75, 30,
|
||||||
0.80f, 1.8f, 0.90f, 1.60f,
|
0.80f, 1.8f, 0.90f, 1.60f,
|
||||||
0.50f, 1.2f, 0.40f, 0.8f,
|
0.50f, 1.2f, 0.40f, 0.8f,
|
||||||
5.2f, 0, 0.16f, 2,
|
5.2f, 0, 0.16f, 2,
|
||||||
3, 0.38f, 0.40f,
|
3, 0.38f, 0.40f,
|
||||||
0.92f, 0.0f, 12, 0.0f, 0, 0 } },
|
0.92f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
// 3 Dream Pad --------------------------------------------------------
|
// 3 Dream Pad
|
||||||
{ "Dream Pad", "POLY PAD", { O_SAW, O_PUL, -7, 50, 1, 3000, 0.25f, 70, 40,
|
{ "Dream Pad", "POLY PAD", { O_SAW, O_PUL, -7, 50, 1, 3000, 0.25f, 70, 40,
|
||||||
1.10f, 2.4f, 0.85f, 1.80f,
|
1.10f, 2.4f, 0.85f, 1.80f,
|
||||||
0.60f, 1.4f, 0.30f, 0.9f,
|
0.60f, 1.4f, 0.30f, 0.9f,
|
||||||
0.35f, 1, 0.20f, 0,
|
0.35f, 1, 0.20f, 0,
|
||||||
1, 0.40f, 0.50f,
|
1, 0.40f, 0.50f,
|
||||||
0.80f, 0.0f, 12, 0.0f, 0, 0 } },
|
0.80f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
// 4 Movie Strings ----------------------------------------------------
|
// 15 Choir Layered
|
||||||
{ "Movie Strings", "POLY PAD", { O_SAW, O_SAW, 6, 45, 1, 2200, 0.15f, 65, 55,
|
|
||||||
0.50f, 1.2f, 0.80f, 0.70f,
|
|
||||||
0.40f, 0.8f, 0.35f, 0.5f,
|
|
||||||
3.1f, 0, 0.10f, 0,
|
|
||||||
3, 0.28f, 0.45f,
|
|
||||||
0.82f, 0.0f, 12, 0.0f, 0, 0 } },
|
|
||||||
// 5 Electric Piano ---------------------------------------------------
|
|
||||||
{ "Electric Piano", "KEY", { O_PUL, O_TRI, 5, 62, 1, 2800, 0.25f, 45, 40,
|
|
||||||
0.004f, 1.4f, 0.30f, 0.40f,
|
|
||||||
0.004f, 0.2f, 0.10f, 0.3f,
|
|
||||||
6.0f, 0, 0.05f, 2,
|
|
||||||
1, 0.35f, 0.30f,
|
|
||||||
0.80f, 0.0f, 12, 0.0f, 0, 0 } },
|
|
||||||
// 6 Acoustic Piano ---------------------------------------------------
|
|
||||||
{ "Acoustic Piano", "KEY", { O_SAW, O_TRI, 3, 55, 1, 5200, 0.18f, 30, 60,
|
|
||||||
0.003f, 2.2f, 0.25f, 0.50f,
|
|
||||||
0.002f, 0.25f, 0.05f, 0.35f,
|
|
||||||
4.0f, 0, 0.03f, 2,
|
|
||||||
1, 0.22f, 0.30f,
|
|
||||||
0.80f, 0.0f, 12, 0.0f, 0, 0 } },
|
|
||||||
// 7 Toy Piano --------------------------------------------------------
|
|
||||||
{ "Toy Piano", "KEY", { O_PUL, O_PUL, 0, 70, 2, 2100, 0.50f, 30, 45,
|
|
||||||
0.002f, 0.35f, 0.0f, 0.18f,
|
|
||||||
0.002f, 0.05f, 0.02f, 0.08f,
|
|
||||||
5.0f, 0, 0.03f, 2,
|
|
||||||
2, 0.18f, 0.17f,
|
|
||||||
0.90f, 0.0f, 12, 0.0f, 0, 0 } },
|
|
||||||
// 8 Clavinet 70 ------------------------------------------------------
|
|
||||||
{ "Clavinet 70", "KEY", { O_PUL, O_PUL, 0, 40, 2, 1500, 0.40f, 60, 30,
|
|
||||||
0.002f, 0.50f, 0.45f, 0.12f,
|
|
||||||
0.001f, 0.05f, 0.05f, 0.10f,
|
|
||||||
4.0f, 0, 0.05f, 0,
|
|
||||||
1, 0.30f, 0.28f,
|
|
||||||
0.92f, 0.0f, 12, 0.0f, 0, 0 } },
|
|
||||||
// 9 Organ Drawbar ----------------------------------------------------
|
|
||||||
{ "Organ Drawbar", "KEY", { O_SAW, O_SAW, 0, 50, 1, 10000, 0.10f, 20, 80,
|
|
||||||
0.010f, 0.10f, 0.98f, 0.10f,
|
|
||||||
0.010f, 0.10f, 0.50f, 0.10f,
|
|
||||||
8.0f, 3, 0.03f, 1,
|
|
||||||
1, 0.20f, 0.28f,
|
|
||||||
0.82f, 0.0f, 8, 0.0f, 0, 0 } },
|
|
||||||
// 10 FM E.Piano ------------------------------------------------------
|
|
||||||
{ "FM E.Piano", "KEY", { O_SIN, O_TRI, 0, 30, 1, 4000, 0.12f, 50, 50,
|
|
||||||
0.002f, 1.0f, 0.50f, 0.30f,
|
|
||||||
0.002f, 0.20f, 0.10f, 0.25f,
|
|
||||||
4.5f, 0, 0.04f, 2,
|
|
||||||
1, 0.40f, 0.30f,
|
|
||||||
0.86f, 0.0f, 12, 0.0f, 0, 0 } },
|
|
||||||
// 11 Analog Bass -----------------------------------------------------
|
|
||||||
{ "Analog Bass", "BASS", { O_PUL, O_SIN, 0, 80, 1, 700, 0.35f, 35, 10,
|
|
||||||
0.002f, 0.30f, 0.82f, 0.12f,
|
|
||||||
0.002f, 0.10f, 0.15f, 0.10f,
|
|
||||||
3.0f, 0, 0.05f, 0,
|
|
||||||
0, 0.0f, 0.30f,
|
|
||||||
0.94f, 0.0f, 1, 0.04f, 0, 0 } },
|
|
||||||
// 12 Sub Rez Bass ----------------------------------------------------
|
|
||||||
{ "Sub Rez Bass", "BASS", { O_SAW, O_PUL, -4, 45, 1, 520, 0.70f, 55, 15,
|
|
||||||
0.004f, 0.40f, 0.70f, 0.10f,
|
|
||||||
0.004f, 0.15f, 0.25f, 0.10f,
|
|
||||||
2.4f, 3, 0.30f, 0,
|
|
||||||
0, 0.0f, 0.30f,
|
|
||||||
0.94f, 0.0f, 1, 0.03f, 0, 0 } },
|
|
||||||
// 13 Saw Lead --------------------------------------------------------
|
|
||||||
{ "Saw Lead", "LEAD", { O_SAW, O_SAW, 14, 50, 1, 3800, 0.25f, 30, 60,
|
|
||||||
0.005f, 0.30f, 0.80f, 0.20f,
|
|
||||||
0.005f, 0.15f, 0.30f, 0.15f,
|
|
||||||
5.4f, 0, 0.14f, 1,
|
|
||||||
0, 0.0f, 0.28f,
|
|
||||||
0.86f, 0.0f, 1, 0.06f, 0, 0 } },
|
|
||||||
// 14 Mr. Square ------------------------------------------------------
|
|
||||||
{ "Mr. Square", "LEAD", { O_PUL, O_PUL, 7, 60, 1, 2400, 0.22f, 20, 70,
|
|
||||||
0.003f, 0.25f, 0.72f, 0.18f,
|
|
||||||
0.003f, 0.12f, 0.25f, 0.12f,
|
|
||||||
6.6f, 0, 0.10f, 1,
|
|
||||||
0, 0.0f, 0.25f,
|
|
||||||
0.84f, 0.0f, 1, 0.05f, 0, 0 } },
|
|
||||||
// 15 Choir Layered ---------------------------------------------------
|
|
||||||
{ "Choir Layered", "POLY PAD",{ O_SAW, O_PUL, 9, 48, 1, 2600, 0.14f, 62, 45,
|
{ "Choir Layered", "POLY PAD",{ O_SAW, O_PUL, 9, 48, 1, 2600, 0.14f, 62, 45,
|
||||||
0.35f, 0.80f, 0.85f, 1.10f,
|
0.35f, 0.80f, 0.85f, 1.10f,
|
||||||
0.30f, 0.60f, 0.35f, 0.60f,
|
0.30f, 0.60f, 0.35f, 0.60f,
|
||||||
3.4f, 1, 0.12f, 0,
|
3.4f, 1, 0.12f, 0,
|
||||||
1, 0.38f, 0.42f,
|
1, 0.38f, 0.42f,
|
||||||
0.82f, 0.0f, 12, 0.0f, 0, 0 } },
|
0.82f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
// 16 8-bit Chiptune --------------------------------------------------
|
// 17 Shimmer Pad
|
||||||
{ "8bit Chiptune", "LEAD", { O_PUL, O_PUL, 0, 30, 1, 9000, 0.10f, 10, 70,
|
{ "Shimmer Pad", "POLY PAD", { O_TRI, O_TRI, 14, 55, 1, 1800, 0.50f, 60, 20,
|
||||||
0.003f, 0.12f, 0.90f, 0.12f,
|
|
||||||
0.003f, 0.06f, 0.10f, 0.08f,
|
|
||||||
9.0f, 0, 0.05f, 0,
|
|
||||||
0, 0.0f, 0.20f,
|
|
||||||
0.80f, 0.0f, 6, 0.0f, 0, 0 } },
|
|
||||||
// 17 Shimmer Pad -----------------------------------------------------
|
|
||||||
{ "Shimmer Pad", "POLY PAD", { O_TRI, O_TRI, 14, 55, 2, 1800, 0.50f, 60, 20,
|
|
||||||
0.50f, 2.0f, 0.85f, 2.0f,
|
0.50f, 2.0f, 0.85f, 2.0f,
|
||||||
0.30f, 1.5f, 0.40f, 1.0f,
|
0.30f, 1.5f, 0.40f, 1.0f,
|
||||||
5.2f, 0, 0.20f, 2,
|
5.2f, 0, 0.20f, 2,
|
||||||
3, 0.38f, 0.55f,
|
3, 0.38f, 0.55f,
|
||||||
0.90f, 0.0f, 16, 0.0f, 0, 0 } },
|
0.90f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
// 18 Strings Ensemble -------------------------------------------------
|
// 25 Deep Space Pad
|
||||||
{ "Strings Ens.", "POLY PAD", { O_SAW, O_PUL, 8, 58, 1, 3000, 0.10f, 65, 55,
|
|
||||||
0.25f, 1.2f, 0.82f, 1.8f,
|
|
||||||
0.20f, 0.7f, 0.40f, 0.6f,
|
|
||||||
3.8f, 0, 0.10f, 0,
|
|
||||||
1, 0.30f, 0.30f,
|
|
||||||
0.80f, 0.0f, 16, 0.0f, 0, 0 } },
|
|
||||||
// 19 Church Bell -----------------------------------------------------
|
|
||||||
{ "Church Bell", "KEY", { O_SIN, O_SIN, 0, 40, 2, 3200, 0.40f, 40, 30,
|
|
||||||
0.002f, 2.5f, 0.10f, 1.8f,
|
|
||||||
0.002f, 0.3f, 0.10f, 0.6f,
|
|
||||||
4.4f, 0, 0.05f, 2,
|
|
||||||
3, 0.30f, 0.60f,
|
|
||||||
0.92f, 0.0f, 10, 0.0f, 0, 0 } },
|
|
||||||
// 20 E.Piano 2 -------------------------------------------------------
|
|
||||||
{ "E.Piano 2", "KEY", { O_PUL, O_TRI, 4, 58, 1, 2500, 0.30f, 40, 35,
|
|
||||||
0.003f, 1.2f, 0.40f, 0.5f,
|
|
||||||
0.003f, 0.25f, 0.20f, 0.3f,
|
|
||||||
6.0f, 0, 0.04f, 2,
|
|
||||||
1, 0.35f, 0.30f,
|
|
||||||
0.80f, 0.0f, 12, 0.0f, 0, 0 } },
|
|
||||||
// 21 Acid Bass -------------------------------------------------------
|
|
||||||
{ "Acid Bass", "BASS", { O_SAW, O_PUL, 0, 72, 1, 650, 0.60f, 70, 0,
|
|
||||||
0.002f, 0.30f, 0.75f, 0.15f,
|
|
||||||
0.002f, 0.10f, 0.12f, 0.05f,
|
|
||||||
0.6f, 1, 0.30f, 0,
|
|
||||||
0, 0.0f, 0.30f,
|
|
||||||
0.90f, 0.0f, 1, 0.04f, 0, 0 } },
|
|
||||||
// 22 Analog Bass II --------------------------------------------------
|
|
||||||
{ "Analog Bass II", "BASS", { O_PUL, O_SIN, 0, 82, 1, 420, 0.40f, 45, 20,
|
|
||||||
0.002f, 0.25f, 0.80f, 0.12f,
|
|
||||||
0.002f, 0.12f, 0.20f, 0.08f,
|
|
||||||
4.0f, 0, 0.08f, 1,
|
|
||||||
1, 0.25f, 0.20f,
|
|
||||||
0.88f, 0.0f, 1, 0.08f, 0, 0 } },
|
|
||||||
// 23 Detuned Lead ----------------------------------------------------
|
|
||||||
{ "Detuned Lead", "LEAD", { O_SAW, O_SAW, 18, 45, 1, 4100, 0.20f, 35, 60,
|
|
||||||
0.004f, 0.22f, 0.85f, 0.16f,
|
|
||||||
0.004f, 0.12f, 0.30f, 0.10f,
|
|
||||||
6.8f, 0, 0.09f, 1,
|
|
||||||
2, 0.25f, 0.45f,
|
|
||||||
0.85f, 0.0f, 1, 0.05f, 0, 0 } },
|
|
||||||
// 24 Square Lead -----------------------------------------------------
|
|
||||||
{ "Square Lead", "LEAD", { O_PUL, O_PUL, 10, 55, 1, 5200, 0.25f, 30, 65,
|
|
||||||
0.004f, 0.35f, 0.72f, 0.20f,
|
|
||||||
0.004f, 0.15f, 0.25f, 0.12f,
|
|
||||||
7.2f, 0, 0.08f, 1,
|
|
||||||
1, 0.30f, 0.25f,
|
|
||||||
0.85f, 0.0f, 1, 0.06f, 0, 0 } },
|
|
||||||
// 25 Deep Space Pad --------------------------------------------------
|
|
||||||
{ "Deep Space Pad","POLY PAD", { O_SAW, O_SAW, 16, 60, 1, 1500, 0.45f, 70, 35,
|
{ "Deep Space Pad","POLY PAD", { O_SAW, O_SAW, 16, 60, 1, 1500, 0.45f, 70, 35,
|
||||||
0.90f, 2.4f, 0.85f, 2.2f,
|
0.90f, 2.4f, 0.85f, 2.2f,
|
||||||
0.50f, 1.6f, 0.45f, 1.2f,
|
0.50f, 1.6f, 0.45f, 1.2f,
|
||||||
5.0f, 0, 0.22f, 2,
|
5.0f, 0, 0.22f, 2,
|
||||||
3, 0.42f, 0.70f,
|
3, 0.42f, 0.70f,
|
||||||
0.80f, 0.0f, 16, 0.0f, 0, 0 } },
|
0.80f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
// 26 Liquid Keys -----------------------------------------------------
|
// 34 Space Choir
|
||||||
{ "Liquid Keys", "KEY", { O_TRI, O_TRI, 8, 55, 1, 2800, 0.30f, 55, 30,
|
|
||||||
0.30f, 1.6f, 0.60f, 0.90f,
|
|
||||||
0.20f, 1.0f, 0.30f, 0.60f,
|
|
||||||
3.6f, 0, 0.16f, 2,
|
|
||||||
3, 0.36f, 0.50f,
|
|
||||||
0.84f, 0.0f, 12, 0.0f, 0, 0 } },
|
|
||||||
// 27 Techno Bass -----------------------------------------------------
|
|
||||||
{ "Techno Bass", "BASS", { O_SAW, O_PUL, 0, 88, 1, 480, 0.45f, 60, 10,
|
|
||||||
0.003f, 0.40f, 0.75f, 0.10f,
|
|
||||||
0.003f, 0.12f, 0.15f, 0.08f,
|
|
||||||
2.0f, 2, 0.35f, 0,
|
|
||||||
2, 0.32f, 0.28f,
|
|
||||||
0.92f, 0.0f, 1, 0.03f, 0, 0 } },
|
|
||||||
// 28 Octave Lead -----------------------------------------------------
|
|
||||||
{ "Octave Lead", "LEAD", { O_PUL, O_SAW, 0, 45, 1, 4200, 0.20f, 25, 65,
|
|
||||||
0.004f, 0.28f, 0.78f, 0.18f,
|
|
||||||
0.004f, 0.14f, 0.28f, 0.12f,
|
|
||||||
6.0f, 0, 0.12f, 1,
|
|
||||||
2, 0.28f, 0.35f,
|
|
||||||
0.86f, 0.0f, 1, 0.05f, 0, 0 } },
|
|
||||||
// 29 Glassy Chime ----------------------------------------------------
|
|
||||||
{ "Glassy Chime", "KEY", { O_SIN, O_TRI, 20, 65, 2, 4200, 0.35f, 35, 40,
|
|
||||||
0.005f, 2.0f, 0.30f, 1.6f,
|
|
||||||
0.004f, 0.35f, 0.15f, 0.6f,
|
|
||||||
5.6f, 0, 0.08f, 2,
|
|
||||||
3, 0.34f, 0.60f,
|
|
||||||
0.82f, 0.0f, 10, 0.0f, 0, 0 } },
|
|
||||||
// 30 Chorus Organ ----------------------------------------------------
|
|
||||||
{ "Chorus Organ", "KEY", { O_SAW, O_SAW, 0, 42, 1, 8000, 0.14f, 30, 75,
|
|
||||||
0.010f, 0.10f, 0.95f, 0.12f,
|
|
||||||
0.010f, 0.12f, 0.60f, 0.15f,
|
|
||||||
7.5f, 3, 0.04f, 1,
|
|
||||||
1, 0.40f, 0.34f,
|
|
||||||
0.86f, 0.0f, 8, 0.0f, 0, 0 } },
|
|
||||||
// 31 Night Strings ---------------------------------------------------
|
|
||||||
{ "Night Strings","POLY PAD", { O_SAW, O_TRI, 10, 52, 0, 2600, 0.10f, 60, 50,
|
|
||||||
1.10f, 2.6f, 0.85f, 2.0f,
|
|
||||||
0.55f, 1.6f, 0.40f, 1.1f,
|
|
||||||
3.2f, 0, 0.14f, 0,
|
|
||||||
3, 0.40f, 0.55f,
|
|
||||||
0.84f, 0.0f, 16, 0.0f, 0, 0 } },
|
|
||||||
// 32 Pulse Echo ------------------------------------------------------
|
|
||||||
{ "Pulse Echo", "LEAD", { O_PUL, O_PUL, 0, 35, 2, 6000, 0.18f, 15, 60,
|
|
||||||
0.005f, 0.32f, 0.70f, 0.20f,
|
|
||||||
0.005f, 0.16f, 0.30f, 0.15f,
|
|
||||||
8.0f, 0, 0.10f, 0,
|
|
||||||
2, 0.45f, 0.38f,
|
|
||||||
0.84f, 0.0f, 1, 0.04f, 0, 0 } },
|
|
||||||
// 33 Sine Sub Bass ---------------------------------------------------
|
|
||||||
{ "Sine Sub Bass","BASS", { O_SIN, O_SIN, 0, 100, 1, 380, 0.20f, 55, 10,
|
|
||||||
0.003f, 0.35f, 0.85f, 0.10f,
|
|
||||||
0.003f, 0.10f, 0.30f, 0.10f,
|
|
||||||
3.5f, 0, 0.06f, 0,
|
|
||||||
2, 0.25f, 0.20f,
|
|
||||||
0.90f, 0.0f, 1, 0.05f, 0, 0 } },
|
|
||||||
// 34 Space Choir -----------------------------------------------------
|
|
||||||
{ "Space Choir", "POLY PAD", { O_TRI, O_SAW, 12, 50, 1, 2400, 0.35f, 65, 45,
|
{ "Space Choir", "POLY PAD", { O_TRI, O_SAW, 12, 50, 1, 2400, 0.35f, 65, 45,
|
||||||
0.45f, 1.4f, 0.82f, 1.7f,
|
0.45f, 1.4f, 0.82f, 1.7f,
|
||||||
0.30f, 0.90f, 0.40f, 0.9f,
|
0.30f, 0.90f, 0.40f, 0.9f,
|
||||||
3.8f, 1, 0.15f, 0,
|
3.8f, 1, 0.15f, 0,
|
||||||
3, 0.46f, 0.65f,
|
3, 0.46f, 0.65f,
|
||||||
0.82f, 0.0f, 12, 0.0f, 0, 0 } },
|
0.82f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
// 35 Retro Clav ------------------------------------------------------
|
// 39 Crystal Pad
|
||||||
|
{ "Crystal Pad", "POLY PAD", { O_SIN, O_TRI, 18, 55, 1, 3600, 0.40f, 50, 25,
|
||||||
|
0.60f, 2.2f, 0.85f, 2.0f,
|
||||||
|
0.40f, 1.4f, 0.40f, 1.2f,
|
||||||
|
5.5f, 0, 0.20f, 2,
|
||||||
|
3, 0.40f, 0.60f,
|
||||||
|
0.80f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
|
// 41 Infinite Pad
|
||||||
|
{ "Infinite Pad", "POLY PAD", { O_SAW, O_SAW, 20, 50, 1, 1800, 0.30f, 75, 35,
|
||||||
|
2.0f, 3.5f, 0.90f, 4.0f,
|
||||||
|
1.5f, 2.5f, 0.50f, 3.0f,
|
||||||
|
0.25f, 1, 0.25f, 2,
|
||||||
|
3, 0.50f, 0.55f,
|
||||||
|
0.80f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 42 Nebula
|
||||||
|
{ "Nebula", "POLY PAD", { O_TRI, O_PUL, 15, 55, 1, 2000, 0.25f, 65, 30,
|
||||||
|
1.8f, 3.0f, 0.88f, 3.5f,
|
||||||
|
1.2f, 2.0f, 0.45f, 2.5f,
|
||||||
|
0.30f, 0, 0.20f, 0,
|
||||||
|
3, 0.55f, 0.65f,
|
||||||
|
0.82f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 43 Cathedral Pad
|
||||||
|
{ "Cathedral Pad","POLY PAD", { O_SIN, O_SAW, 12, 45, 2, 1600, 0.45f, 80, 40,
|
||||||
|
2.5f, 4.0f, 0.92f, 5.0f,
|
||||||
|
1.8f, 3.0f, 0.55f, 3.5f,
|
||||||
|
4.0f, 0, 0.15f, 0,
|
||||||
|
3, 0.60f, 0.80f,
|
||||||
|
0.78f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 45 Monolith
|
||||||
|
{ "Monolith", "POLY PAD", { O_SIN, O_SIN, 0, 50, 1, 320, 0.25f, 40, 15,
|
||||||
|
2.0f, 3.0f, 0.88f, 4.5f,
|
||||||
|
1.5f, 2.5f, 0.50f, 3.0f,
|
||||||
|
3.5f, 0, 0.18f, 2,
|
||||||
|
3, 0.42f, 0.75f,
|
||||||
|
0.78f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 46 Quantum Drift
|
||||||
|
{ "Quantum Drift","POLY PAD", { O_SAW, O_SAW, 22, 48, 2, 2200, 0.40f, 70, 30,
|
||||||
|
0.80f, 2.2f, 0.88f, 3.0f,
|
||||||
|
0.50f, 1.8f, 0.45f, 2.0f,
|
||||||
|
5.8f, 0, 0.22f, 0,
|
||||||
|
3, 0.45f, 0.65f,
|
||||||
|
0.82f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 47 Horizon
|
||||||
|
{ "Horizon", "POLY PAD", { O_TRI, O_SAW, 4, 50, 1, 1800, 0.20f, 65, 35,
|
||||||
|
1.5f, 2.8f, 0.90f, 3.5f,
|
||||||
|
1.0f, 2.0f, 0.50f, 2.5f,
|
||||||
|
0.3f, 1, 0.18f, 0,
|
||||||
|
1, 0.40f, 0.55f,
|
||||||
|
0.80f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 52 Abyss
|
||||||
|
{ "Abyss", "POLY PAD", { O_SAW, O_TRI, 14, 50, 1, 1200, 0.35f, 80, 30,
|
||||||
|
2.5f, 3.5f, 0.90f, 5.0f,
|
||||||
|
2.0f, 3.0f, 0.50f, 3.5f,
|
||||||
|
4.0f, 1, 0.18f, 0,
|
||||||
|
3, 0.70f, 0.80f,
|
||||||
|
0.78f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 53 Shimmer Reverb
|
||||||
|
{ "Shimmer Reverb","POLY PAD",{ O_TRI, O_SIN, 8, 55, 2, 3400, 0.50f, 75, 25,
|
||||||
|
3.0f, 4.0f, 0.92f, 5.5f,
|
||||||
|
2.2f, 3.5f, 0.55f, 4.0f,
|
||||||
|
5.0f, 0, 0.20f, 2,
|
||||||
|
3, 0.75f, 0.70f,
|
||||||
|
0.76f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 54 Temple
|
||||||
|
{ "Temple", "POLY PAD", { O_PUL, O_SAW, 6, 48, 1, 1600, 0.30f, 70, 35,
|
||||||
|
2.0f, 3.0f, 0.88f, 4.5f,
|
||||||
|
1.5f, 2.5f, 0.48f, 3.0f,
|
||||||
|
3.5f, 0, 0.15f, 0,
|
||||||
|
3, 0.65f, 0.75f,
|
||||||
|
0.80f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 55 Silk Pad
|
||||||
|
{ "Silk Pad", "POLY PAD", { O_TRI, O_TRI, 6, 55, 1, 3400, 0.20f, 55, 25,
|
||||||
|
1.2f, 2.5f, 0.90f, 3.0f,
|
||||||
|
0.8f, 1.8f, 0.45f, 2.0f,
|
||||||
|
3.0f, 0, 0.18f, 0,
|
||||||
|
1, 0.38f, 0.45f,
|
||||||
|
0.82f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 56 Glass Cloud
|
||||||
|
{ "Glass Cloud", "POLY PAD", { O_SIN, O_TRI, 16, 50, 1, 3800, 0.35f, 65, 20,
|
||||||
|
1.5f, 2.8f, 0.88f, 3.5f,
|
||||||
|
1.0f, 2.0f, 0.40f, 2.5f,
|
||||||
|
0.35f, 1, 0.22f, 0,
|
||||||
|
3, 0.55f, 0.60f,
|
||||||
|
0.80f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 57 Velvet Pad
|
||||||
|
{ "Velvet Pad", "POLY PAD", { O_SAW, O_TRI, 10, 52, 1, 1600, 0.25f, 70, 30,
|
||||||
|
1.8f, 3.0f, 0.90f, 3.5f,
|
||||||
|
1.2f, 2.2f, 0.50f, 2.5f,
|
||||||
|
0.30f, 1, 0.20f, 0,
|
||||||
|
3, 0.48f, 0.55f,
|
||||||
|
0.82f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// == KEY ===============================================================
|
||||||
|
// 5 Electric Piano
|
||||||
|
{ "Electric Piano", "KEY", { O_PUL, O_TRI, 5, 62, 1, 2800, 0.25f, 45, 40,
|
||||||
|
0.004f, 1.4f, 0.30f, 0.40f,
|
||||||
|
0.004f, 0.2f, 0.10f, 0.3f,
|
||||||
|
6.0f, 0, 0.05f, 2,
|
||||||
|
1, 0.35f, 0.30f,
|
||||||
|
0.80f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
|
// 6 Acoustic Piano
|
||||||
|
{ "Acoustic Piano", "KEY", { O_SAW, O_TRI, 3, 55, 1, 5200, 0.18f, 30, 60,
|
||||||
|
0.003f, 2.2f, 0.25f, 0.50f,
|
||||||
|
0.002f, 0.25f, 0.05f, 0.35f,
|
||||||
|
4.0f, 0, 0.03f, 2,
|
||||||
|
1, 0.22f, 0.30f,
|
||||||
|
0.80f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
|
// 7 Toy Piano
|
||||||
|
{ "Toy Piano", "KEY", { O_PUL, O_PUL, 0, 70, 1, 2100, 0.50f, 30, 45,
|
||||||
|
0.002f, 0.35f, 0.0f, 0.18f,
|
||||||
|
0.002f, 0.05f, 0.02f, 0.08f,
|
||||||
|
5.0f, 0, 0.03f, 2,
|
||||||
|
2, 0.18f, 0.17f,
|
||||||
|
0.90f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
|
// 8 Clavinet 70
|
||||||
|
{ "Clavinet 70", "KEY", { O_PUL, O_PUL, 0, 40, 1, 1500, 0.40f, 60, 30,
|
||||||
|
0.002f, 0.50f, 0.45f, 0.12f,
|
||||||
|
0.001f, 0.05f, 0.05f, 0.10f,
|
||||||
|
4.0f, 0, 0.05f, 0,
|
||||||
|
1, 0.30f, 0.28f,
|
||||||
|
0.92f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
|
// 9 Organ Drawbar
|
||||||
|
{ "Organ Drawbar", "KEY", { O_SAW, O_SAW, 0, 50, 1, 10000, 0.10f, 20, 80,
|
||||||
|
0.010f, 0.10f, 0.98f, 0.10f,
|
||||||
|
0.010f, 0.10f, 0.50f, 0.10f,
|
||||||
|
8.0f, 3, 0.03f, 1,
|
||||||
|
1, 0.20f, 0.28f,
|
||||||
|
0.82f, 0.0f, 8, 0.0f, 0, 0 } },
|
||||||
|
// 10 FM E.Piano
|
||||||
|
{ "FM E.Piano", "KEY", { O_SIN, O_TRI, 0, 30, 1, 4000, 0.12f, 50, 50,
|
||||||
|
0.002f, 1.0f, 0.50f, 0.30f,
|
||||||
|
0.002f, 0.20f, 0.10f, 0.25f,
|
||||||
|
4.5f, 0, 0.04f, 2,
|
||||||
|
1, 0.40f, 0.30f,
|
||||||
|
0.86f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
|
// 19 Church Bell
|
||||||
|
{ "Church Bell", "KEY", { O_SIN, O_SIN, 0, 40, 1, 3200, 0.40f, 40, 30,
|
||||||
|
0.002f, 2.5f, 0.10f, 1.8f,
|
||||||
|
0.002f, 0.3f, 0.10f, 0.6f,
|
||||||
|
4.4f, 0, 0.05f, 2,
|
||||||
|
3, 0.30f, 0.60f,
|
||||||
|
0.92f, 0.0f, 10, 0.0f, 0, 0 } },
|
||||||
|
// 20 E.Piano 2
|
||||||
|
{ "E.Piano 2", "KEY", { O_PUL, O_TRI, 4, 58, 1, 2500, 0.30f, 40, 35,
|
||||||
|
0.003f, 1.2f, 0.40f, 0.5f,
|
||||||
|
0.003f, 0.25f, 0.20f, 0.3f,
|
||||||
|
6.0f, 0, 0.04f, 2,
|
||||||
|
1, 0.35f, 0.30f,
|
||||||
|
0.80f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
|
// 26 Liquid Keys
|
||||||
|
{ "Liquid Keys", "KEY", { O_TRI, O_TRI, 8, 55, 1, 2800, 0.30f, 55, 30,
|
||||||
|
0.30f, 1.6f, 0.60f, 0.90f,
|
||||||
|
0.20f, 1.0f, 0.30f, 0.60f,
|
||||||
|
3.6f, 0, 0.16f, 2,
|
||||||
|
3, 0.36f, 0.50f,
|
||||||
|
0.84f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
|
// 29 Glassy Chime
|
||||||
|
{ "Glassy Chime", "KEY", { O_SIN, O_TRI, 20, 65, 1, 4200, 0.35f, 35, 40,
|
||||||
|
0.005f, 2.0f, 0.30f, 1.6f,
|
||||||
|
0.004f, 0.35f, 0.15f, 0.6f,
|
||||||
|
5.6f, 0, 0.08f, 2,
|
||||||
|
3, 0.34f, 0.60f,
|
||||||
|
0.82f, 0.0f, 10, 0.0f, 0, 0 } },
|
||||||
|
// 30 Chorus Organ
|
||||||
|
{ "Chorus Organ", "KEY", { O_SAW, O_SAW, 0, 42, 1, 8000, 0.14f, 30, 75,
|
||||||
|
0.010f, 0.10f, 0.95f, 0.12f,
|
||||||
|
0.010f, 0.12f, 0.60f, 0.15f,
|
||||||
|
7.5f, 3, 0.04f, 1,
|
||||||
|
1, 0.40f, 0.34f,
|
||||||
|
0.86f, 0.0f, 8, 0.0f, 0, 0 } },
|
||||||
|
// 35 Retro Clav
|
||||||
{ "Retro Clav", "KEY", { O_PUL, O_TRI, 2, 55, 1, 2600, 0.35f, 40, 35,
|
{ "Retro Clav", "KEY", { O_PUL, O_TRI, 2, 55, 1, 2600, 0.35f, 40, 35,
|
||||||
0.003f, 0.60f, 0.40f, 0.15f,
|
0.003f, 0.60f, 0.40f, 0.15f,
|
||||||
0.002f, 0.10f, 0.08f, 0.12f,
|
0.002f, 0.10f, 0.08f, 0.12f,
|
||||||
5.0f, 0, 0.05f, 2,
|
5.0f, 0, 0.05f, 2,
|
||||||
3, 0.25f, 0.30f,
|
3, 0.25f, 0.30f,
|
||||||
0.86f, 0.0f, 10, 0.0f, 0, 0 } },
|
0.86f, 0.0f, 10, 0.0f, 0, 0 } },
|
||||||
// 36 High Strings ----------------------------------------------------
|
// 40 Funk Wah
|
||||||
{ "High Strings", "POLY PAD", { O_SAW, O_PUL, 6, 60, 1, 3400, 0.10f, 55, 60,
|
|
||||||
0.20f, 1.0f, 0.80f, 1.6f,
|
|
||||||
0.15f, 0.60f, 0.40f, 0.7f,
|
|
||||||
4.2f, 0, 0.10f, 0,
|
|
||||||
1, 0.35f, 0.40f,
|
|
||||||
0.82f, 0.0f, 16, 0.0f, 0, 0 } },
|
|
||||||
// 37 Echo Flute ------------------------------------------------------
|
|
||||||
{ "Echo Flute", "LEAD", { O_TRI, O_SIN, 0, 85, 1, 3000, 0.15f, 20, 55,
|
|
||||||
0.05f, 0.50f, 0.75f, 0.25f,
|
|
||||||
0.04f, 0.30f, 0.40f, 0.20f,
|
|
||||||
4.8f, 0, 0.10f, 1,
|
|
||||||
2, 0.40f, 0.45f,
|
|
||||||
0.86f, 0.0f, 1, 0.06f, 0, 0 } },
|
|
||||||
// 38 Dub Rez Bass ----------------------------------------------------
|
|
||||||
{ "Dub Rez Bass", "BASS", { O_SAW, O_PUL, -5, 60, 1, 500, 0.65f, 45, 5,
|
|
||||||
0.004f, 0.35f, 0.80f, 0.12f,
|
|
||||||
0.004f, 0.15f, 0.25f, 0.12f,
|
|
||||||
0.8f, 3, 0.30f, 0,
|
|
||||||
2, 0.40f, 0.36f,
|
|
||||||
0.90f, 0.0f, 1, 0.05f, 0, 0 } },
|
|
||||||
// 39 Crystal Pad -----------------------------------------------------
|
|
||||||
{ "Crystal Pad", "POLY PAD", { O_SIN, O_TRI, 18, 55, 2, 3600, 0.40f, 50, 25,
|
|
||||||
0.60f, 2.2f, 0.85f, 2.0f,
|
|
||||||
0.40f, 1.4f, 0.40f, 1.2f,
|
|
||||||
5.5f, 0, 0.20f, 2,
|
|
||||||
3, 0.40f, 0.60f,
|
|
||||||
0.80f, 0.0f, 12, 0.0f, 0, 0 } },
|
|
||||||
// 40 Funk Wah --------------------------------------------------------
|
|
||||||
{ "Funk Wah", "KEY", { O_PUL, O_SAW, 0, 40, 1, 1800, 0.55f, 75, 20,
|
{ "Funk Wah", "KEY", { O_PUL, O_SAW, 0, 40, 1, 1800, 0.55f, 75, 20,
|
||||||
0.004f, 0.70f, 0.50f, 0.20f,
|
0.004f, 0.70f, 0.50f, 0.20f,
|
||||||
0.004f, 0.20f, 0.20f, 0.15f,
|
0.004f, 0.20f, 0.20f, 0.15f,
|
||||||
7.0f, 2, 0.40f, 0,
|
7.0f, 2, 0.40f, 0,
|
||||||
1, 0.32f, 0.30f,
|
1, 0.32f, 0.30f,
|
||||||
0.88f, 0.0f, 1, 0.03f, 0, 0 } },
|
0.88f, 0.0f, 1, 0.03f, 0, 0 } },
|
||||||
|
// == BASS ==============================================================
|
||||||
|
// 11 Analog Bass
|
||||||
|
{ "Analog Bass", "BASS", { O_PUL, O_SIN, 0, 80, 1, 700, 0.35f, 35, 10,
|
||||||
|
0.002f, 0.30f, 0.82f, 0.12f,
|
||||||
|
0.002f, 0.10f, 0.15f, 0.10f,
|
||||||
|
3.0f, 0, 0.05f, 0,
|
||||||
|
0, 0.0f, 0.30f,
|
||||||
|
0.94f, 0.0f, 1, 0.04f, 0, 0 } },
|
||||||
|
// 12 Sub Rez Bass
|
||||||
|
{ "Sub Rez Bass", "BASS", { O_SAW, O_PUL, -4, 45, 1, 520, 0.70f, 55, 15,
|
||||||
|
0.004f, 0.40f, 0.70f, 0.10f,
|
||||||
|
0.004f, 0.15f, 0.25f, 0.10f,
|
||||||
|
2.4f, 3, 0.30f, 0,
|
||||||
|
0, 0.0f, 0.30f,
|
||||||
|
0.94f, 0.0f, 1, 0.03f, 0, 0 } },
|
||||||
|
// 21 Acid Bass
|
||||||
|
{ "Acid Bass", "BASS", { O_SAW, O_PUL, 0, 72, 1, 650, 0.60f, 70, 0,
|
||||||
|
0.002f, 0.30f, 0.75f, 0.15f,
|
||||||
|
0.002f, 0.10f, 0.12f, 0.05f,
|
||||||
|
0.6f, 1, 0.30f, 0,
|
||||||
|
0, 0.0f, 0.30f,
|
||||||
|
0.90f, 0.0f, 1, 0.04f, 0, 0 } },
|
||||||
|
// 22 Analog Bass II
|
||||||
|
{ "Analog Bass II", "BASS", { O_PUL, O_SIN, 0, 82, 1, 420, 0.40f, 45, 20,
|
||||||
|
0.002f, 0.25f, 0.80f, 0.12f,
|
||||||
|
0.002f, 0.12f, 0.20f, 0.08f,
|
||||||
|
4.0f, 0, 0.08f, 1,
|
||||||
|
1, 0.25f, 0.20f,
|
||||||
|
0.88f, 0.0f, 1, 0.08f, 0, 0 } },
|
||||||
|
// 33 Sine Sub Bass
|
||||||
|
{ "Sine Sub Bass","BASS", { O_SIN, O_SIN, 0, 100, 1, 380, 0.20f, 55, 10,
|
||||||
|
0.003f, 0.35f, 0.85f, 0.10f,
|
||||||
|
0.003f, 0.10f, 0.30f, 0.10f,
|
||||||
|
3.5f, 0, 0.06f, 0,
|
||||||
|
2, 0.25f, 0.20f,
|
||||||
|
0.90f, 0.0f, 1, 0.05f, 0, 0 } },
|
||||||
|
// == LEAD ==============================================================
|
||||||
|
// 13 Saw Lead
|
||||||
|
{ "Saw Lead", "LEAD", { O_SAW, O_SAW, 14, 50, 1, 3800, 0.25f, 30, 60,
|
||||||
|
0.005f, 0.30f, 0.80f, 0.20f,
|
||||||
|
0.005f, 0.15f, 0.30f, 0.15f,
|
||||||
|
5.4f, 0, 0.14f, 1,
|
||||||
|
0, 0.0f, 0.28f,
|
||||||
|
0.86f, 0.0f, 1, 0.06f, 0, 0 } },
|
||||||
|
// 14 Mr. Square
|
||||||
|
{ "Mr. Square", "LEAD", { O_PUL, O_PUL, 7, 60, 1, 2400, 0.22f, 20, 70,
|
||||||
|
0.003f, 0.25f, 0.72f, 0.18f,
|
||||||
|
0.003f, 0.12f, 0.25f, 0.12f,
|
||||||
|
6.6f, 0, 0.10f, 1,
|
||||||
|
0, 0.0f, 0.25f,
|
||||||
|
0.84f, 0.0f, 1, 0.05f, 0, 0 } },
|
||||||
|
// 16 8bit Chiptune
|
||||||
|
{ "8bit Chiptune", "LEAD", { O_PUL, O_PUL, 0, 30, 1, 9000, 0.10f, 10, 70,
|
||||||
|
0.003f, 0.12f, 0.90f, 0.12f,
|
||||||
|
0.003f, 0.06f, 0.10f, 0.08f,
|
||||||
|
9.0f, 0, 0.05f, 0,
|
||||||
|
0, 0.0f, 0.20f,
|
||||||
|
0.80f, 0.0f, 6, 0.0f, 0, 0 } },
|
||||||
|
// 23 Detuned Lead
|
||||||
|
{ "Detuned Lead", "LEAD", { O_SAW, O_SAW, 18, 45, 1, 4100, 0.20f, 35, 60,
|
||||||
|
0.004f, 0.22f, 0.85f, 0.16f,
|
||||||
|
0.004f, 0.12f, 0.30f, 0.10f,
|
||||||
|
6.8f, 0, 0.09f, 1,
|
||||||
|
2, 0.25f, 0.45f,
|
||||||
|
0.85f, 0.0f, 1, 0.05f, 0, 0 } },
|
||||||
|
// 24 Square Lead
|
||||||
|
{ "Square Lead", "LEAD", { O_PUL, O_PUL, 10, 55, 1, 5200, 0.25f, 30, 65,
|
||||||
|
0.004f, 0.35f, 0.72f, 0.20f,
|
||||||
|
0.004f, 0.15f, 0.25f, 0.12f,
|
||||||
|
7.2f, 0, 0.08f, 1,
|
||||||
|
1, 0.30f, 0.25f,
|
||||||
|
0.85f, 0.0f, 1, 0.06f, 0, 0 } },
|
||||||
|
// 28 Octave Lead
|
||||||
|
{ "Octave Lead", "LEAD", { O_PUL, O_SAW, 0, 45, 1, 4200, 0.20f, 25, 65,
|
||||||
|
0.004f, 0.28f, 0.78f, 0.18f,
|
||||||
|
0.004f, 0.14f, 0.28f, 0.12f,
|
||||||
|
6.0f, 0, 0.12f, 1,
|
||||||
|
2, 0.28f, 0.35f,
|
||||||
|
0.86f, 0.0f, 1, 0.05f, 0, 0 } },
|
||||||
|
// 32 Pulse Echo
|
||||||
|
{ "Pulse Echo", "LEAD", { O_PUL, O_PUL, 0, 35, 1, 6000, 0.18f, 15, 60,
|
||||||
|
0.005f, 0.32f, 0.70f, 0.20f,
|
||||||
|
0.005f, 0.16f, 0.30f, 0.15f,
|
||||||
|
8.0f, 0, 0.10f, 0,
|
||||||
|
2, 0.45f, 0.38f,
|
||||||
|
0.84f, 0.0f, 1, 0.04f, 0, 0 } },
|
||||||
|
// 37 Echo Flute
|
||||||
|
{ "Echo Flute", "LEAD", { O_TRI, O_SIN, 0, 85, 1, 3000, 0.15f, 20, 55,
|
||||||
|
0.05f, 0.50f, 0.75f, 0.25f,
|
||||||
|
0.04f, 0.30f, 0.40f, 0.20f,
|
||||||
|
4.8f, 0, 0.10f, 1,
|
||||||
|
2, 0.40f, 0.45f,
|
||||||
|
0.86f, 0.0f, 1, 0.06f, 0, 0 } },
|
||||||
|
// 48 Delay Garden
|
||||||
|
{ "Delay Garden", "LEAD", { O_SAW, O_SAW, 12, 50, 1, 3200, 0.20f, 55, 40,
|
||||||
|
0.005f, 0.40f, 0.75f, 0.30f,
|
||||||
|
0.15f, 0.80f, 0.35f, 0.60f,
|
||||||
|
4.5f, 0, 0.15f, 0,
|
||||||
|
2, 0.55f, 0.50f,
|
||||||
|
0.85f, 0.0f, 1, 0.06f, 0, 0 } },
|
||||||
|
// 49 Cosmic Echo
|
||||||
|
{ "Cosmic Echo", "LEAD", { O_TRI, O_SIN, 0, 70, 1, 2600, 0.30f, 50, 35,
|
||||||
|
0.015f, 0.50f, 0.70f, 0.40f,
|
||||||
|
0.10f, 1.0f, 0.40f, 0.80f,
|
||||||
|
5.5f, 0, 0.12f, 1,
|
||||||
|
2, 0.70f, 0.75f,
|
||||||
|
0.84f, 0.0f, 1, 0.05f, 0, 0 } },
|
||||||
|
// 50 Stellar Delay
|
||||||
|
{ "Stellar Delay", "LEAD", { O_SAW, O_PUL, 10, 45, 1, 2800, 0.35f, 50, 35,
|
||||||
|
0.008f, 0.45f, 0.72f, 0.35f,
|
||||||
|
0.12f, 0.90f, 0.38f, 0.70f,
|
||||||
|
6.0f, 0, 0.10f, 0,
|
||||||
|
2, 0.60f, 0.65f,
|
||||||
|
0.85f, 0.0f, 1, 0.05f, 0, 0 } },
|
||||||
|
// 51 Pulse Train
|
||||||
|
{ "Pulse Train", "LEAD", { O_PUL, O_PUL, 0, 60, 1, 4000, 0.25f, 45, 55,
|
||||||
|
0.005f, 0.35f, 0.80f, 0.25f,
|
||||||
|
0.08f, 0.50f, 0.30f, 0.40f,
|
||||||
|
8.0f, 0, 0.08f, 0,
|
||||||
|
2, 0.65f, 0.45f,
|
||||||
|
0.86f, 0.0f, 1, 0.04f, 0, 0 } },
|
||||||
|
// == STRINGS ===========================================================
|
||||||
|
// 4 Movie Strings
|
||||||
|
{ "Movie Strings", "STRINGS", { O_SAW, O_SAW, 6, 45, 1, 2200, 0.15f, 65, 55,
|
||||||
|
0.50f, 1.2f, 0.80f, 0.70f,
|
||||||
|
0.40f, 0.8f, 0.35f, 0.5f,
|
||||||
|
3.1f, 0, 0.10f, 0,
|
||||||
|
3, 0.28f, 0.45f,
|
||||||
|
0.82f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
|
// 18 Strings Ensemble
|
||||||
|
{ "Strings Ens.", "STRINGS", { O_SAW, O_PUL, 8, 58, 1, 3000, 0.10f, 65, 55,
|
||||||
|
0.25f, 1.2f, 0.82f, 1.8f,
|
||||||
|
0.20f, 0.7f, 0.40f, 0.6f,
|
||||||
|
3.8f, 0, 0.10f, 0,
|
||||||
|
1, 0.30f, 0.30f,
|
||||||
|
0.80f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 31 Night Strings
|
||||||
|
{ "Night Strings","STRINGS", { O_SAW, O_TRI, 10, 52, 0, 2600, 0.10f, 60, 50,
|
||||||
|
1.10f, 2.6f, 0.85f, 2.0f,
|
||||||
|
0.55f, 1.6f, 0.40f, 1.1f,
|
||||||
|
3.2f, 0, 0.14f, 0,
|
||||||
|
3, 0.40f, 0.55f,
|
||||||
|
0.84f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 36 High Strings
|
||||||
|
{ "High Strings", "STRINGS", { O_SAW, O_PUL, 6, 60, 1, 3400, 0.10f, 55, 60,
|
||||||
|
0.20f, 1.0f, 0.80f, 1.6f,
|
||||||
|
0.15f, 0.60f, 0.40f, 0.7f,
|
||||||
|
4.2f, 0, 0.10f, 0,
|
||||||
|
1, 0.35f, 0.40f,
|
||||||
|
0.82f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 44 Frozen Strings
|
||||||
|
{ "Frozen Strings","STRINGS", { O_SAW, O_PUL, 10, 55, 1, 2400, 0.12f, 60, 55,
|
||||||
|
0.20f, 1.0f, 0.80f, 1.6f,
|
||||||
|
0.15f, 0.60f, 0.40f, 0.7f,
|
||||||
|
4.2f, 0, 0.10f, 0,
|
||||||
|
1, 0.35f, 0.40f,
|
||||||
|
0.82f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 58 Deep Strings
|
||||||
|
{ "Deep Strings", "STRINGS", { O_SAW, O_SAW, 12, 50, 1, 1800, 0.12f, 65, 50,
|
||||||
|
0.40f, 1.4f, 0.82f, 1.5f,
|
||||||
|
0.30f, 0.80f, 0.38f, 0.60f,
|
||||||
|
3.5f, 0, 0.12f, 0,
|
||||||
|
3, 0.38f, 0.50f,
|
||||||
|
0.82f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 59 Vinyl Strings
|
||||||
|
{ "Vinyl Strings","STRINGS", { O_SAW, O_TRI, 8, 48, 1, 2000, 0.15f, 60, 45,
|
||||||
|
0.35f, 1.2f, 0.78f, 1.4f,
|
||||||
|
0.25f, 0.70f, 0.35f, 0.55f,
|
||||||
|
3.2f, 0, 0.10f, 0,
|
||||||
|
1, 0.32f, 0.42f,
|
||||||
|
0.84f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 60 Brass Ensemble
|
||||||
|
{ "Brass Ens.", "STRINGS", { O_SAW, O_PUL, 5, 55, 1, 2600, 0.20f, 50, 50,
|
||||||
|
0.15f, 0.80f, 0.75f, 0.50f,
|
||||||
|
0.10f, 0.50f, 0.30f, 0.40f,
|
||||||
|
4.0f, 0, 0.08f, 0,
|
||||||
|
1, 0.30f, 0.35f,
|
||||||
|
0.82f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// == DUB ===============================================================
|
||||||
|
// 27 Techno Bass
|
||||||
|
{ "Techno Bass", "DUB", { O_SAW, O_PUL, 0, 88, 1, 480, 0.45f, 60, 10,
|
||||||
|
0.003f, 0.40f, 0.75f, 0.10f,
|
||||||
|
0.003f, 0.12f, 0.15f, 0.08f,
|
||||||
|
2.0f, 2, 0.35f, 0,
|
||||||
|
2, 0.32f, 0.28f,
|
||||||
|
0.92f, 0.0f, 1, 0.03f, 0, 0 } },
|
||||||
|
// 38 Dub Rez Bass
|
||||||
|
{ "Dub Rez Bass", "DUB", { O_SAW, O_PUL, -5, 60, 1, 500, 0.65f, 45, 5,
|
||||||
|
0.004f, 0.35f, 0.80f, 0.12f,
|
||||||
|
0.004f, 0.15f, 0.25f, 0.12f,
|
||||||
|
0.8f, 3, 0.30f, 0,
|
||||||
|
2, 0.40f, 0.36f,
|
||||||
|
0.90f, 0.0f, 1, 0.05f, 0, 0 } },
|
||||||
|
// 61 Dub Sub
|
||||||
|
{ "Dub Sub", "DUB", { O_SIN, O_SIN, 0, 100, 1, 350, 0.15f, 30, 15,
|
||||||
|
0.003f, 0.40f, 0.85f, 0.15f,
|
||||||
|
0.003f, 0.12f, 0.25f, 0.10f,
|
||||||
|
3.0f, 0, 0.05f, 0,
|
||||||
|
2, 0.45f, 0.35f,
|
||||||
|
0.90f, 0.0f, 1, 0.04f, 0, 0 } },
|
||||||
|
// 62 Dub Wobble
|
||||||
|
{ "Dub Wobble", "DUB", { O_SAW, O_PUL, 0, 75, 1, 400, 0.55f, 65, 20,
|
||||||
|
0.003f, 0.35f, 0.78f, 0.12f,
|
||||||
|
0.003f, 0.15f, 0.20f, 0.10f,
|
||||||
|
0.5f, 2, 0.40f, 0,
|
||||||
|
2, 0.50f, 0.40f,
|
||||||
|
0.90f, 0.0f, 1, 0.03f, 0, 0 } },
|
||||||
|
// 63 Dub Rumble
|
||||||
|
{ "Dub Rumble", "DUB", { O_SAW, O_SIN, -3, 65, 1, 380, 0.40f, 50, 10,
|
||||||
|
0.004f, 0.30f, 0.82f, 0.10f,
|
||||||
|
0.004f, 0.10f, 0.18f, 0.08f,
|
||||||
|
1.2f, 3, 0.30f, 0,
|
||||||
|
2, 0.55f, 0.45f,
|
||||||
|
0.88f, 0.0f, 1, 0.04f, 0, 0 } },
|
||||||
|
// 64 Dub Delay Bass
|
||||||
|
{ "Dub Delay Bs", "DUB", { O_PUL, O_SIN, 0, 78, 1, 420, 0.50f, 55, 15,
|
||||||
|
0.003f, 0.30f, 0.80f, 0.10f,
|
||||||
|
0.003f, 0.10f, 0.20f, 0.08f,
|
||||||
|
1.5f, 2, 0.35f, 0,
|
||||||
|
2, 0.60f, 0.50f,
|
||||||
|
0.90f, 0.0f, 1, 0.03f, 0, 0 } },
|
||||||
|
// == CHORDS ============================================================
|
||||||
|
// 65 Chord Stab
|
||||||
|
{ "Chord Stab", "CHORDS", { O_SAW, O_SAW, 6, 55, 1, 3200, 0.20f, 40, 30,
|
||||||
|
0.003f, 0.20f, 0.0f, 0.10f,
|
||||||
|
0.002f, 0.10f, 0.02f, 0.08f,
|
||||||
|
6.0f, 0, 0.05f, 2,
|
||||||
|
1, 0.35f, 0.25f,
|
||||||
|
0.84f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
|
// 66 Super Chord
|
||||||
|
{ "Super Chord", "CHORDS", { O_SAW, O_PUL, 10, 50, 1, 2800, 0.18f, 45, 35,
|
||||||
|
0.004f, 0.25f, 0.0f, 0.12f,
|
||||||
|
0.003f, 0.12f, 0.03f, 0.10f,
|
||||||
|
5.5f, 0, 0.06f, 2,
|
||||||
|
1, 0.40f, 0.30f,
|
||||||
|
0.84f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
|
// 67 Poly Brass
|
||||||
|
{ "Poly Brass", "CHORDS", { O_SAW, O_SAW, 8, 52, 1, 3000, 0.25f, 35, 40,
|
||||||
|
0.005f, 0.30f, 0.0f, 0.15f,
|
||||||
|
0.004f, 0.15f, 0.05f, 0.12f,
|
||||||
|
5.0f, 0, 0.08f, 2,
|
||||||
|
1, 0.35f, 0.28f,
|
||||||
|
0.82f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
|
// 68 Saw Stack
|
||||||
|
{ "Saw Stack", "CHORDS", { O_SAW, O_SAW, 14, 55, 1, 2600, 0.15f, 50, 30,
|
||||||
|
0.003f, 0.18f, 0.0f, 0.08f,
|
||||||
|
0.002f, 0.08f, 0.02f, 0.06f,
|
||||||
|
6.5f, 0, 0.04f, 2,
|
||||||
|
1, 0.30f, 0.22f,
|
||||||
|
0.82f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
|
// 69 Deep Chord
|
||||||
|
{ "Deep Chord", "CHORDS", { O_TRI, O_SAW, 4, 50, 1, 2200, 0.22f, 55, 35,
|
||||||
|
0.004f, 0.22f, 0.0f, 0.12f,
|
||||||
|
0.003f, 0.10f, 0.03f, 0.10f,
|
||||||
|
5.0f, 0, 0.06f, 2,
|
||||||
|
1, 0.38f, 0.28f,
|
||||||
|
0.84f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
|
// 70 Synth Stab
|
||||||
|
{ "Synth Stab", "CHORDS", { O_PUL, O_SAW, 5, 58, 1, 3500, 0.30f, 35, 25,
|
||||||
|
0.003f, 0.15f, 0.0f, 0.08f,
|
||||||
|
0.002f, 0.08f, 0.02f, 0.06f,
|
||||||
|
7.0f, 0, 0.04f, 2,
|
||||||
|
1, 0.32f, 0.20f,
|
||||||
|
0.86f, 0.0f, 12, 0.0f, 0, 0 } },
|
||||||
|
// == SYNTH =============================================================
|
||||||
|
// 71 Poly Sync
|
||||||
|
{ "Poly Sync", "SYNTH", { O_SAW, O_PUL, 8, 55, 1, 3400, 0.25f, 40, 35,
|
||||||
|
0.005f, 0.30f, 0.75f, 0.18f,
|
||||||
|
0.004f, 0.15f, 0.25f, 0.12f,
|
||||||
|
5.5f, 0, 0.12f, 0,
|
||||||
|
1, 0.32f, 0.28f,
|
||||||
|
0.84f, 0.0f, 10, 0.0f, 0, 0 } },
|
||||||
|
// 72 Dark Synth
|
||||||
|
{ "Dark Synth", "SYNTH", { O_SAW, O_SAW, 12, 48, 1, 2000, 0.35f, 60, 25,
|
||||||
|
0.008f, 0.35f, 0.70f, 0.20f,
|
||||||
|
0.006f, 0.20f, 0.30f, 0.15f,
|
||||||
|
4.0f, 0, 0.15f, 0,
|
||||||
|
1, 0.35f, 0.32f,
|
||||||
|
0.82f, 0.0f, 10, 0.0f, 0, 0 } },
|
||||||
|
// 73 Bright Synth
|
||||||
|
{ "Bright Synth", "SYNTH", { O_SAW, O_TRI, 10, 50, 1, 4200, 0.15f, 30, 45,
|
||||||
|
0.004f, 0.25f, 0.80f, 0.15f,
|
||||||
|
0.003f, 0.12f, 0.28f, 0.10f,
|
||||||
|
6.0f, 0, 0.10f, 1,
|
||||||
|
1, 0.28f, 0.25f,
|
||||||
|
0.85f, 0.0f, 10, 0.0f, 0, 0 } },
|
||||||
|
// 74 Mono Synth
|
||||||
|
{ "Mono Synth", "SYNTH", { O_PUL, O_SAW, 0, 60, 1, 3000, 0.30f, 45, 40,
|
||||||
|
0.003f, 0.22f, 0.78f, 0.15f,
|
||||||
|
0.003f, 0.10f, 0.25f, 0.10f,
|
||||||
|
5.0f, 0, 0.12f, 1,
|
||||||
|
0, 0.0f, 0.25f,
|
||||||
|
0.85f, 0.0f, 1, 0.06f, 0, 0 } },
|
||||||
|
// 75 Retro Synth
|
||||||
|
{ "Retro Synth", "SYNTH", { O_SAW, O_PUL, 6, 52, 1, 2800, 0.22f, 38, 50,
|
||||||
|
0.005f, 0.28f, 0.72f, 0.18f,
|
||||||
|
0.004f, 0.14f, 0.22f, 0.12f,
|
||||||
|
5.5f, 0, 0.10f, 0,
|
||||||
|
1, 0.30f, 0.30f,
|
||||||
|
0.84f, 0.0f, 10, 0.0f, 0, 0 } },
|
||||||
|
// 76 Sine Synth
|
||||||
|
{ "Sine Synth", "SYNTH", { O_SIN, O_TRI, 4, 60, 1, 3600, 0.18f, 35, 30,
|
||||||
|
0.006f, 0.30f, 0.78f, 0.20f,
|
||||||
|
0.005f, 0.18f, 0.28f, 0.15f,
|
||||||
|
4.5f, 0, 0.10f, 0,
|
||||||
|
1, 0.32f, 0.28f,
|
||||||
|
0.84f, 0.0f, 10, 0.0f, 0, 0 } },
|
||||||
|
// == ARP ===============================================================
|
||||||
|
// 77 Arp Saw
|
||||||
|
{ "Arp Saw", "ARP", { O_SAW, O_SAW, 6, 55, 1, 3800, 0.20f, 35, 45,
|
||||||
|
0.005f, 0.15f, 0.70f, 0.08f,
|
||||||
|
0.004f, 0.10f, 0.20f, 0.08f,
|
||||||
|
6.0f, 0, 0.10f, 0,
|
||||||
|
1, 0.28f, 0.20f,
|
||||||
|
0.84f, 0.0f, 10, 0.0f, 0, 0,
|
||||||
|
1, 2, 0, 2, 70 } },
|
||||||
|
// 78 Arp Pulse
|
||||||
|
{ "Arp Pulse", "ARP", { O_PUL, O_PUL, 4, 58, 1, 3200, 0.25f, 40, 50,
|
||||||
|
0.004f, 0.12f, 0.65f, 0.06f,
|
||||||
|
0.003f, 0.08f, 0.18f, 0.06f,
|
||||||
|
7.0f, 0, 0.08f, 0,
|
||||||
|
1, 0.30f, 0.18f,
|
||||||
|
0.84f, 0.0f, 10, 0.0f, 0, 0,
|
||||||
|
1, 2, 0, 2, 70 } },
|
||||||
|
// 79 Arp Sine
|
||||||
|
{ "Arp Sine", "ARP", { O_SIN, O_TRI, 0, 70, 1, 4000, 0.15f, 30, 35,
|
||||||
|
0.003f, 0.10f, 0.60f, 0.05f,
|
||||||
|
0.002f, 0.06f, 0.15f, 0.05f,
|
||||||
|
5.5f, 0, 0.06f, 0,
|
||||||
|
1, 0.25f, 0.15f,
|
||||||
|
0.86f, 0.0f, 10, 0.0f, 0, 0,
|
||||||
|
1, 1, 2, 2, 60 } },
|
||||||
|
// 80 Arp Space
|
||||||
|
{ "Arp Space", "ARP", { O_TRI, O_SAW, 8, 52, 1, 3000, 0.22f, 45, 40,
|
||||||
|
0.005f, 0.18f, 0.70f, 0.10f,
|
||||||
|
0.004f, 0.12f, 0.22f, 0.08f,
|
||||||
|
5.0f, 0, 0.12f, 0,
|
||||||
|
3, 0.45f, 0.40f,
|
||||||
|
0.82f, 0.0f, 10, 0.0f, 0, 0,
|
||||||
|
1, 4, 0, 2, 70 } },
|
||||||
|
// 81 Arp Trance
|
||||||
|
{ "Arp Trance", "ARP", { O_SAW, O_SAW, 10, 50, 1, 3500, 0.18f, 40, 50,
|
||||||
|
0.004f, 0.12f, 0.72f, 0.08f,
|
||||||
|
0.003f, 0.08f, 0.20f, 0.06f,
|
||||||
|
8.0f, 0, 0.10f, 0,
|
||||||
|
2, 0.40f, 0.35f,
|
||||||
|
0.84f, 0.0f, 10, 0.0f, 0, 0,
|
||||||
|
1, 2, 0, 3, 60 } },
|
||||||
|
// 82 Arp Square
|
||||||
|
{ "Arp Square", "ARP", { O_PUL, O_SAW, 0, 65, 1, 3000, 0.30f, 35, 45,
|
||||||
|
0.003f, 0.10f, 0.65f, 0.05f,
|
||||||
|
0.002f, 0.06f, 0.18f, 0.05f,
|
||||||
|
7.5f, 0, 0.06f, 0,
|
||||||
|
1, 0.30f, 0.18f,
|
||||||
|
0.85f, 0.0f, 10, 0.0f, 0, 0,
|
||||||
|
1, 2, 1, 2, 60 } },
|
||||||
|
// 83 Arp Pluck
|
||||||
|
{ "Arp Pluck", "ARP", { O_TRI, O_TRI, 3, 60, 1, 3200, 0.18f, 30, 40,
|
||||||
|
0.002f, 0.08f, 0.55f, 0.04f,
|
||||||
|
0.002f, 0.05f, 0.15f, 0.04f,
|
||||||
|
6.5f, 0, 0.05f, 0,
|
||||||
|
1, 0.28f, 0.15f,
|
||||||
|
0.86f, 0.0f, 10, 0.0f, 0, 0,
|
||||||
|
1, 1, 2, 2, 50 } },
|
||||||
|
// 84 Moon Pad
|
||||||
|
{ "Moon Pad", "POLY PAD", { O_SIN, O_TRI, 14, 55, 1, 2200, 0.30f, 55, 25,
|
||||||
|
1.5f, 2.8f, 0.88f, 3.2f,
|
||||||
|
1.0f, 2.0f, 0.40f, 2.2f,
|
||||||
|
0.4f, 1, 0.20f, 0,
|
||||||
|
3, 0.50f, 0.55f,
|
||||||
|
0.82f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 85 Orchestral
|
||||||
|
{ "Orchestral", "STRINGS", { O_SAW, O_PUL, 8, 55, 1, 2400, 0.12f, 60, 55,
|
||||||
|
0.30f, 1.1f, 0.80f, 1.8f,
|
||||||
|
0.20f, 0.70f, 0.38f, 0.60f,
|
||||||
|
3.6f, 0, 0.10f, 0,
|
||||||
|
3, 0.35f, 0.45f,
|
||||||
|
0.82f, 0.0f, 16, 0.0f, 0, 0 } },
|
||||||
|
// 86 Arp Bubble
|
||||||
|
{ "Arp Bubble", "ARP", { O_TRI, O_SIN, 0, 70, 1, 3800, 0.12f, 25, 30,
|
||||||
|
0.002f, 0.08f, 0.55f, 0.04f,
|
||||||
|
0.002f, 0.05f, 0.12f, 0.04f,
|
||||||
|
7.5f, 0, 0.05f, 0,
|
||||||
|
1, 0.25f, 0.12f,
|
||||||
|
0.86f, 0.0f, 10, 0.0f, 0, 0,
|
||||||
|
1, 1, 3, 2, 50 } },
|
||||||
|
// 87 Arp Pyramid
|
||||||
|
{ "Arp Pyramid", "ARP", { O_PUL, O_SAW, 0, 62, 1, 3000, 0.25f, 30, 45,
|
||||||
|
0.003f, 0.10f, 0.60f, 0.05f,
|
||||||
|
0.002f, 0.06f, 0.15f, 0.05f,
|
||||||
|
6.0f, 0, 0.06f, 0,
|
||||||
|
1, 0.28f, 0.15f,
|
||||||
|
0.85f, 0.0f, 10, 0.0f, 0, 0,
|
||||||
|
1, 3, 2, 3, 60 } },
|
||||||
|
// 88 Arp Sweep
|
||||||
|
{ "Arp Sweep", "ARP", { O_SAW, O_SAW, 8, 55, 1, 3400, 0.20f, 45, 35,
|
||||||
|
0.004f, 0.15f, 0.68f, 0.08f,
|
||||||
|
0.003f, 0.10f, 0.20f, 0.06f,
|
||||||
|
5.5f, 0, 0.10f, 0,
|
||||||
|
3, 0.42f, 0.35f,
|
||||||
|
0.83f, 0.0f, 10, 0.0f, 0, 0,
|
||||||
|
1, 4, 0, 2, 65 } },
|
||||||
|
// 89 Arp Chimes
|
||||||
|
{ "Arp Chimes", "ARP", { O_SIN, O_TRI, 12, 60, 1, 4200, 0.25f, 35, 30,
|
||||||
|
0.002f, 0.06f, 0.50f, 0.03f,
|
||||||
|
0.002f, 0.04f, 0.10f, 0.03f,
|
||||||
|
6.0f, 0, 0.04f, 0,
|
||||||
|
3, 0.38f, 0.40f,
|
||||||
|
0.84f, 0.0f, 10, 0.0f, 0, 0,
|
||||||
|
1, 2, 2, 2, 45 } },
|
||||||
|
// 90 Arp Midnight
|
||||||
|
{ "Arp Midnight", "ARP", { O_PUL, O_PUL, 6, 55, 1, 2600, 0.30f, 40, 40,
|
||||||
|
0.004f, 0.12f, 0.62f, 0.06f,
|
||||||
|
0.003f, 0.08f, 0.18f, 0.05f,
|
||||||
|
5.0f, 0, 0.08f, 0,
|
||||||
|
2, 0.35f, 0.48f,
|
||||||
|
0.85f, 0.0f, 10, 0.0f, 0, 0,
|
||||||
|
1, 2, 1, 2, 55 } },
|
||||||
|
// 91 Arp Cascade
|
||||||
|
{ "Arp Cascade", "ARP", { O_SAW, O_TRI, 10, 52, 1, 3200, 0.15f, 35, 45,
|
||||||
|
0.003f, 0.10f, 0.65f, 0.06f,
|
||||||
|
0.003f, 0.06f, 0.20f, 0.05f,
|
||||||
|
7.0f, 0, 0.06f, 0,
|
||||||
|
1, 0.30f, 0.28f,
|
||||||
|
0.85f, 0.0f, 10, 0.0f, 0, 0,
|
||||||
|
1, 2, 0, 3, 65 } },
|
||||||
};
|
};
|
||||||
|
|
||||||
PresetBank::PresetBank()
|
PresetBank::PresetBank()
|
||||||
{
|
{
|
||||||
patches.assign (defs, defs + juce::numElementsInArray (defs));
|
patches.assign (defs, defs + juce::numElementsInArray (defs));
|
||||||
|
buildCategoryIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PresetBank::buildCategoryIndex()
|
||||||
|
{
|
||||||
|
catIndex.names.clear();
|
||||||
|
catIndex.patchIndices.clear();
|
||||||
|
|
||||||
|
for (int i = 0; i < (int) patches.size(); ++i)
|
||||||
|
{
|
||||||
|
const juce::String cat (patches[i].category);
|
||||||
|
int idx = catIndex.names.indexOf (cat);
|
||||||
|
if (idx < 0)
|
||||||
|
{
|
||||||
|
idx = catIndex.names.size();
|
||||||
|
catIndex.names.add (cat);
|
||||||
|
catIndex.patchIndices.push_back ({});
|
||||||
|
}
|
||||||
|
catIndex.patchIndices[(size_t) idx].push_back (i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int PresetBank::findByName (const juce::String& name) const
|
int PresetBank::findByName (const juce::String& name) const
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,12 @@ struct Patch
|
||||||
float v[prm::num];
|
float v[prm::num];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct CategoryIndex
|
||||||
|
{
|
||||||
|
juce::StringArray names;
|
||||||
|
std::vector<std::vector<int>> patchIndices; // parallel to names
|
||||||
|
};
|
||||||
|
|
||||||
class PresetBank
|
class PresetBank
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -21,6 +27,10 @@ public:
|
||||||
|
|
||||||
void writeTo (int i, juce::AudioProcessorValueTreeState&) const;
|
void writeTo (int i, juce::AudioProcessorValueTreeState&) const;
|
||||||
|
|
||||||
|
const CategoryIndex& categories() const { return catIndex; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Patch> patches;
|
std::vector<Patch> patches;
|
||||||
|
CategoryIndex catIndex;
|
||||||
|
void buildCategoryIndex();
|
||||||
};
|
};
|
||||||
|
|
@ -59,7 +59,13 @@ SynthEngine::Voice* SynthEngine::acquireVoice()
|
||||||
return &voices[i];
|
return &voices[i];
|
||||||
|
|
||||||
stealIdx = (stealIdx + 1) % lim;
|
stealIdx = (stealIdx + 1) % lim;
|
||||||
return &voices[stealIdx];
|
Voice* v = &voices[stealIdx];
|
||||||
|
if (v->active)
|
||||||
|
{
|
||||||
|
v->amp.noteOff();
|
||||||
|
v->flt.noteOff();
|
||||||
|
}
|
||||||
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SynthEngine::noteOn (int midiNote, float velocity)
|
void SynthEngine::noteOn (int midiNote, float velocity)
|
||||||
|
|
@ -121,11 +127,16 @@ void SynthEngine::render (const VParams& p, float* left, float* right, int num)
|
||||||
: 1.0f - std::exp (-1.0f / (p.glide * (float) sr));
|
: 1.0f - std::exp (-1.0f / (p.glide * (float) sr));
|
||||||
const float detuneRatio = std::exp2f (p.detune / 1200.0f);
|
const float detuneRatio = std::exp2f (p.detune / 1200.0f);
|
||||||
const float tuneRatio = std::exp2f (p.tune / 12.0f);
|
const float tuneRatio = std::exp2f (p.tune / 12.0f);
|
||||||
const float maxFc = 18000.0f, minFc = 30.0f;
|
const float maxFc = 0.4f * (float) sr;
|
||||||
|
const float minFc = 30.0f;
|
||||||
|
|
||||||
for (int s = 0; s < num; ++s)
|
for (int s = 0; s < num; ++s)
|
||||||
{
|
{
|
||||||
float mixL = 0.0f, mixR = 0.0f;
|
float mixL = 0.0f, mixR = 0.0f;
|
||||||
|
int activeCount = 0;
|
||||||
|
for (auto& v : voices)
|
||||||
|
if (v.active) ++activeCount;
|
||||||
|
const float voiceGain = activeCount > 1 ? 1.0f / std::sqrtf ((float) activeCount) : 1.0f;
|
||||||
|
|
||||||
for (auto& v : voices)
|
for (auto& v : voices)
|
||||||
{
|
{
|
||||||
|
|
@ -213,7 +224,7 @@ void SynthEngine::render (const VParams& p, float* left, float* right, int num)
|
||||||
ampMod = 1.0f - p.lfoDepth * 0.5f * (1.0f + lfo);
|
ampMod = 1.0f - p.lfoDepth * 0.5f * (1.0f + lfo);
|
||||||
|
|
||||||
v.outSmooth += (filtered - v.outSmooth) * 0.12f;
|
v.outSmooth += (filtered - v.outSmooth) * 0.12f;
|
||||||
float o = v.outSmooth * (ae * ampMod) * v.vel;
|
float o = v.outSmooth * (ae * ampMod) * v.vel * voiceGain;
|
||||||
if (! std::isfinite (o)) o = 0.0f;
|
if (! std::isfinite (o)) o = 0.0f;
|
||||||
|
|
||||||
mixL += o * v.panL;
|
mixL += o * v.panL;
|
||||||
|
|
@ -228,8 +239,10 @@ void SynthEngine::render (const VParams& p, float* left, float* right, int num)
|
||||||
|
|
||||||
if (! std::isfinite (mixL)) mixL = 0.0f;
|
if (! std::isfinite (mixL)) mixL = 0.0f;
|
||||||
if (! std::isfinite (mixR)) mixR = 0.0f;
|
if (! std::isfinite (mixR)) mixR = 0.0f;
|
||||||
mixL = std::tanh (mixL);
|
|
||||||
mixR = std::tanh (mixR);
|
// Soft limiter before tanh for cleaner saturation
|
||||||
|
mixL = std::tanh (mixL * 0.8f);
|
||||||
|
mixR = std::tanh (mixR * 0.8f);
|
||||||
|
|
||||||
left[s] += mixL;
|
left[s] += mixL;
|
||||||
right[s] += mixR;
|
right[s] += mixR;
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ struct VParams
|
||||||
float fxType = 1, fxAmt = 0.30f, fxTime = 0.28f;
|
float fxType = 1, fxAmt = 0.30f, fxTime = 0.28f;
|
||||||
float vol = 0.85f, pan = 0.0f, poly = 10.f, glide = 0.f, tune = 0.f;
|
float vol = 0.85f, pan = 0.0f, poly = 10.f, glide = 0.f, tune = 0.f;
|
||||||
float hq = 0.0f;
|
float hq = 0.0f;
|
||||||
|
float arpOn = 0, arpRate = 2, arpPattern = 0, arpOct = 1, arpGate = 70;
|
||||||
|
|
||||||
void update (const juce::AudioProcessorValueTreeState& apvts)
|
void update (const juce::AudioProcessorValueTreeState& apvts)
|
||||||
{
|
{
|
||||||
|
|
@ -30,6 +31,11 @@ struct VParams
|
||||||
vol = get ("vol"); pan = get ("pan"); poly = get ("poly");
|
vol = get ("vol"); pan = get ("pan"); poly = get ("poly");
|
||||||
glide = get ("glide"); tune = get ("tune");
|
glide = get ("glide"); tune = get ("tune");
|
||||||
hq = get ("hq");
|
hq = get ("hq");
|
||||||
|
arpOn = get ("arpon");
|
||||||
|
arpRate = get ("arprate");
|
||||||
|
arpPattern = get ("arppattern");
|
||||||
|
arpOct = get ("arpoct");
|
||||||
|
arpGate = get ("arpgate");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
BIN
b2.png
BIN
b2.png
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 MiB |
BIN
b3.png
Normal file
BIN
b3.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 MiB |
BIN
shot.png
BIN
shot.png
Binary file not shown.
|
Before Width: | Height: | Size: 662 KiB After Width: | Height: | Size: 395 KiB |
3
todo.txt
Normal file
3
todo.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
- make sure all presets use lowpass, not bandpass, also check why bandpass makes the overall volume MUCH lower
|
||||||
|
- move all submenus under "settings" into the main menu; make a new submenu for the patches, and put all the patches categories in there
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue