Initial KeymapEditor implementation

This commit is contained in:
Jack Andersen 2018-08-05 18:20:42 -10:00
parent 2abed18784
commit 409d52c120
22 changed files with 1158 additions and 231 deletions

View File

@ -195,6 +195,7 @@ void ADSRView::mousePressEvent(QMouseEvent* ev)
if (dists[minDist] < 10.0) if (dists[minDist] < 10.0)
{ {
++m_cycleIdx;
m_dragPoint = minDist; m_dragPoint = minDist;
mouseMoveEvent(ev); mouseMoveEvent(ev);
} }
@ -235,17 +236,18 @@ void ADSRView::mouseMoveEvent(QMouseEvent* ev)
{ {
qreal newAttack = std::max(0.0, (ev->localPos().x() - 30.0) / (width() - 30.0) * totalTime); qreal newAttack = std::max(0.0, (ev->localPos().x() - 30.0) / (width() - 30.0) * totalTime);
qreal delta = newAttack - aTime; qreal delta = newAttack - aTime;
ctrls->setAttackAndDecay(newAttack, std::max(0.0, ctrls->m_decay->value() - delta)); ctrls->setAttackAndDecay(newAttack, std::max(0.0, ctrls->m_decay->value() - delta), m_cycleIdx);
} }
else if (m_dragPoint == 1) else if (m_dragPoint == 1)
{ {
qreal newDecay = std::max(0.0, (ev->localPos().x() - 30.0) * totalTime / (width() - 30.0) - aTime); qreal newDecay = std::max(0.0, (ev->localPos().x() - 30.0) * totalTime / (width() - 30.0) - aTime);
qreal newSustain = (-ev->localPos().y() + (height() - 16.0)) / (height() - 16.0); qreal newSustain = (-ev->localPos().y() + (height() - 16.0)) / (height() - 16.0);
ctrls->setDecayAndSustain(newDecay, newSustain * 100.0); ctrls->setDecayAndSustain(newDecay, newSustain * 100.0, m_cycleIdx);
} }
else if (m_dragPoint == 2) else if (m_dragPoint == 2)
{ {
qreal newRelease = std::max(0.0, (width() - 30.0) * (adTime + 1.0) / (ev->localPos().x() - 30.0) - (adTime + 1.0)); qreal newRelease = std::max(0.0, (width() - 30.0) * (adTime + 1.0) / (ev->localPos().x() - 30.0) - (adTime + 1.0));
ctrls->setRelease(newRelease, m_cycleIdx);
ctrls->m_release->setValue(newRelease); ctrls->m_release->setValue(newRelease);
} }
} }
@ -522,11 +524,13 @@ class ADSRAttackAndDecayUndoCommand : public EditorUndoCommand
{ {
double m_redoAttack, m_redoDecay; double m_redoAttack, m_redoDecay;
double m_undoAttack, m_undoDecay; double m_undoAttack, m_undoDecay;
uint64_t m_cycleCount;
bool m_undid = false; bool m_undid = false;
public: public:
ADSRAttackAndDecayUndoCommand(double redoAttack, double redoDecay, amuse::ObjToken<ProjectModel::ADSRNode> node) ADSRAttackAndDecayUndoCommand(double redoAttack, double redoDecay, uint64_t cycleCount,
amuse::ObjToken<ProjectModel::ADSRNode> node)
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Attack/Decay")), : EditorUndoCommand(node.get(), ADSRControls::tr("Change Attack/Decay")),
m_redoAttack(redoAttack), m_redoDecay(redoDecay) {} m_redoAttack(redoAttack), m_redoDecay(redoDecay), m_cycleCount(cycleCount) {}
void undo() void undo()
{ {
m_undid = true; m_undid = true;
@ -569,7 +573,8 @@ public:
} }
bool mergeWith(const QUndoCommand* other) bool mergeWith(const QUndoCommand* other)
{ {
if (other->id() == id()) if (other->id() == id() && m_cycleCount ==
static_cast<const ADSRAttackAndDecayUndoCommand*>(other)->m_cycleCount)
{ {
m_redoAttack = static_cast<const ADSRAttackAndDecayUndoCommand*>(other)->m_redoAttack; m_redoAttack = static_cast<const ADSRAttackAndDecayUndoCommand*>(other)->m_redoAttack;
m_redoDecay = static_cast<const ADSRAttackAndDecayUndoCommand*>(other)->m_redoDecay; m_redoDecay = static_cast<const ADSRAttackAndDecayUndoCommand*>(other)->m_redoDecay;
@ -580,7 +585,7 @@ public:
int id() const { return int(Id::ADSRAttackAndDecay); } int id() const { return int(Id::ADSRAttackAndDecay); }
}; };
void ADSRControls::setAttackAndDecay(double attack, double decay) void ADSRControls::setAttackAndDecay(double attack, double decay, uint64_t cycleCount)
{ {
m_enableUpdate = false; m_enableUpdate = false;
m_attack->setValue(attack); m_attack->setValue(attack);
@ -588,7 +593,8 @@ void ADSRControls::setAttackAndDecay(double attack, double decay)
m_enableUpdate = true; m_enableUpdate = true;
ADSRView* view = getEditor()->m_adsrView; ADSRView* view = getEditor()->m_adsrView;
g_MainWindow->pushUndoCommand(new ADSRAttackAndDecayUndoCommand(attack, decay, view->m_node)); g_MainWindow->pushUndoCommand(new ADSRAttackAndDecayUndoCommand(m_attack->value(), m_decay->value(),
cycleCount, view->m_node));
view->update(); view->update();
} }
@ -596,11 +602,13 @@ class ADSRDecayAndSustainUndoCommand : public EditorUndoCommand
{ {
double m_redoDecay, m_redoSustain; double m_redoDecay, m_redoSustain;
double m_undoDecay, m_undoSustain; double m_undoDecay, m_undoSustain;
uint64_t m_cycleCount;
bool m_undid = false; bool m_undid = false;
public: public:
ADSRDecayAndSustainUndoCommand(double redoDecay, double redoSustain, amuse::ObjToken<ProjectModel::ADSRNode> node) ADSRDecayAndSustainUndoCommand(double redoDecay, double redoSustain, uint64_t cycleCount,
amuse::ObjToken<ProjectModel::ADSRNode> node)
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Decay/Sustain")), : EditorUndoCommand(node.get(), ADSRControls::tr("Change Decay/Sustain")),
m_redoDecay(redoDecay), m_redoSustain(redoSustain) {} m_redoDecay(redoDecay), m_redoSustain(redoSustain), m_cycleCount(cycleCount) {}
void undo() void undo()
{ {
m_undid = true; m_undid = true;
@ -643,7 +651,8 @@ public:
} }
bool mergeWith(const QUndoCommand* other) bool mergeWith(const QUndoCommand* other)
{ {
if (other->id() == id()) if (other->id() == id() && m_cycleCount ==
static_cast<const ADSRDecayAndSustainUndoCommand*>(other)->m_cycleCount)
{ {
m_redoDecay = static_cast<const ADSRDecayAndSustainUndoCommand*>(other)->m_redoDecay; m_redoDecay = static_cast<const ADSRDecayAndSustainUndoCommand*>(other)->m_redoDecay;
m_redoSustain = static_cast<const ADSRDecayAndSustainUndoCommand*>(other)->m_redoSustain; m_redoSustain = static_cast<const ADSRDecayAndSustainUndoCommand*>(other)->m_redoSustain;
@ -654,7 +663,7 @@ public:
int id() const { return int(Id::ADSRDecayAndSustain); } int id() const { return int(Id::ADSRDecayAndSustain); }
}; };
void ADSRControls::setDecayAndSustain(double decay, double sustain) void ADSRControls::setDecayAndSustain(double decay, double sustain, uint64_t cycleCount)
{ {
m_enableUpdate = false; m_enableUpdate = false;
m_decay->setValue(decay); m_decay->setValue(decay);
@ -662,18 +671,21 @@ void ADSRControls::setDecayAndSustain(double decay, double sustain)
m_enableUpdate = true; m_enableUpdate = true;
ADSRView* view = getEditor()->m_adsrView; ADSRView* view = getEditor()->m_adsrView;
g_MainWindow->pushUndoCommand(new ADSRDecayAndSustainUndoCommand(decay, sustain / 100.0, view->m_node)); g_MainWindow->pushUndoCommand(new ADSRDecayAndSustainUndoCommand(m_decay->value(), m_sustain->value() / 100.0,
cycleCount, view->m_node));
view->update(); view->update();
} }
class ADSRReleaseUndoCommand : public EditorUndoCommand class ADSRReleaseUndoCommand : public EditorUndoCommand
{ {
double m_redoVal, m_undoVal; double m_redoVal, m_undoVal;
uint64_t m_cycleCount;
bool m_undid = false; bool m_undid = false;
public: public:
ADSRReleaseUndoCommand(double redoVal, amuse::ObjToken<ProjectModel::ADSRNode> node) ADSRReleaseUndoCommand(double redoVal, uint64_t cycleCount,
amuse::ObjToken<ProjectModel::ADSRNode> node)
: EditorUndoCommand(node.get(), ADSRControls::tr("Change Release")), : EditorUndoCommand(node.get(), ADSRControls::tr("Change Release")),
m_redoVal(redoVal) {} m_redoVal(redoVal), m_cycleCount(cycleCount) {}
void undo() void undo()
{ {
m_undid = true; m_undid = true;
@ -710,7 +722,8 @@ public:
} }
bool mergeWith(const QUndoCommand* other) bool mergeWith(const QUndoCommand* other)
{ {
if (other->id() == id()) if (other->id() == id() && m_cycleCount ==
static_cast<const ADSRReleaseUndoCommand*>(other)->m_cycleCount)
{ {
m_redoVal = static_cast<const ADSRReleaseUndoCommand*>(other)->m_redoVal; m_redoVal = static_cast<const ADSRReleaseUndoCommand*>(other)->m_redoVal;
return true; return true;
@ -720,12 +733,23 @@ public:
int id() const { return int(Id::ADSRRelease); } int id() const { return int(Id::ADSRRelease); }
}; };
void ADSRControls::setRelease(double release, uint64_t cycleCount)
{
m_enableUpdate = false;
m_release->setValue(release);
m_enableUpdate = true;
ADSRView* view = getEditor()->m_adsrView;
g_MainWindow->pushUndoCommand(new ADSRReleaseUndoCommand(m_release->value(), cycleCount, view->m_node));
view->update();
}
void ADSRControls::releaseChanged(double val) void ADSRControls::releaseChanged(double val)
{ {
if (m_enableUpdate) if (m_enableUpdate)
{ {
ADSRView* view = getEditor()->m_adsrView; ADSRView* view = getEditor()->m_adsrView;
g_MainWindow->pushUndoCommand(new ADSRReleaseUndoCommand(val, view->m_node)); g_MainWindow->pushUndoCommand(new ADSRReleaseUndoCommand(val, ~0ull, view->m_node));
view->update(); view->update();
} }
} }

View File

@ -20,6 +20,7 @@ Q_OBJECT
QStaticText m_percentTexts[11]; QStaticText m_percentTexts[11];
std::vector<QStaticText> m_timeTexts; std::vector<QStaticText> m_timeTexts;
int m_dragPoint = -1; int m_dragPoint = -1;
uint64_t m_cycleIdx = 0;
ADSREditor* getEditor() const; ADSREditor* getEditor() const;
public: public:
explicit ADSRView(QWidget* parent = Q_NULLPTR); explicit ADSRView(QWidget* parent = Q_NULLPTR);
@ -48,8 +49,9 @@ Q_OBJECT
QDoubleSpinBox* m_keyToDecay; QDoubleSpinBox* m_keyToDecay;
bool m_enableUpdate = true; bool m_enableUpdate = true;
ADSREditor* getEditor() const; ADSREditor* getEditor() const;
void setAttackAndDecay(double attack, double decay); void setAttackAndDecay(double attack, double decay, uint64_t cycleCount);
void setDecayAndSustain(double decay, double sustain); void setDecayAndSustain(double decay, double sustain, uint64_t cycleCount);
void setRelease(double release, uint64_t cycleCount);
public: public:
explicit ADSRControls(QWidget* parent = Q_NULLPTR); explicit ADSRControls(QWidget* parent = Q_NULLPTR);
void loadData(); void loadData();

View File

@ -85,3 +85,14 @@ QString ShowInGraphicalShellString()
return MainWindow::tr("Show in Browser"); return MainWindow::tr("Show in Browser");
#endif #endif
} }
QTransform RectToRect(const QRectF& from, const QRectF& to)
{
QPolygonF orig(from);
orig.pop_back();
QPolygonF resize(to);
resize.pop_back();
QTransform ret;
QTransform::quadToQuad(orig, resize, ret);
return ret;
}

View File

@ -44,5 +44,7 @@ static QLatin1String StringViewToQString(std::string_view sv)
return QLatin1String(sv.data(), int(sv.size())); return QLatin1String(sv.data(), int(sv.size()));
} }
/* Used for generating transform matrices to map SVG coordinate space */
QTransform RectToRect(const QRectF& from, const QRectF& to);
#endif //AMUSE_COMMON_HPP #endif //AMUSE_COMMON_HPP

View File

@ -10,10 +10,12 @@ class CurveEditUndoCommand : public EditorUndoCommand
{ {
uint8_t m_redoData[128]; uint8_t m_redoData[128];
uint8_t m_undoData[128]; uint8_t m_undoData[128];
bool m_usedExpr;
bool m_undid = false; bool m_undid = false;
public: public:
CurveEditUndoCommand(const uint8_t* redoData, amuse::ObjToken<ProjectModel::CurveNode> node) CurveEditUndoCommand(const uint8_t* redoData, bool usedExpr,
: EditorUndoCommand(node.get(), CurveControls::tr("Edit Curve")) amuse::ObjToken<ProjectModel::CurveNode> node)
: EditorUndoCommand(node.get(), CurveControls::tr("Edit Curve")), m_usedExpr(usedExpr)
{ {
std::memcpy(m_redoData, redoData, 128); std::memcpy(m_redoData, redoData, 128);
} }
@ -44,7 +46,7 @@ public:
} }
bool mergeWith(const QUndoCommand* other) bool mergeWith(const QUndoCommand* other)
{ {
if (other->id() == id()) if (other->id() == id() && !m_usedExpr && !static_cast<const CurveEditUndoCommand*>(other)->m_usedExpr)
{ {
std::memcpy(m_redoData, static_cast<const CurveEditUndoCommand*>(other)->m_redoData, 128); std::memcpy(m_redoData, static_cast<const CurveEditUndoCommand*>(other)->m_redoData, 128);
return true; return true;
@ -153,7 +155,7 @@ void CurveView::mouseMoveEvent(QMouseEvent* ev)
std::memcpy(newData, curve.data.data(), 128); std::memcpy(newData, curve.data.data(), 128);
newData[idx] = uint8_t(val); newData[idx] = uint8_t(val);
g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, m_node)); g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, false, m_node));
update(); update();
} }
@ -227,7 +229,7 @@ void CurveControls::exprCommit()
if (notANumber) if (notANumber)
m_errLabel->setText(tr("Did not evaluate as a number")); m_errLabel->setText(tr("Did not evaluate as a number"));
g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, view->m_node)); g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, true, view->m_node));
view->update(); view->update();
} }

View File

@ -1,5 +1,6 @@
#include "EditorWidget.hpp" #include "EditorWidget.hpp"
#include "MainWindow.hpp" #include "MainWindow.hpp"
#include <QStandardItemModel>
EditorWidget::EditorWidget(QWidget* parent) EditorWidget::EditorWidget(QWidget* parent)
: QWidget(parent) : QWidget(parent)
@ -16,3 +17,24 @@ void EditorUndoCommand::redo()
{ {
g_MainWindow->openEditor(m_node.get()); g_MainWindow->openEditor(m_node.get());
} }
FieldProjectNode::FieldProjectNode(ProjectModel::CollectionNode* collection, QWidget* parent)
: FieldComboBox(parent)
{
setCollection(collection);
}
void FieldProjectNode::setCollection(ProjectModel::CollectionNode* collection)
{
m_collection = collection;
if (!collection)
{
setModel(new QStandardItemModel(0, 1, this));
return;
}
ProjectModel* model = g_MainWindow->projectModel();
setModel(model->getNullProxy());
setRootModelIndex(model->getNullProxy()->mapFromSource(model->index(collection)));
}

View File

@ -4,6 +4,9 @@
#include <QWidget> #include <QWidget>
#include <QUndoCommand> #include <QUndoCommand>
#include <QApplication> #include <QApplication>
#include <QSpinBox>
#include <QComboBox>
#include <QWheelEvent>
#include "ProjectModel.hpp" #include "ProjectModel.hpp"
class EditorWidget : public QWidget class EditorWidget : public QWidget
@ -44,4 +47,36 @@ public:
void redo(); void redo();
}; };
class FieldSpinBox : public QSpinBox
{
Q_OBJECT
public:
explicit FieldSpinBox(QWidget* parent = Q_NULLPTR)
: QSpinBox(parent) {}
/* Don't scroll */
void wheelEvent(QWheelEvent* event) { event->ignore(); }
};
class FieldComboBox : public QComboBox
{
Q_OBJECT
public:
explicit FieldComboBox(QWidget* parent = Q_NULLPTR)
: QComboBox(parent) {}
/* Don't scroll */
void wheelEvent(QWheelEvent* event) { event->ignore(); }
};
class FieldProjectNode : public FieldComboBox
{
Q_OBJECT
ProjectModel::CollectionNode* m_collection;
public:
explicit FieldProjectNode(ProjectModel::CollectionNode* collection = Q_NULLPTR, QWidget* parent = Q_NULLPTR);
void setCollection(ProjectModel::CollectionNode* collection);
ProjectModel::CollectionNode* collection() const { return m_collection; }
};
#endif //AMUSE_EDITOR_WIDGET_HPP #endif //AMUSE_EDITOR_WIDGET_HPP

View File

@ -6,19 +6,7 @@
#include <QApplication> #include <QApplication>
#include <QScrollBar> #include <QScrollBar>
/* Used for generating transform matrices to map SVG coordinate space */ const QString NaturalKeyNames[] =
static QTransform RectToRect(const QRectF& from, const QRectF& to)
{
QPolygonF orig(from);
orig.pop_back();
QPolygonF resize(to);
resize.pop_back();
QTransform ret;
QTransform::quadToQuad(orig, resize, ret);
return ret;
}
static const QString NaturalKeyNames[] =
{ {
QStringLiteral("C"), QStringLiteral("C"),
QStringLiteral("D"), QStringLiteral("D"),
@ -29,7 +17,7 @@ static const QString NaturalKeyNames[] =
QStringLiteral("B") QStringLiteral("B")
}; };
static const QString SharpKeyNames[] = const QString SharpKeyNames[] =
{ {
QStringLiteral("Cs"), QStringLiteral("Cs"),
QStringLiteral("Ds"), QStringLiteral("Ds"),
@ -38,7 +26,7 @@ static const QString SharpKeyNames[] =
QStringLiteral("As") QStringLiteral("As")
}; };
static const QString KeyStrings[] = const QString KeyStrings[] =
{ {
QStringLiteral("C"), QStringLiteral("C"),
QStringLiteral("C#"), QStringLiteral("C#"),
@ -54,12 +42,12 @@ static const QString KeyStrings[] =
QStringLiteral("B") QStringLiteral("B")
}; };
static const int NaturalKeyNumbers[] = const int NaturalKeyNumbers[] =
{ {
0, 2, 4, 5, 7, 9, 11 0, 2, 4, 5, 7, 9, 11
}; };
static const int SharpKeyNumbers[] = const int SharpKeyNumbers[] =
{ {
1, 3, 6, 8, 10 1, 3, 6, 8, 10
}; };

View File

@ -6,6 +6,13 @@
#include <QSlider> #include <QSlider>
#include <QWheelEvent> #include <QWheelEvent>
#include "StatusBarWidget.hpp" #include "StatusBarWidget.hpp"
#include "Common.hpp"
extern const QString NaturalKeyNames[7];
extern const QString SharpKeyNames[5];
extern const QString KeyStrings[12];
extern const int NaturalKeyNumbers[7];
extern const int SharpKeyNumbers[5];
class KeyboardWidget; class KeyboardWidget;

View File

@ -1,12 +1,600 @@
#include "KeymapEditor.hpp" #include "KeymapEditor.hpp"
#include "MainWindow.hpp"
#include "KeyboardWidget.hpp"
#include <QVBoxLayout>
#include <QPainter>
#include <QScrollBar>
static const int HueTable[] =
{
0, 30, 60, 80, 120, 170, 200, 240, 280, 320
};
static const int SaturationTable[] =
{
255, 255, 255, 255, 255, 127, 127, 127, 127, 127, 63, 63, 63
};
static const int ValueTable[] =
{
240, 200, 160, 120, 80, 240, 200, 160, 120, 80, 240, 200, 160
};
static const QPoint PointTable[] =
{
{21, 180},
{41, 104},
{61, 180},
{86, 104},
{101, 180},
{141, 180},
{156, 104},
{181, 180},
{201, 104},
{221, 180},
{246, 104},
{261, 180}
};
static const int RadiusTable[] =
{
14,
10,
14,
10,
14,
14,
10,
14,
10,
14,
10,
14
};
static const QColor PenColorTable[] =
{
Qt::black,
Qt::white,
Qt::black,
Qt::white,
Qt::black,
Qt::black,
Qt::white,
Qt::black,
Qt::white,
Qt::black,
Qt::white,
Qt::black
};
static const QColor NeutralColorTable[] =
{
Qt::white,
Qt::black,
Qt::white,
Qt::black,
Qt::white,
Qt::white,
Qt::black,
Qt::white,
Qt::black,
Qt::white,
Qt::black,
Qt::white
};
PaintButton::PaintButton(QWidget* parent)
: QPushButton(parent)
{
setIcon(QIcon(QStringLiteral(":/icons/IconPaintbrush.svg")));
setIconSize(QSize(26, 26));
setFixedSize(46, 46);
setToolTip(tr("Activate brush to apply values to keys"));
}
KeymapEditor* KeymapView::getEditor() const
{
return qobject_cast<KeymapEditor*>(parentWidget()->parentWidget()->parentWidget());
}
void KeymapView::loadData(ProjectModel::KeymapNode* node)
{
m_node = node;
}
void KeymapView::unloadData()
{
m_node.reset();
std::fill(std::begin(m_keyPalettes), std::end(m_keyPalettes), -1);
}
ProjectModel::INode* KeymapView::currentNode() const
{
return m_node.get();
}
void KeymapView::drawKey(QPainter& painter, const QRect& octaveRect, qreal penWidth,
const QColor* keyPalette, int o, int k) const
{
int keyIdx = o * 12 + k;
int keyPalIdx = m_keyPalettes[keyIdx];
painter.setPen(QPen(PenColorTable[k], penWidth));
painter.setBrush(keyPalIdx < 0 ? NeutralColorTable[k] : keyPalette[keyPalIdx]);
painter.drawEllipse(PointTable[k] + octaveRect.topLeft(), RadiusTable[k], RadiusTable[k]);
painter.setTransform(QTransform().translate(
PointTable[k].x() + octaveRect.left() - 13, PointTable[k].y() + octaveRect.top() - 20).rotate(-90.0));
painter.drawStaticText(QPointF{}, m_keyTexts[keyIdx]);
painter.setTransform(QTransform());
}
void KeymapView::paintEvent(QPaintEvent* ev)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
const QColor* keyPalette = getEditor()->m_paintPalette;
qreal deviceRatio = devicePixelRatioF();
qreal penWidth = std::max(std::floor(deviceRatio), 1.0) / deviceRatio;
painter.setFont(m_keyFont);
int kbY = height() / 2 - 100;
for (int o = 0; o < 10; ++o)
{
QRect thisRect(o * 280, kbY, 280, 200);
if (ev->rect().intersects(thisRect))
{
m_octaveRenderer.render(&painter, thisRect);
for (int k = 0; k < 12; ++k)
drawKey(painter, thisRect, penWidth, keyPalette, o, k);
}
}
QRect thisRect(2800, kbY, 202, 200);
if (ev->rect().intersects(thisRect))
{
m_lastOctaveRenderer.render(&painter, thisRect);
for (int k = 0; k < 8; ++k)
drawKey(painter, thisRect, penWidth, keyPalette, 10, k);
}
}
void KeymapView::mousePressEvent(QMouseEvent* ev)
{
mouseMoveEvent(ev);
}
int KeymapView::getKey(const QPoint& localPos) const
{
QPointF localPoint = m_widgetToSvg.map(localPos);
for (int i = 0; i < 5; ++i)
if (m_sharp[i].contains(localPoint))
return SharpKeyNumbers[i];
for (int i = 0; i < 7; ++i)
if (m_natural[i].contains(localPoint))
return NaturalKeyNumbers[i];
return -1;
}
void KeymapView::mouseMoveEvent(QMouseEvent* ev)
{
int octave = ev->x() / 280;
int key = getKey(ev->pos() - QPoint(octave * 280, height() / 2 - 100));
if (octave >= 0 && key >= 0)
getEditor()->touchKey(octave * 12 + key, ev->modifiers() & Qt::ShiftModifier);
}
void KeymapView::wheelEvent(QWheelEvent* event)
{
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget()))
{
/* Send wheel event directly to the scroll bar */
QApplication::sendEvent(scroll->horizontalScrollBar(), event);
}
}
KeymapView::KeymapView(QWidget* parent)
: QWidget(parent), m_octaveRenderer(QStringLiteral(":/bg/keyboard.svg")),
m_lastOctaveRenderer(QStringLiteral(":/bg/keyboard_last.svg"))
{
setMinimumWidth(3002);
int k = 0;
for (int i = 0; i < 11; ++i)
for (int j = 0; j < 12 && k < 128; ++j)
m_keyTexts[k++].setText(QStringLiteral("%1%2").arg(KeyStrings[j]).arg(i - 1));
m_keyFont.setPointSize(12);
std::fill(std::begin(m_keyPalettes), std::end(m_keyPalettes), -1);
for (int i = 0; i < 7; ++i)
if (m_octaveRenderer.elementExists(NaturalKeyNames[i]))
m_natural[i] = m_octaveRenderer.matrixForElement(NaturalKeyNames[i]).
mapRect(m_octaveRenderer.boundsOnElement(NaturalKeyNames[i]));
for (int i = 0; i < 5; ++i)
if (m_octaveRenderer.elementExists(SharpKeyNames[i]))
m_sharp[i] = m_octaveRenderer.matrixForElement(SharpKeyNames[i]).
mapRect(m_octaveRenderer.boundsOnElement(SharpKeyNames[i]));
m_widgetToSvg = RectToRect(QRect(0, 0, 280, 200), m_octaveRenderer.viewBoxF());
}
KeymapEditor* KeymapControls::getEditor() const
{
return qobject_cast<KeymapEditor*>(parentWidget());
}
void KeymapControls::setPaintIdx(int idx)
{
QPalette palette = m_paintButton->palette();
if (idx < 0)
{
palette.setColor(QPalette::Background, QWidget::palette().color(QPalette::Background));
palette.setColor(QPalette::Button, QWidget::palette().color(QPalette::Button));
}
else
{
const QColor* keyPalette = getEditor()->m_paintPalette;
palette.setColor(QPalette::Background, keyPalette[idx]);
palette.setColor(QPalette::Button, keyPalette[idx].darker(300));
}
m_paintButton->setPalette(palette);
}
void KeymapControls::setKeymap(const amuse::Keymap& km)
{
m_enableUpdate = false;
int idx = m_macro->collection()->indexOfId(km.macro.id);
m_macro->setCurrentIndex(idx + 1);
m_transpose->setValue(km.transpose);
if (km.pan == -128)
{
m_pan->setDisabled(true);
m_pan->setValue(true);
m_surround->setChecked(true);
}
else
{
m_pan->setEnabled(true);
m_pan->setValue(km.pan);
m_surround->setChecked(false);
}
m_prioOffset->setValue(km.prioOffset);
m_enableUpdate = true;
}
void KeymapControls::loadData(ProjectModel::KeymapNode* node)
{
m_enableUpdate = false;
m_macro->setCollection(g_MainWindow->projectModel()->getGroupNode(node)->
getCollectionOfType(ProjectModel::INode::Type::SoundMacro));
m_macro->setDisabled(false);
m_transpose->setDisabled(false);
m_transpose->setValue(0);
m_pan->setDisabled(false);
m_pan->setValue(64);
m_surround->setDisabled(false);
m_surround->setChecked(false);
m_prioOffset->setDisabled(false);
m_prioOffset->setValue(0);
m_paintButton->setDisabled(false);
m_paintButton->setPalette(palette());
m_enableUpdate = true;
}
void KeymapControls::unloadData()
{
m_enableUpdate = false;
m_macro->setCollection(nullptr);
m_macro->setDisabled(true);
m_transpose->setDisabled(true);
m_pan->setDisabled(true);
m_surround->setDisabled(true);
m_prioOffset->setDisabled(true);
m_paintButton->setDisabled(true);
m_paintButton->setPalette(palette());
m_enableUpdate = true;
}
void KeymapControls::controlChanged()
{
if (m_enableUpdate)
{
amuse::Keymap km;
km.macro.id = m_macro->currentIndex() == 0 ? amuse::SoundMacroId{} :
m_macro->collection()->idOfIndex(m_macro->currentIndex() - 1);
km.transpose = int8_t(m_transpose->value());
km.pan = int8_t(m_pan->value());
if (m_surround->isChecked())
{
km.pan = -128;
m_pan->setDisabled(true);
}
else
{
m_pan->setEnabled(true);
}
km.prioOffset = int8_t(m_prioOffset->value());
getEditor()->touchControl(km);
}
}
void KeymapControls::paintButtonPressed()
{
KeymapEditor* editor = getEditor();
if (!editor->m_inPaint)
{
editor->setCursor(QCursor(QPixmap(QStringLiteral(":/icons/IconPaintbrush.svg")), 2, 30));
editor->m_inPaint = true;
m_paintButton->setDown(true);
}
else
{
editor->unsetCursor();
editor->m_inPaint = false;
m_paintButton->setDown(false);
}
}
KeymapControls::KeymapControls(QWidget* parent)
: QFrame(parent)
{
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
setFixedHeight(100);
setFrameShape(QFrame::StyledPanel);
setFrameShadow(QFrame::Sunken);
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
QPalette palette = QWidget::palette();
palette.setColor(QPalette::Base, palette.color(QPalette::Background));
QHBoxLayout* mainLayout = new QHBoxLayout;
QGridLayout* leftLayout = new QGridLayout;
leftLayout->addWidget(new QLabel(tr("SoundMacro")), 0, 0);
m_macro = new FieldProjectNode;
m_macro->setDisabled(true);
connect(m_macro, SIGNAL(currentIndexChanged(int)), this, SLOT(controlChanged()));
leftLayout->addWidget(m_macro, 1, 0);
leftLayout->addWidget(new QLabel(tr("Transpose")), 0, 1);
m_transpose = new QSpinBox;
m_transpose->setPalette(palette);
m_transpose->setDisabled(true);
m_transpose->setRange(-128, 127);
m_transpose->setToolTip(tr("Offset resulting MIDI note"));
connect(m_transpose, SIGNAL(valueChanged(int)), this, SLOT(controlChanged()));
leftLayout->addWidget(m_transpose, 1, 1);
leftLayout->addWidget(new QLabel(tr("Pan")), 0, 2);
m_pan = new QSpinBox;
m_pan->setPalette(palette);
m_pan->setDisabled(true);
m_pan->setRange(-127, 127);
m_pan->setToolTip(tr("Set initial pan"));
connect(m_pan, SIGNAL(valueChanged(int)), this, SLOT(controlChanged()));
leftLayout->addWidget(m_pan, 1, 2);
leftLayout->addWidget(new QLabel(tr("Surround")), 0, 3);
m_surround = new QCheckBox;
m_surround->setPalette(palette);
m_surround->setDisabled(true);
m_surround->setToolTip(tr("Initially play through surround channels"));
connect(m_surround, SIGNAL(stateChanged(int)), this, SLOT(controlChanged()));
leftLayout->addWidget(m_surround, 1, 3);
leftLayout->addWidget(new QLabel(tr("Prio Offset")), 0, 4);
m_prioOffset = new QSpinBox;
m_prioOffset->setPalette(palette);
m_prioOffset->setDisabled(true);
m_prioOffset->setRange(-128, 127);
m_prioOffset->setToolTip(tr("Offset resulting priority"));
connect(m_prioOffset, SIGNAL(valueChanged(int)), this, SLOT(controlChanged()));
leftLayout->addWidget(m_prioOffset, 1, 4);
leftLayout->setColumnMinimumWidth(0, 200);
leftLayout->setColumnMinimumWidth(1, 75);
leftLayout->setColumnMinimumWidth(2, 75);
leftLayout->setColumnMinimumWidth(3, 50);
leftLayout->setColumnMinimumWidth(4, 75);
leftLayout->setRowMinimumHeight(0, 22);
leftLayout->setRowMinimumHeight(1, 37);
leftLayout->setContentsMargins(10, 6, 0, 14);
QVBoxLayout* rightLayout = new QVBoxLayout;
m_paintButton = new PaintButton;
m_paintButton->setDisabled(true);
connect(m_paintButton, SIGNAL(pressed()), this, SLOT(paintButtonPressed()));
rightLayout->addWidget(m_paintButton);
rightLayout->setContentsMargins(0, 0, 10, 0);
mainLayout->addLayout(leftLayout);
mainLayout->addStretch(1);
mainLayout->addLayout(rightLayout);
setLayout(mainLayout);
}
void KeymapEditor::_touch()
{
if (m_controlKeymap.macro.id == 0xffff)
m_controls->setPaintIdx(-1);
else
m_controls->setPaintIdx(getConfigIdx(m_controlKeymap.configKey()));
}
void KeymapEditor::touchKey(int key, bool bulk)
{
if (m_inPaint)
{
if (bulk)
{
uint64_t refKey = (*m_kmView->m_node->m_obj)[key].configKey();
for (int i = 0; i < 128; ++i)
{
amuse::Keymap& km = (*m_kmView->m_node->m_obj)[i];
if (km.configKey() != refKey)
continue;
if (km.macro.id != 0xffff)
deallocateConfigIdx(km.configKey());
km = m_controlKeymap;
if (km.macro.id == 0xffff)
m_kmView->m_keyPalettes[i] = -1;
else
m_kmView->m_keyPalettes[i] = allocateConfigIdx(km.configKey());
}
}
else
{
amuse::Keymap& km = (*m_kmView->m_node->m_obj)[key];
if (km.macro.id != 0xffff)
deallocateConfigIdx(km.configKey());
km = m_controlKeymap;
if (km.macro.id == 0xffff)
m_kmView->m_keyPalettes[key] = -1;
else
m_kmView->m_keyPalettes[key] = allocateConfigIdx(km.configKey());
}
m_kmView->update();
}
else
{
amuse::Keymap& km = (*m_kmView->m_node->m_obj)[key];
m_controlKeymap = km;
m_controls->setKeymap(km);
_touch();
}
}
void KeymapEditor::touchControl(const amuse::Keymap& km)
{
m_controlKeymap = km;
_touch();
}
int KeymapEditor::allocateConfigIdx(uint64_t key)
{
auto search = m_configToIdx.find(key);
if (search != m_configToIdx.end())
{
++search->second.second;
return search->second.first;
}
for (int i = 0; i < 128; ++i)
if (!m_idxBitmap[i])
{
m_configToIdx[key] = std::make_pair(i, 1);
m_idxBitmap.set(i);
return i;
}
Q_UNREACHABLE();
}
void KeymapEditor::deallocateConfigIdx(uint64_t key)
{
auto search = m_configToIdx.find(key);
if (search != m_configToIdx.end())
{
--search->second.second;
if (search->second.second == 0)
{
m_idxBitmap.reset(search->second.first);
m_configToIdx.erase(search);
}
return;
}
Q_UNREACHABLE();
}
int KeymapEditor::getConfigIdx(uint64_t key) const
{
auto search = m_configToIdx.find(key);
if (search != m_configToIdx.end())
return search->second.first;
for (int i = 0; i < 128; ++i)
if (!m_idxBitmap[i])
return i;
Q_UNREACHABLE();
}
bool KeymapEditor::loadData(ProjectModel::KeymapNode* node) bool KeymapEditor::loadData(ProjectModel::KeymapNode* node)
{ {
return false; if (m_kmView->m_node.get() != node)
{
m_configToIdx.clear();
m_idxBitmap.reset();
for (int i = 0; i < 128; ++i)
{
amuse::Keymap& km = (*node->m_obj)[i];
if (km.macro.id == 0xffff)
m_kmView->m_keyPalettes[i] = -1;
else
m_kmView->m_keyPalettes[i] = allocateConfigIdx(km.configKey());
}
m_controlKeymap = amuse::Keymap();
m_kmView->loadData(node);
m_controls->loadData(node);
}
m_inPaint = false;
m_controls->m_paintButton->setDown(false);
unsetCursor();
return true;
}
void KeymapEditor::unloadData()
{
m_configToIdx.clear();
m_idxBitmap.reset();
m_kmView->unloadData();
m_controls->unloadData();
m_inPaint = false;
m_controls->m_paintButton->setDown(false);
unsetCursor();
}
ProjectModel::INode* KeymapEditor::currentNode() const
{
return m_kmView->currentNode();
}
void KeymapEditor::keyPressEvent(QKeyEvent* event)
{
if (event->key() == Qt::Key_Escape)
{
if (m_inPaint)
{
m_inPaint = false;
m_controls->m_paintButton->setDown(false);
unsetCursor();
}
}
} }
KeymapEditor::KeymapEditor(QWidget* parent) KeymapEditor::KeymapEditor(QWidget* parent)
: EditorWidget(parent) : EditorWidget(parent), m_scrollArea(new QScrollArea),
m_kmView(new KeymapView), m_controls(new KeymapControls)
{ {
int k = 0;
for (int i = 0; i < 13; ++i)
for (int j = 0; j < 10 && k < 128; ++j)
m_paintPalette[k++].setHsv(HueTable[j], SaturationTable[i], ValueTable[i]);
m_scrollArea->setWidget(m_kmView);
m_scrollArea->setWidgetResizable(true);
QVBoxLayout* layout = new QVBoxLayout;
layout->setContentsMargins(QMargins());
layout->setSpacing(1);
layout->addWidget(m_scrollArea);
layout->addWidget(m_controls);
setLayout(layout);
} }

View File

@ -2,14 +2,108 @@
#define AMUSE_KEYMAP_EDITOR_HPP #define AMUSE_KEYMAP_EDITOR_HPP
#include "EditorWidget.hpp" #include "EditorWidget.hpp"
#include <QFrame>
#include <QLabel>
#include <QStaticText>
#include <QSpinBox>
#include <QPushButton>
#include <QSvgRenderer>
#include <QScrollArea>
#include <QCheckBox>
#include <bitset>
class KeymapEditor;
class PaintButton : public QPushButton
{
Q_OBJECT
public:
explicit PaintButton(QWidget* parent = Q_NULLPTR);
void mouseReleaseEvent(QMouseEvent* event) { event->ignore(); }
void mouseMoveEvent(QMouseEvent* event) { event->ignore(); }
void focusOutEvent(QFocusEvent* event) { event->ignore(); }
void keyPressEvent(QKeyEvent* event) { event->ignore(); }
};
class KeymapView : public QWidget
{
Q_OBJECT
friend class KeymapControls;
friend class KeymapEditor;
amuse::ObjToken<ProjectModel::KeymapNode> m_node;
QSvgRenderer m_octaveRenderer;
QSvgRenderer m_lastOctaveRenderer;
QRectF m_natural[7];
QRectF m_sharp[5];
QTransform m_widgetToSvg;
QFont m_keyFont;
QStaticText m_keyTexts[128];
int m_keyPalettes[128];
KeymapEditor* getEditor() const;
int getKey(const QPoint& localPos) const;
void drawKey(QPainter& painter, const QRect& octaveRect, qreal penWidth,
const QColor* keyPalette, int o, int k) const;
public:
explicit KeymapView(QWidget* parent = Q_NULLPTR);
void loadData(ProjectModel::KeymapNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void paintEvent(QPaintEvent* ev);
void mousePressEvent(QMouseEvent* ev);
void mouseMoveEvent(QMouseEvent* ev);
void wheelEvent(QWheelEvent* event);
};
class KeymapControls : public QFrame
{
Q_OBJECT
friend class KeymapView;
friend class KeymapEditor;
FieldProjectNode* m_macro;
QSpinBox* m_transpose;
QSpinBox* m_pan;
QCheckBox* m_surround;
QSpinBox* m_prioOffset;
PaintButton* m_paintButton;
bool m_enableUpdate = true;
KeymapEditor* getEditor() const;
void setPaintIdx(int idx);
void setKeymap(const amuse::Keymap& km);
public:
explicit KeymapControls(QWidget* parent = Q_NULLPTR);
void loadData(ProjectModel::KeymapNode* node);
void unloadData();
public slots:
void controlChanged();
void paintButtonPressed();
};
class KeymapEditor : public EditorWidget class KeymapEditor : public EditorWidget
{ {
Q_OBJECT Q_OBJECT
friend class KeymapView;
friend class KeymapControls;
QScrollArea* m_scrollArea;
KeymapView* m_kmView;
KeymapControls* m_controls;
QColor m_paintPalette[128];
amuse::Keymap m_controlKeymap;
std::unordered_map<uint64_t, std::pair<int, int>> m_configToIdx;
std::bitset<128> m_idxBitmap;
bool m_inPaint = false;
void _touch();
void touchKey(int key, bool bulk = false);
void touchControl(const amuse::Keymap& km);
int allocateConfigIdx(uint64_t key);
void deallocateConfigIdx(uint64_t key);
int getConfigIdx(uint64_t key) const;
public: public:
explicit KeymapEditor(QWidget* parent = Q_NULLPTR); explicit KeymapEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::KeymapNode* node); bool loadData(ProjectModel::KeymapNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void keyPressEvent(QKeyEvent* event);
}; };
#endif //AMUSE_KEYMAP_EDITOR_HPP #endif //AMUSE_KEYMAP_EDITOR_HPP

View File

@ -110,8 +110,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1100</width> <width>1103</width>
<height>610</height> <height>606</height>
</rect> </rect>
</property> </property>
</widget> </widget>
@ -151,6 +151,78 @@
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </property>
<item>
<widget class="PitchSlider" name="pitchSlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="minimum">
<number>-2048</number>
</property>
<property name="maximum">
<number>2048</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="ModulationSlider" name="modulationSlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="maximum">
<number>127</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="VelocitySlider" name="velocitySlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="maximum">
<number>127</number>
</property>
<property name="value">
<number>90</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item> <item>
<widget class="QScrollArea" name="keyboardScrollArea"> <widget class="QScrollArea" name="keyboardScrollArea">
<property name="sizePolicy"> <property name="sizePolicy">
@ -189,7 +261,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1501</width> <width>1501</width>
<height>85</height> <height>80</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -213,78 +285,6 @@
</widget> </widget>
</widget> </widget>
</item> </item>
<item>
<widget class="VelocitySlider" name="velocitySlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="maximum">
<number>127</number>
</property>
<property name="value">
<number>90</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="ModulationSlider" name="modulationSlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="maximum">
<number>127</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
<item>
<widget class="PitchSlider" name="pitchSlider">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>100</height>
</size>
</property>
<property name="minimum">
<number>-2048</number>
</property>
<property name="maximum">
<number>2048</number>
</property>
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
</widget> </widget>
@ -298,7 +298,7 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>1360</width> <width>1360</width>
<height>27</height> <height>34</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile"> <widget class="QMenu" name="menuFile">
@ -325,7 +325,7 @@
</widget> </widget>
<widget class="QMenu" name="menuProject"> <widget class="QMenu" name="menuProject">
<property name="title"> <property name="title">
<string>P&amp;roject</string> <string>Pro&amp;ject</string>
</property> </property>
<addaction name="actionNew_Subproject"/> <addaction name="actionNew_Subproject"/>
<addaction name="separator"/> <addaction name="separator"/>

View File

@ -269,6 +269,10 @@ void ProjectModel::_resetModelData()
col.reserve(keymaps.size()); col.reserve(keymaps.size());
for (auto& keymap : SortUnorderedMap(keymaps)) for (auto& keymap : SortUnorderedMap(keymaps))
col.makeChild<KeymapNode>(keymap.first, keymap.second.get()); col.makeChild<KeymapNode>(keymap.first, keymap.second.get());
amuse::KeymapId id = 42;
amuse::KeymapId::CurNameDB->registerPair("test", id);
auto km = amuse::MakeObj<std::array<amuse::Keymap, 128>>();
col.makeChild<KeymapNode>(id, km);
} }
{ {
CollectionNode& col = CollectionNode& col =

View File

@ -230,7 +230,7 @@ public:
using SoundMacroNode = PoolObjectNode<amuse::SoundMacroId, amuse::SoundMacro, INode::Type::SoundMacro>; using SoundMacroNode = PoolObjectNode<amuse::SoundMacroId, amuse::SoundMacro, INode::Type::SoundMacro>;
using ADSRNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::ADSR>; using ADSRNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::ADSR>;
using CurveNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::Curve>; using CurveNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::Curve>;
using KeymapNode = PoolObjectNode<amuse::KeymapId, amuse::Keymap, INode::Type::Keymap>; using KeymapNode = PoolObjectNode<amuse::KeymapId, std::array<amuse::Keymap, 128>, INode::Type::Keymap>;
using LayersNode = PoolObjectNode<amuse::LayersId, std::vector<amuse::LayerMapping>, INode::Type::Layer>; using LayersNode = PoolObjectNode<amuse::LayersId, std::vector<amuse::LayerMapping>, INode::Type::Layer>;
using SampleNode = PoolObjectNode<amuse::SampleId, amuse::SampleEntry, INode::Type::Sample>; using SampleNode = PoolObjectNode<amuse::SampleId, amuse::SampleEntry, INode::Type::Sample>;

View File

@ -10,14 +10,6 @@
#include <QApplication> #include <QApplication>
#include <QCheckBox> #include <QCheckBox>
FieldProjectNode::FieldProjectNode(ProjectModel::CollectionNode* collection, QWidget* parent)
: FieldComboBox(parent), m_collection(collection)
{
ProjectModel* model = g_MainWindow->projectModel();
setModel(model->getNullProxy());
setRootModelIndex(model->getNullProxy()->mapFromSource(model->index(collection)));
}
TargetButton::TargetButton(QWidget* parent) TargetButton::TargetButton(QWidget* parent)
: QPushButton(parent) : QPushButton(parent)
{ {

View File

@ -17,37 +17,6 @@ class SoundMacroEditor;
class SoundMacroListing; class SoundMacroListing;
class CatalogueItem; class CatalogueItem;
class FieldSpinBox : public QSpinBox
{
Q_OBJECT
public:
explicit FieldSpinBox(QWidget* parent = Q_NULLPTR)
: QSpinBox(parent) {}
/* Don't scroll */
void wheelEvent(QWheelEvent* event) { event->ignore(); }
};
class FieldComboBox : public QComboBox
{
Q_OBJECT
public:
explicit FieldComboBox(QWidget* parent = Q_NULLPTR)
: QComboBox(parent) {}
/* Don't scroll */
void wheelEvent(QWheelEvent* event) { event->ignore(); }
};
class FieldProjectNode : public FieldComboBox
{
Q_OBJECT
ProjectModel::CollectionNode* m_collection;
public:
explicit FieldProjectNode(ProjectModel::CollectionNode* collection, QWidget* parent = Q_NULLPTR);
ProjectModel::CollectionNode* collection() const { return m_collection; }
};
class TargetButton : public QPushButton class TargetButton : public QPushButton
{ {
Q_OBJECT Q_OBJECT

View File

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="32"
height="32"
viewBox="0 0 8.4666664 8.4666672"
version="1.1"
id="svg8"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconPaintbrush.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#353535"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="39.33"
inkscape:cx="14.81887"
inkscape:cy="20.680873"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="3840"
inkscape:window-height="2079"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="1"
gridtolerance="10"
showguides="false"
showborder="true">
<inkscape:grid
type="xygrid"
id="grid817"
empspacing="0"
spacingx="0.52916666"
spacingy="0.52916666"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-288.53332)">
<path
style="fill:#333333;fill-opacity:1;stroke:#ffffff;stroke-width:0.26458334;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 7.9375067,289.06249 c 0.2663267,0.26632 -4.2333372,5.29165 -4.2333372,5.29165 l -1.0583343,-1.05832 c 0,0 5.0253447,-4.49967 5.2916715,-4.23333 z"
id="path846"
inkscape:connector-curvature="0"
sodipodi:nodetypes="zccz" />
<path
style="fill:#333333;fill-opacity:1;stroke:#ffffff;stroke-width:0.26458334;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 3.7041695,294.35414 c 0.7934571,0.79347 -2.1166685,2.11668 -3.17500284,2.11668 0,-1.05833 1.32960554,-3.96208 2.11666854,-3.175 z"
id="path848"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.26458334;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.7321623,293.41165 c 0,0 -1.1446613,0.94249 -2.20299564,3.05917"
id="path850"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.26458334;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 3.6041925,294.27028 c 0,0 -0.9583573,1.14221 -3.07502584,2.20054"
id="path852"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:0.26458334;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 3.1750023,293.82499 -2.64583564,2.64583"
id="path854"
inkscape:connector-curvature="0" />
<path
style="fill:#fffffd;fill-opacity:1;stroke:none;stroke-width:0.26458356px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.4564631,292.56098 0.9825481,0.98255 -0.7348417,0.81061 -1.0583343,-1.05832 z"
id="path1960"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -4,94 +4,94 @@
<context> <context>
<name>ADSRControls</name> <name>ADSRControls</name>
<message> <message>
<location filename="../ADSREditor.cpp" line="335"/> <location filename="../ADSREditor.cpp" line="337"/>
<source>Change Attack</source> <source>Change Attack</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ADSREditor.cpp" line="399"/> <location filename="../ADSREditor.cpp" line="401"/>
<source>Change Decay</source> <source>Change Decay</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ADSREditor.cpp" line="463"/> <location filename="../ADSREditor.cpp" line="465"/>
<source>Change Sustain</source> <source>Change Sustain</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ADSREditor.cpp" line="528"/> <location filename="../ADSREditor.cpp" line="532"/>
<source>Change Attack/Decay</source> <source>Change Attack/Decay</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ADSREditor.cpp" line="602"/> <location filename="../ADSREditor.cpp" line="610"/>
<source>Change Decay/Sustain</source> <source>Change Decay/Sustain</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ADSREditor.cpp" line="675"/> <location filename="../ADSREditor.cpp" line="687"/>
<source>Change Release</source> <source>Change Release</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ADSREditor.cpp" line="765"/> <location filename="../ADSREditor.cpp" line="789"/>
<source>Change DLS</source> <source>Change DLS</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ADSREditor.cpp" line="852"/> <location filename="../ADSREditor.cpp" line="876"/>
<source>Change Vel To Attack</source> <source>Change Vel To Attack</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ADSREditor.cpp" line="905"/> <location filename="../ADSREditor.cpp" line="929"/>
<source>Change Key To Decay</source> <source>Change Key To Decay</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ADSREditor.cpp" line="970"/> <location filename="../ADSREditor.cpp" line="994"/>
<source>Attack</source> <source>Attack</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ADSREditor.cpp" line="978"/> <location filename="../ADSREditor.cpp" line="1002"/>
<location filename="../ADSREditor.cpp" line="992"/> <location filename="../ADSREditor.cpp" line="1016"/>
<location filename="../ADSREditor.cpp" line="1017"/> <location filename="../ADSREditor.cpp" line="1041"/>
<source> sec</source> <source> sec</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ADSREditor.cpp" line="984"/> <location filename="../ADSREditor.cpp" line="1008"/>
<source>Decay</source> <source>Decay</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ADSREditor.cpp" line="998"/> <location filename="../ADSREditor.cpp" line="1022"/>
<source>Sustain</source> <source>Sustain</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ADSREditor.cpp" line="1003"/> <location filename="../ADSREditor.cpp" line="1027"/>
<source> %</source> <source> %</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ADSREditor.cpp" line="1009"/> <location filename="../ADSREditor.cpp" line="1033"/>
<source>Release</source> <source>Release</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ADSREditor.cpp" line="1023"/> <location filename="../ADSREditor.cpp" line="1047"/>
<source>DLS</source> <source>DLS</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ADSREditor.cpp" line="1031"/> <location filename="../ADSREditor.cpp" line="1055"/>
<source>Vel To Attack</source> <source>Vel To Attack</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ADSREditor.cpp" line="1043"/> <location filename="../ADSREditor.cpp" line="1067"/>
<source>Key To Decay</source> <source>Key To Decay</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -99,7 +99,7 @@
<context> <context>
<name>CommandWidget</name> <name>CommandWidget</name>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="350"/> <location filename="../SoundMacroEditor.cpp" line="342"/>
<source>Change %1</source> <source>Change %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -107,32 +107,80 @@
<context> <context>
<name>CurveControls</name> <name>CurveControls</name>
<message> <message>
<location filename="../CurveEditor.cpp" line="16"/> <location filename="../CurveEditor.cpp" line="18"/>
<source>Edit Curve</source> <source>Edit Curve</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../CurveEditor.cpp" line="228"/> <location filename="../CurveEditor.cpp" line="230"/>
<source>Did not evaluate as a number</source> <source>Did not evaluate as a number</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../CurveEditor.cpp" line="253"/> <location filename="../CurveEditor.cpp" line="255"/>
<source>Expression</source> <source>Expression</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../CurveEditor.cpp" line="262"/> <location filename="../CurveEditor.cpp" line="264"/>
<source>Apply</source> <source>Apply</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../CurveEditor.cpp" line="286"/> <location filename="../CurveEditor.cpp" line="288"/>
<source>Expression interpreter mapping x:[0,1] to y:[0,1] with the following constants and functions available: <source>Expression interpreter mapping x:[0,1] to y:[0,1] with the following constants and functions available:
</source> </source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>KeymapControls</name>
<message>
<location filename="../KeymapEditor.cpp" line="356"/>
<source>SoundMacro</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../KeymapEditor.cpp" line="362"/>
<source>Transpose</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../KeymapEditor.cpp" line="367"/>
<source>Offset resulting MIDI note</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../KeymapEditor.cpp" line="371"/>
<source>Pan</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../KeymapEditor.cpp" line="376"/>
<source>Set initial pan</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../KeymapEditor.cpp" line="380"/>
<source>Surround</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../KeymapEditor.cpp" line="384"/>
<source>Initially play through surround channels</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../KeymapEditor.cpp" line="388"/>
<source>Prio Offset</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../KeymapEditor.cpp" line="393"/>
<source>Offset resulting priority</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>MainWindow</name> <name>MainWindow</name>
<message> <message>
@ -153,7 +201,7 @@
</message> </message>
<message> <message>
<location filename="../MainWindow.ui" line="328"/> <location filename="../MainWindow.ui" line="328"/>
<source>P&amp;roject</source> <source>Pro&amp;ject</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
@ -522,15 +570,23 @@
<context> <context>
<name>ModulationSlider</name> <name>ModulationSlider</name>
<message> <message>
<location filename="../KeyboardWidget.cpp" line="267"/> <location filename="../KeyboardWidget.cpp" line="255"/>
<source>Modulation: %1</source> <source>Modulation: %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
</context> </context>
<context>
<name>PaintButton</name>
<message>
<location filename="../KeymapEditor.cpp" line="93"/>
<source>Activate brush to apply values to keys</source>
<translation type="unfinished"></translation>
</message>
</context>
<context> <context>
<name>PitchSlider</name> <name>PitchSlider</name>
<message> <message>
<location filename="../KeyboardWidget.cpp" line="276"/> <location filename="../KeyboardWidget.cpp" line="264"/>
<source>Pitch: %1</source> <source>Pitch: %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -558,17 +614,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="275"/> <location filename="../ProjectModel.cpp" line="279"/>
<source>Layers</source> <source>Layers</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="282"/> <location filename="../ProjectModel.cpp" line="286"/>
<source>Samples</source> <source>Samples</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../ProjectModel.cpp" line="434"/> <location filename="../ProjectModel.cpp" line="438"/>
<source>Delete %1</source> <source>Delete %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -647,72 +703,72 @@
<context> <context>
<name>SoundMacroCatalogue</name> <name>SoundMacroCatalogue</name>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="977"/> <location filename="../SoundMacroEditor.cpp" line="969"/>
<source>Control</source> <source>Control</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="978"/> <location filename="../SoundMacroEditor.cpp" line="970"/>
<source>Pitch</source> <source>Pitch</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="979"/> <location filename="../SoundMacroEditor.cpp" line="971"/>
<source>Sample</source> <source>Sample</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="980"/> <location filename="../SoundMacroEditor.cpp" line="972"/>
<source>Setup</source> <source>Setup</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="981"/> <location filename="../SoundMacroEditor.cpp" line="973"/>
<source>Special</source> <source>Special</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="982"/> <location filename="../SoundMacroEditor.cpp" line="974"/>
<source>Structure</source> <source>Structure</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="983"/> <location filename="../SoundMacroEditor.cpp" line="975"/>
<source>Volume</source> <source>Volume</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="988"/> <location filename="../SoundMacroEditor.cpp" line="980"/>
<source>Commands to control the voice</source> <source>Commands to control the voice</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="989"/> <location filename="../SoundMacroEditor.cpp" line="981"/>
<source>Commands to control the voice&apos;s pitch</source> <source>Commands to control the voice&apos;s pitch</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="990"/> <location filename="../SoundMacroEditor.cpp" line="982"/>
<source>Commands to control the voice&apos;s sample playback</source> <source>Commands to control the voice&apos;s sample playback</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="991"/> <location filename="../SoundMacroEditor.cpp" line="983"/>
<source>Commands to setup the voice&apos;s mixing process</source> <source>Commands to setup the voice&apos;s mixing process</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="992"/> <location filename="../SoundMacroEditor.cpp" line="984"/>
<source>Miscellaneous commands</source> <source>Miscellaneous commands</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="993"/> <location filename="../SoundMacroEditor.cpp" line="985"/>
<source>Commands to control macro branching</source> <source>Commands to control macro branching</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="994"/> <location filename="../SoundMacroEditor.cpp" line="986"/>
<source>Commands to control the voice&apos;s volume</source> <source>Commands to control the voice&apos;s volume</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -720,7 +776,7 @@
<context> <context>
<name>SoundMacroDeleteButton</name> <name>SoundMacroDeleteButton</name>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="153"/> <location filename="../SoundMacroEditor.cpp" line="145"/>
<source>Delete this SoundMacro</source> <source>Delete this SoundMacro</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -728,17 +784,17 @@
<context> <context>
<name>SoundMacroListing</name> <name>SoundMacroListing</name>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="676"/> <location filename="../SoundMacroEditor.cpp" line="668"/>
<source>Reorder %1</source> <source>Reorder %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="808"/> <location filename="../SoundMacroEditor.cpp" line="800"/>
<source>Insert %1</source> <source>Insert %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="861"/> <location filename="../SoundMacroEditor.cpp" line="853"/>
<source>Delete %1</source> <source>Delete %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -754,7 +810,7 @@
<context> <context>
<name>TargetButton</name> <name>TargetButton</name>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="27"/> <location filename="../SoundMacroEditor.cpp" line="19"/>
<source>Set step with target click</source> <source>Set step with target click</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -762,7 +818,7 @@
<context> <context>
<name>VelocitySlider</name> <name>VelocitySlider</name>
<message> <message>
<location filename="../KeyboardWidget.cpp" line="258"/> <location filename="../KeyboardWidget.cpp" line="246"/>
<source>Velocity: %1</source> <source>Velocity: %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>

View File

@ -25,6 +25,7 @@
<file>IconSoundMacroTargetDisabled.svg</file> <file>IconSoundMacroTargetDisabled.svg</file>
<file>IconKill.svg</file> <file>IconKill.svg</file>
<file>IconSample.svg</file> <file>IconSample.svg</file>
<file>IconPaintbrush.svg</file>
</qresource> </qresource>
<qresource prefix="/bg"> <qresource prefix="/bg">
<file>FaceGrey.svg</file> <file>FaceGrey.svg</file>

View File

@ -1310,9 +1310,9 @@ struct Keymap : BigDNA
{ {
AT_DECL_DNA_YAML AT_DECL_DNA_YAML
SoundMacroIdDNA<athena::Big> macro; SoundMacroIdDNA<athena::Big> macro;
Value<atInt8> transpose; Value<atInt8> transpose = 0;
Value<atInt8> pan; /* -128 for surround-channel only */ Value<atInt8> pan = 64; /* -128 for surround-channel only */
Value<atInt8> prioOffset; Value<atInt8> prioOffset = 0;
Keymap() = default; Keymap() = default;
@ -1331,6 +1331,11 @@ struct Keymap : BigDNA
ret.prioOffset = prioOffset; ret.prioOffset = prioOffset;
return ret; return ret;
} }
uint64_t configKey() const
{
return uint64_t(macro.id) | (uint64_t(transpose) << 16) | (uint64_t(pan) << 24) | (uint64_t(prioOffset) << 32);
}
}; };
/** Maps ranges of MIDI keys to sound-entity (macro-voice, keymap, layer) */ /** Maps ranges of MIDI keys to sound-entity (macro-voice, keymap, layer) */
@ -1390,7 +1395,7 @@ class AudioGroupPool
{ {
std::unordered_map<SoundMacroId, ObjToken<SoundMacro>> m_soundMacros; std::unordered_map<SoundMacroId, ObjToken<SoundMacro>> m_soundMacros;
std::unordered_map<TableId, ObjToken<std::unique_ptr<ITable>>> m_tables; std::unordered_map<TableId, ObjToken<std::unique_ptr<ITable>>> m_tables;
std::unordered_map<KeymapId, ObjToken<Keymap>> m_keymaps; std::unordered_map<KeymapId, ObjToken<std::array<Keymap, 128>>> m_keymaps;
std::unordered_map<LayersId, ObjToken<std::vector<LayerMapping>>> m_layers; std::unordered_map<LayersId, ObjToken<std::vector<LayerMapping>>> m_layers;
template <athena::Endian DNAE> template <athena::Endian DNAE>
@ -1402,11 +1407,11 @@ public:
const std::unordered_map<SoundMacroId, ObjToken<SoundMacro>>& soundMacros() const { return m_soundMacros; } const std::unordered_map<SoundMacroId, ObjToken<SoundMacro>>& soundMacros() const { return m_soundMacros; }
const std::unordered_map<TableId, ObjToken<std::unique_ptr<ITable>>>& tables() const { return m_tables; } const std::unordered_map<TableId, ObjToken<std::unique_ptr<ITable>>>& tables() const { return m_tables; }
const std::unordered_map<KeymapId, ObjToken<Keymap>>& keymaps() const { return m_keymaps; } const std::unordered_map<KeymapId, ObjToken<std::array<Keymap, 128>>>& keymaps() const { return m_keymaps; }
const std::unordered_map<LayersId, ObjToken<std::vector<LayerMapping>>>& layers() const { return m_layers; } const std::unordered_map<LayersId, ObjToken<std::vector<LayerMapping>>>& layers() const { return m_layers; }
std::unordered_map<SoundMacroId, ObjToken<SoundMacro>>& soundMacros() { return m_soundMacros; } std::unordered_map<SoundMacroId, ObjToken<SoundMacro>>& soundMacros() { return m_soundMacros; }
std::unordered_map<TableId, ObjToken<std::unique_ptr<ITable>>>& tables() { return m_tables; } std::unordered_map<TableId, ObjToken<std::unique_ptr<ITable>>>& tables() { return m_tables; }
std::unordered_map<KeymapId, ObjToken<Keymap>>& keymaps() { return m_keymaps; } std::unordered_map<KeymapId, ObjToken<std::array<Keymap, 128>>>& keymaps() { return m_keymaps; }
std::unordered_map<LayersId, ObjToken<std::vector<LayerMapping>>>& layers() { return m_layers; } std::unordered_map<LayersId, ObjToken<std::vector<LayerMapping>>>& layers() { return m_layers; }
const SoundMacro* soundMacro(ObjectId id) const; const SoundMacro* soundMacro(ObjectId id) const;

View File

@ -152,10 +152,14 @@ AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r)
ObjectHeader<DNAE> objHead; ObjectHeader<DNAE> objHead;
atInt64 startPos = r.position(); atInt64 startPos = r.position();
objHead.read(r); objHead.read(r);
KeymapDNA<DNAE> kmData;
kmData.read(r);
auto& km = ret.m_keymaps[objHead.objectId.id]; auto& km = ret.m_keymaps[objHead.objectId.id];
km = MakeObj<Keymap>(kmData); km = MakeObj<std::array<Keymap, 128>>();
for (int i = 0; i < 128; ++i)
{
KeymapDNA<DNAE> kmData;
kmData.read(r);
(*km)[i] = kmData;
}
r.seek(startPos + objHead.size, athena::Begin); r.seek(startPos + objHead.size, athena::Begin);
} }
} }
@ -311,12 +315,17 @@ AudioGroupPool AudioGroupPool::CreateAudioGroupPool(SystemStringView groupPath)
{ {
ret.m_keymaps.reserve(r.getCurNode()->m_mapChildren.size()); ret.m_keymaps.reserve(r.getCurNode()->m_mapChildren.size());
for (const auto& k : r.getCurNode()->m_mapChildren) for (const auto& k : r.getCurNode()->m_mapChildren)
if (auto __v = r.enterSubRecord(k.first.c_str())) {
size_t mappingCount;
if (auto __v = r.enterSubVector(k.first.c_str(), mappingCount))
{ {
auto& kmOut = ret.m_keymaps[KeymapId::CurNameDB->resolveIdFromName(k.first)]; auto& kmOut = ret.m_keymaps[KeymapId::CurNameDB->resolveIdFromName(k.first)];
kmOut = MakeObj<Keymap>(); kmOut = MakeObj<std::array<Keymap, 128>>();
kmOut->read(r); for (int i = 0; i < mappingCount && i < 128; ++i)
if (auto __r2 = r.enterSubRecord(nullptr))
(*kmOut)[i].read(r);
} }
}
} }
if (auto __r = r.enterSubRecord("layers")) if (auto __r = r.enterSubRecord("layers"))
@ -388,7 +397,7 @@ const Keymap* AudioGroupPool::keymap(ObjectId id) const
auto search = m_keymaps.find(id); auto search = m_keymaps.find(id);
if (search == m_keymaps.cend()) if (search == m_keymaps.cend())
return nullptr; return nullptr;
return search->second.get(); return search->second.get()->data();
} }
const std::vector<LayerMapping>* AudioGroupPool::layer(ObjectId id) const const std::vector<LayerMapping>* AudioGroupPool::layer(ObjectId id) const
@ -991,10 +1000,16 @@ bool AudioGroupPool::toYAML(SystemStringView groupPath) const
{ {
for (const auto& p : SortUnorderedMap(m_keymaps)) for (const auto& p : SortUnorderedMap(m_keymaps))
{ {
if (auto __v = w.enterSubRecord(KeymapId::CurNameDB->resolveNameFromId(p.first).data())) if (auto __v = w.enterSubVector(KeymapId::CurNameDB->resolveNameFromId(p.first).data()))
{ {
w.setStyle(athena::io::YAMLNodeStyle::Flow); for (const auto& km : *p.second.get())
p.second.get()->write(w); {
if (auto __r2 = w.enterSubRecord(nullptr))
{
w.setStyle(athena::io::YAMLNodeStyle::Flow);
km.write(w);
}
}
} }
} }
} }

View File

@ -811,8 +811,15 @@ bool Voice::_loadKeymap(const Keymap* keymap, double ticksPerSec,
midiKey += km.transpose; midiKey += km.transpose;
bool ret = loadMacroObject(km.macro.id, 0, ticksPerSec, midiKey, midiVel, midiMod, pushPc); bool ret = loadMacroObject(km.macro.id, 0, ticksPerSec, midiKey, midiVel, midiMod, pushPc);
m_curVol = 1.f; m_curVol = 1.f;
_setPan((km.pan - 64) / 64.f); if (km.pan == -128)
_setSurroundPan(-1.f); {
_setSurroundPan(1.f);
}
else
{
_setPan((km.pan - 64) / 64.f);
_setSurroundPan(-1.f);
}
return ret; return ret;
} }