Update the piano

This commit is contained in:
Jean Pierre Cimalando 2020-10-14 15:29:19 +02:00
parent 5cd6a514ae
commit 39a4df017e
8 changed files with 343 additions and 234 deletions

View file

@ -34,6 +34,8 @@ add_library(sfizz_editor STATIC EXCLUDE_FROM_ALL
src/editor/EditorController.h
src/editor/GUIComponents.h
src/editor/GUIComponents.cpp
src/editor/GUIPiano.h
src/editor/GUIPiano.cpp
src/editor/NativeHelpers.h
src/editor/NativeHelpers.cpp
src/editor/layout/main.hpp

View file

@ -56,7 +56,7 @@ widget_class mainView {open
}
Fl_Box sfzFileLabel_ {
label {DefaultInstrument.sfz}
comment {tag=kTagLoadSfzFile} selected
comment {tag=kTagLoadSfzFile}
xywh {195 11 250 31} labelsize 20 align 20
class ClickableLabel
}
@ -138,7 +138,7 @@ widget_class mainView {open
}
}
Fl_Group {subPanels_[kPanelGeneral]} {
xywh {5 110 791 285} hide
xywh {5 110 791 285}
class LogicalGroup
} {
Fl_Group {} {open
@ -212,8 +212,8 @@ widget_class mainView {open
}
}
}
Fl_Group {subPanels_[kPanelSettings]} {
xywh {5 109 790 286}
Fl_Group {subPanels_[kPanelSettings]} {open
xywh {5 109 790 286} hide
class LogicalGroup
} {
Fl_Group {} {
@ -305,8 +305,8 @@ widget_class mainView {open
}
}
}
Fl_Box {} {
xywh {5 400 790 70}
Fl_Box piano_ {selected
xywh {5 400 790 70} labelsize 12
class Piano
}
}

View file

@ -8,6 +8,7 @@
#include "EditorController.h"
#include "EditIds.h"
#include "GUIComponents.h"
#include "GUIPiano.h"
#include "NativeHelpers.h"
#include <absl/strings/string_view.h>
#include <absl/strings/match.h>
@ -96,6 +97,8 @@ struct Editor::Impl : EditorController::Receiver, IControlListener {
SActionMenu* fileOperationsMenu_ = nullptr;
SPiano* piano_ = nullptr;
void uiReceiveValue(EditId id, const EditValue& v) override;
void createFrameContents();
@ -554,8 +557,10 @@ void Editor::Impl::createFrameContents()
auto createNextFileButton = [&createGlyphButton](const CRect& bounds, int tag, const char*, CHoriTxtAlign, int fontsize) {
return createGlyphButton(u8"\ue0da", bounds, tag, fontsize);
};
auto createPiano = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int) {
auto createPiano = [](const CRect& bounds, int, const char*, CHoriTxtAlign, int fontsize) {
SPiano* piano = new SPiano(bounds);
auto font = owned(new CFontDesc("Roboto", fontsize));
piano->setFont(font);
return piano;
};
auto createChevronDropDown = [this, &theme](const CRect& bounds, int, const char*, CHoriTxtAlign, int fontsize) {
@ -693,6 +698,23 @@ void Editor::Impl::createFrameContents()
menu->addEntry("Edit file", kTagEditSfzFile);
}
if (SPiano* piano = piano_) {
piano->onKeyPressed = [this](unsigned key, float vel) {
uint8_t msg[3];
msg[0] = 0x90;
msg[1] = static_cast<uint8_t>(key);
msg[2] = static_cast<uint8_t>(std::max(1, static_cast<int>(vel * 127)));
ctrl_->uiSendMIDI(msg, sizeof(msg));
};
piano->onKeyReleased = [this](unsigned key, float vel) {
uint8_t msg[3];
msg[0] = 0x80;
msg[1] = static_cast<uint8_t>(key);
msg[2] = static_cast<uint8_t>(vel * 127);
ctrl_->uiSendMIDI(msg, sizeof(msg));
};
}
///
CViewContainer* panel;
activePanel_ = 0;

View file

@ -154,201 +154,6 @@ bool SFileDropTarget::isFileDrop(IDataPackage* package)
package->getDataType(0) == IDataPackage::kFilePath;
}
///
SPiano::SPiano(const CRect& bounds)
: CView(bounds), font_(kNormalFont)
{
}
void SPiano::setFont(CFontRef font)
{
font_ = font;
invalid();
}
void SPiano::clearKeyRanges()
{
keyInRange_.reset();
}
void SPiano::addKeyRange(int start, int end)
{
start = std::min(127, std::max(0, start));
end = std::min(127, std::max(0, end));
for (int x = start; x <= end; ++x)
keyInRange_.set(x);
}
CCoord SPiano::getKeyWidth()
{
return 6.0;
}
CCoord SPiano::getKeySwitchesHeight()
{
return 20.0;
}
CCoord SPiano::getKeyRangesHeight()
{
return 11.0;
}
CCoord SPiano::getKeysHeight() const
{
return getHeight() -
(getKeySwitchesHeight() + getKeyRangesHeight() + getOctavesHeight());
}
CCoord SPiano::getOctavesHeight() const
{
return font_->getSize();
}
void SPiano::getZoneDimensions(
CRect* pKeySwitches,
CRect* pKeyboard,
CRect* pKeyRanges,
CRect* pOctaves)
{
CRect bounds = getViewSize();
CRect keySwitches(bounds);
keySwitches.setHeight(getKeySwitchesHeight());
CRect keyboard(bounds);
keyboard.top = keySwitches.bottom;
keyboard.setHeight(getKeysHeight());
CRect keyRanges(bounds);
keyRanges.top = keyboard.bottom;
keyRanges.setHeight(getKeyRangesHeight());
CRect octaves(bounds);
octaves.top = keyRanges.bottom;
octaves.setHeight(getOctavesHeight());
// apply some paddings
keySwitches.extend(-2.0, -2.0);
keyboard.extend(-2.0, -2.0);
keyRanges.extend(-2.0, -4.0);
octaves.extend(-2.0, -2.0);
// offsets for centered keyboard
CCoord keyWidth = getKeyWidth();
CCoord offset = std::round((keyboard.getWidth() - (128.0 * keyWidth)) * 0.5);
if (offset > 0) {
keySwitches.extend(-offset, 0.0);
keyboard.extend(-offset, 0.0);
keyRanges.extend(-offset, 0.0);
octaves.extend(-offset, 0.0);
}
//
if (pKeySwitches)
*pKeySwitches = keySwitches;
if (pKeyboard)
*pKeyboard = keyboard;
if (pKeyRanges)
*pKeyRanges = keyRanges;
if (pOctaves)
*pOctaves = octaves;
}
void SPiano::draw(CDrawContext* dc)
{
CRect bounds = getViewSize();
dc->setDrawMode(kAntiAliasing);
SharedPointer<CGraphicsPath> path;
path = owned(dc->createGraphicsPath());
path->addRoundRect(bounds, 5.0);
dc->setFillColor(CColor(0xca, 0xca, 0xca));
dc->drawGraphicsPath(path, CDrawContext::kPathFilled);
//
CRect rectKeySwitches;
CRect rectKeyboard;
CRect rectKeyRanges;
CRect rectOctaves;
getZoneDimensions(&rectKeySwitches, &rectKeyboard, &rectKeyRanges, &rectOctaves);
//
path = owned(dc->createGraphicsPath());
path->addRoundRect(rectKeyboard, 1.0);
dc->setFillColor(CColor(0xff, 0xff, 0xff));
dc->drawGraphicsPath(path, CDrawContext::kPathFilled);
CCoord keyWidth = getKeyWidth();
for (int key = 0; key < 128; ++key) {
CCoord keyX = rectKeyboard.left + key * keyWidth;
int key12 = key % 12;
if (key12 == 1 || key12 == 3 ||
key12 == 6 || key12 == 8 || key12 == 10)
{
CRect blackRect(keyX, rectKeyboard.top + 2, keyX + keyWidth, rectKeyboard.bottom - 2);
path = owned(dc->createGraphicsPath());
path->addRoundRect(blackRect, 1.0);
dc->setFillColor(CColor(0x02, 0x02, 0x02));
dc->drawGraphicsPath(path, CDrawContext::kPathFilled);
}
if (key != 0 && key12 == 0) {
dc->setLineWidth(1.5);
dc->setFrameColor(CColor(0x63, 0x63, 0x63));
dc->drawLine(CPoint(keyX, rectKeyboard.top), CPoint(keyX, rectKeyboard.bottom));
}
if (key12 == 5) {
CCoord pad = rectKeyboard.getHeight() * 0.4;
dc->setLineWidth(1.0);
dc->setFrameColor(CColor(0x63, 0x63, 0x63));
dc->drawLine(CPoint(keyX, rectKeyboard.top + pad), CPoint(keyX, rectKeyboard.bottom - pad));
}
}
//
for (int rangeStart = 0; rangeStart < 128;)
{
if (!keyInRange_[rangeStart]) {
++rangeStart;
}
else {
int rangeEnd = rangeStart;
while (rangeEnd + 1 < 128 && keyInRange_[rangeEnd + 1])
++rangeEnd;
CCoord rangeStartX = rectKeyRanges.left + rangeStart * keyWidth;
CCoord rangeEndX = rectKeyRanges.left + (rangeEnd + 1.0) * keyWidth;
CRect rectRange(rangeStartX, rectKeyRanges.top, rangeEndX, rectKeyRanges.bottom);
path = owned(dc->createGraphicsPath());
path->addRoundRect(rectRange, 2.0);
dc->setFillColor(CColor(0x0f, 0x0f, 0x0f));
dc->drawGraphicsPath(path, CDrawContext::kPathFilled);
rangeStart = rangeEnd + 1;
}
}
//
for (int key = 0; key < 128; ++key) {
CCoord keyX = rectOctaves.left + key * keyWidth;
int key12 = key % 12;
if (key12 == 0) {
CRect textRect(keyX, rectOctaves.top, keyX + 12 * keyWidth, rectOctaves.bottom);
dc->setFont(font_);
dc->setFontColor(CColor(0x63, 0x63, 0x63));
dc->drawString(std::to_string(key / 12 - 1).c_str(), textRect, kLeftText);
}
}
//
}
///
SValueMenu::SValueMenu(const CRect& bounds, IControlListener* listener, int32_t tag)
: CParamDisplay(bounds), menuListener_(owned(new MenuListener(*this)))

View file

@ -82,36 +82,6 @@ private:
FileDropFunction dropFunction_;
};
///
class SPiano : public CView {
public:
explicit SPiano(const CRect& bounds);
CFontRef getFont() const { return font_; }
void setFont(CFontRef font);
void clearKeyRanges();
void addKeyRange(int start, int end);
protected:
static CCoord getKeyWidth();
static CCoord getKeySwitchesHeight();
static CCoord getKeyRangesHeight();
CCoord getKeysHeight() const;
CCoord getOctavesHeight() const;
void getZoneDimensions(
CRect* pKeySwitches,
CRect* pKeyboard,
CRect* pKeyRanges,
CRect* pOctaves);
void draw(CDrawContext* dc) override;
private:
SharedPointer<CFontDesc> font_;
std::bitset<128> keyInRange_;
};
///
class SValueMenu : public CParamDisplay {
public:

View file

@ -0,0 +1,237 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#include "GUIPiano.h"
#include "utility/vstgui_before.h"
#include "vstgui/lib/cdrawcontext.h"
#include "vstgui/lib/cgraphicspath.h"
#include "utility/vstgui_after.h"
#include <algorithm>
static constexpr CCoord keyoffs[12] = {0, 0.6, 1, 1.8, 2, 3,
3.55, 4, 4.7, 5, 5.85, 6};
static constexpr bool black[12] = {0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0};
SPiano::SPiano(CRect bounds)
: CView(bounds)
{
setNumOctaves(10);
}
void SPiano::setFont(CFontRef font)
{
font_ = font;
getDimensions(true);
invalid();
}
void SPiano::setNumOctaves(unsigned octs)
{
keyval_.resize(octs * 12);
octs_ = std::max(1u, octs);
getDimensions(true);
invalid();
}
void SPiano::draw(CDrawContext* dc)
{
const Dimensions dim = getDimensions(false);
const unsigned octs = octs_;
const unsigned keyCount = octs * 12;
dc->setDrawMode(kAntiAliasing);
if (backgroundFill_.alpha > 0) {
SharedPointer<CGraphicsPath> path;
path = owned(dc->createGraphicsPath());
path->addRoundRect(dim.bounds, backgroundRadius_);
dc->setFillColor(CColor(0xca, 0xca, 0xca));
dc->drawGraphicsPath(path, CDrawContext::kPathFilled);
}
for (unsigned key = 0; key < keyCount; ++key) {
if (!black[key % 12]) {
CRect rect = keyRect(key);
CColor keycolor = whiteFill_;
if (keyval_[key])
keycolor = pressedFill_;
dc->setFillColor(keycolor);
dc->drawRect(rect, kDrawFilled);
}
}
dc->setFrameColor(outline_);
dc->drawLine(dim.keyBounds.getTopLeft(), dim.keyBounds.getBottomLeft());
for (unsigned key = 0; key < keyCount; ++key) {
if (!black[key % 12]) {
CRect rect = keyRect(key);
dc->drawLine(rect.getTopRight(), rect.getBottomRight());
}
}
for (unsigned key = 0; key < keyCount; ++key) {
if (black[key % 12]) {
CRect rect = keyRect(key);
CColor keycolor = blackFill_;
if (keyval_[key])
keycolor = pressedFill_;
dc->setFillColor(keycolor);
dc->drawRect(rect, kDrawFilled);
dc->setFrameColor(outline_);
dc->drawRect(rect);
}
}
if (const CFontRef& font = font_) {
for (unsigned o = 0; o < octs; ++o) {
CRect rect = keyRect(o * 12);
CRect textRect(
rect.left, dim.labelBounds.top,
rect.right, dim.labelBounds.bottom);
dc->setFont(font_);
dc->setFontColor(labelStroke_);
std::string text = std::to_string(static_cast<int>(o) - 1);
dc->drawString(text.c_str(), textRect, kCenterText);
}
}
{
dc->setFrameColor(outline_);
dc->drawLine(dim.keyBounds.getTopLeft(), dim.keyBounds.getTopRight());
dc->setFrameColor(shadeOutline_);
dc->drawLine(dim.keyBounds.getBottomLeft(), dim.keyBounds.getBottomRight());
}
dc->setFrameColor(outline_);
}
CMouseEventResult SPiano::onMouseDown(CPoint& where, const CButtonState& buttons)
{
unsigned key = keyAtPos(where);
if (key != ~0u) {
keyval_[key] = 1;
mousePressedKey_ = key;
if (onKeyPressed)
onKeyPressed(key, mousePressVelocity(key, where.y));
invalid();
return kMouseEventHandled;
}
return CView::onMouseDown(where, buttons);
}
CMouseEventResult SPiano::onMouseUp(CPoint& where, const CButtonState& buttons)
{
unsigned key = mousePressedKey_;
if (key != ~0u) {
keyval_[key] = 0;
if (onKeyReleased)
onKeyReleased(key, mousePressVelocity(key, where.y));
mousePressedKey_ = ~0u;
invalid();
return kMouseEventHandled;
}
return CView::onMouseUp(where, buttons);
}
CMouseEventResult SPiano::onMouseMoved(CPoint& where, const CButtonState& buttons)
{
if (mousePressedKey_ != ~0u) {
unsigned key = keyAtPos(where);
if (mousePressedKey_ != key) {
keyval_[mousePressedKey_] = 0;
if (onKeyReleased)
onKeyReleased(mousePressedKey_, mousePressVelocity(key, where.y));
// mousePressedKey_ = ~0u;
if (key != ~0u) {
keyval_[key] = 1;
mousePressedKey_ = key;
if (onKeyPressed)
onKeyPressed(key, mousePressVelocity(key, where.y));
}
invalid();
}
return kMouseEventHandled;
}
return CView::onMouseMoved(where, buttons);
}
const SPiano::Dimensions& SPiano::getDimensions(bool forceUpdate) const
{
if (!forceUpdate && dim_.bounds == getViewSize())
return dim_;
Dimensions dim;
dim.bounds = getViewSize();
dim.paddedBounds = CRect(dim.bounds)
.extend(-innerPaddingX_, -innerPaddingY_);
CCoord keyHeight = std::floor(dim.paddedBounds.getHeight());
CCoord fontHeight = font_ ? font_->getSize() : 0.0;
keyHeight -= spacingY_ + fontHeight;
dim.keyBounds = CRect(dim.paddedBounds)
.setHeight(keyHeight);
dim.keyWidth = static_cast<unsigned>(
dim.paddedBounds.getWidth() / octs_ / 7.0);
dim.keyBounds.setWidth(dim.keyWidth * octs_ * 7.0);
dim.keyBounds.offset(
0.5 * (dim.paddedBounds.getWidth() - dim.keyBounds.getWidth()), 0.0);
if (!font_)
dim.labelBounds = CRect();
else
dim.labelBounds = CRect(
dim.keyBounds.left, dim.keyBounds.bottom,
dim.keyBounds.right, dim.keyBounds.bottom + (spacingY_ + fontHeight));
dim_ = dim;
return dim_;
}
CRect SPiano::keyRect(const Dimensions& dim, unsigned key)
{
unsigned oct = key / 12;
unsigned note = key % 12;
unsigned keyw = dim.keyWidth;
unsigned keyh = static_cast<unsigned>(dim.keyBounds.getHeight());
CCoord octwidth = (keyoffs[11] + 1.0) * keyw;
CCoord octx = octwidth * oct;
CCoord notex = octx + keyoffs[note] * keyw;
CCoord notew = black[note] ? (0.6 * keyw) : keyw;
CCoord noteh = black[note] ? (0.6 * keyh) : keyh;
return CRect(notex, 0.0, notex + notew, noteh).offset(dim.keyBounds.getTopLeft());
}
CRect SPiano::keyRect(unsigned key) const
{
return keyRect(getDimensions(false), key);
}
unsigned SPiano::keyAtPos(CPoint pos) const
{
const unsigned octs = octs_;
for (unsigned key = 0; key < octs * 12; ++key) {
if (black[key % 12]) {
if (keyRect(key).pointInside(pos))
return key;
}
}
for (unsigned key = 0; key < octs * 12; ++key) {
if (!black[key % 12]) {
if (keyRect(key).pointInside(pos))
return key;
}
}
return ~0u;
}
float SPiano::mousePressVelocity(unsigned key, CCoord posY)
{
const CRect rect = keyRect(key);
CCoord value = (posY - rect.top) / rect.getHeight();
return std::max(0.0f, std::min(1.0f, static_cast<float>(value)));
}

View file

@ -0,0 +1,72 @@
// SPDX-License-Identifier: BSD-2-Clause
// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz
#pragma once
#include "utility/vstgui_before.h"
#include "vstgui/lib/cview.h"
#include "vstgui/lib/ccolor.h"
#include "utility/vstgui_after.h"
#include <functional>
#include <vector>
using namespace VSTGUI;
class SPiano : public CView {
public:
explicit SPiano(CRect bounds);
CFontRef getFont() const { return font_; }
void setFont(CFontRef font);
unsigned getNumOctaves() const { return octs_; }
void setNumOctaves(unsigned octs);
std::function<void(unsigned, float)> onKeyPressed;
std::function<void(unsigned, float)> onKeyReleased;
protected:
void draw(CDrawContext* dc) override;
CMouseEventResult onMouseDown(CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseUp(CPoint& where, const CButtonState& buttons) override;
CMouseEventResult onMouseMoved(CPoint& where, const CButtonState& buttons) override;
private:
struct Dimensions {
CRect bounds {};
CRect paddedBounds {};
CRect keyBounds {};
unsigned keyWidth {};
CRect labelBounds {};
};
const Dimensions& getDimensions(bool forceUpdate) const;
static CRect keyRect(const Dimensions& dim, unsigned key);
CRect keyRect(unsigned key) const;
unsigned keyAtPos(CPoint pos) const;
float mousePressVelocity(unsigned key, CCoord posY);
private:
unsigned octs_ {};
std::vector<unsigned> keyval_;
unsigned mousePressedKey_ = ~0u;
CCoord innerPaddingX_ = 4.0;
CCoord innerPaddingY_ = 4.0;
CCoord spacingY_ = 4.0;
CColor backgroundFill_ { 0xca, 0xca, 0xca, 0xff };
float backgroundRadius_ = 5.0;
CColor whiteFill_ { 0xee, 0xee, 0xec, 0xff };
CColor blackFill_ { 0x2e, 0x34, 0x36, 0xff };
CColor pressedFill_ { 0xa0, 0xa0, 0xa0, 0xff };
CColor outline_ { 0x00, 0x00, 0x00, 0xff };
CColor shadeOutline_ { 0x80, 0x80, 0x80, 0xff };
CColor labelStroke_ { 0x63, 0x63, 0x63, 0xff };
mutable Dimensions dim_;
SharedPointer<CFontDesc> font_;
};

View file

@ -69,7 +69,6 @@ enterTheme(defaultTheme);
LogicalGroup* const view__28 = createLogicalGroup(CRect(5, 110, 796, 395), -1, "", kCenterText, 14);
subPanels_[kPanelGeneral] = view__28;
view__0->addView(view__28);
view__28->setVisible(false);
RoundedGroup* const view__29 = createRoundedGroup(CRect(0, 0, 175, 280), -1, "", kCenterText, 14);
view__28->addView(view__29);
Label* const view__30 = createLabel(CRect(15, 10, 75, 35), -1, "Curves:", kLeftText, 14);
@ -108,6 +107,7 @@ view__41->addView(view__42);
LogicalGroup* const view__43 = createLogicalGroup(CRect(5, 109, 795, 395), -1, "", kCenterText, 14);
subPanels_[kPanelSettings] = view__43;
view__0->addView(view__43);
view__43->setVisible(false);
TitleGroup* const view__44 = createTitleGroup(CRect(255, 26, 535, 126), -1, "Engine", kCenterText, 12);
view__43->addView(view__44);
ValueMenu* const view__45 = createValueMenu(CRect(25, 60, 85, 85), kTagSetNumVoices, "", kCenterText, 12);
@ -150,5 +150,6 @@ view__51->addView(view__59);
ValueMenu* const view__60 = createValueMenu(CRect(170, 60, 200, 85), kTagSetScalaRootKey, "", kCenterText, 12);
scalaRootOctaveSlider_ = view__60;
view__51->addView(view__60);
Piano* const view__61 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 14);
Piano* const view__61 = createPiano(CRect(5, 400, 795, 470), -1, "", kCenterText, 12);
piano_ = view__61;
view__0->addView(view__61);