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)
{
++m_cycleIdx;
m_dragPoint = minDist;
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 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)
{
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);
ctrls->setDecayAndSustain(newDecay, newSustain * 100.0);
ctrls->setDecayAndSustain(newDecay, newSustain * 100.0, m_cycleIdx);
}
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));
ctrls->setRelease(newRelease, m_cycleIdx);
ctrls->m_release->setValue(newRelease);
}
}
@ -522,11 +524,13 @@ class ADSRAttackAndDecayUndoCommand : public EditorUndoCommand
{
double m_redoAttack, m_redoDecay;
double m_undoAttack, m_undoDecay;
uint64_t m_cycleCount;
bool m_undid = false;
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")),
m_redoAttack(redoAttack), m_redoDecay(redoDecay) {}
m_redoAttack(redoAttack), m_redoDecay(redoDecay), m_cycleCount(cycleCount) {}
void undo()
{
m_undid = true;
@ -569,7 +573,8 @@ public:
}
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_redoDecay = static_cast<const ADSRAttackAndDecayUndoCommand*>(other)->m_redoDecay;
@ -580,7 +585,7 @@ public:
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_attack->setValue(attack);
@ -588,7 +593,8 @@ void ADSRControls::setAttackAndDecay(double attack, double decay)
m_enableUpdate = true;
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();
}
@ -596,11 +602,13 @@ class ADSRDecayAndSustainUndoCommand : public EditorUndoCommand
{
double m_redoDecay, m_redoSustain;
double m_undoDecay, m_undoSustain;
uint64_t m_cycleCount;
bool m_undid = false;
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")),
m_redoDecay(redoDecay), m_redoSustain(redoSustain) {}
m_redoDecay(redoDecay), m_redoSustain(redoSustain), m_cycleCount(cycleCount) {}
void undo()
{
m_undid = true;
@ -643,7 +651,8 @@ public:
}
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_redoSustain = static_cast<const ADSRDecayAndSustainUndoCommand*>(other)->m_redoSustain;
@ -654,7 +663,7 @@ public:
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_decay->setValue(decay);
@ -662,18 +671,21 @@ void ADSRControls::setDecayAndSustain(double decay, double sustain)
m_enableUpdate = true;
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();
}
class ADSRReleaseUndoCommand : public EditorUndoCommand
{
double m_redoVal, m_undoVal;
uint64_t m_cycleCount;
bool m_undid = false;
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")),
m_redoVal(redoVal) {}
m_redoVal(redoVal), m_cycleCount(cycleCount) {}
void undo()
{
m_undid = true;
@ -710,7 +722,8 @@ public:
}
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;
return true;
@ -720,12 +733,23 @@ public:
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)
{
if (m_enableUpdate)
{
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 File

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

View File

@ -85,3 +85,14 @@ QString ShowInGraphicalShellString()
return MainWindow::tr("Show in Browser");
#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()));
}
/* Used for generating transform matrices to map SVG coordinate space */
QTransform RectToRect(const QRectF& from, const QRectF& to);
#endif //AMUSE_COMMON_HPP

View File

@ -10,10 +10,12 @@ class CurveEditUndoCommand : public EditorUndoCommand
{
uint8_t m_redoData[128];
uint8_t m_undoData[128];
bool m_usedExpr;
bool m_undid = false;
public:
CurveEditUndoCommand(const uint8_t* redoData, amuse::ObjToken<ProjectModel::CurveNode> node)
: EditorUndoCommand(node.get(), CurveControls::tr("Edit Curve"))
CurveEditUndoCommand(const uint8_t* redoData, bool usedExpr,
amuse::ObjToken<ProjectModel::CurveNode> node)
: EditorUndoCommand(node.get(), CurveControls::tr("Edit Curve")), m_usedExpr(usedExpr)
{
std::memcpy(m_redoData, redoData, 128);
}
@ -44,7 +46,7 @@ public:
}
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);
return true;
@ -153,7 +155,7 @@ void CurveView::mouseMoveEvent(QMouseEvent* ev)
std::memcpy(newData, curve.data.data(), 128);
newData[idx] = uint8_t(val);
g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, m_node));
g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, false, m_node));
update();
}
@ -227,7 +229,7 @@ void CurveControls::exprCommit()
if (notANumber)
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 File

@ -1,5 +1,6 @@
#include "EditorWidget.hpp"
#include "MainWindow.hpp"
#include <QStandardItemModel>
EditorWidget::EditorWidget(QWidget* parent)
: QWidget(parent)
@ -16,3 +17,24 @@ void EditorUndoCommand::redo()
{
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 <QUndoCommand>
#include <QApplication>
#include <QSpinBox>
#include <QComboBox>
#include <QWheelEvent>
#include "ProjectModel.hpp"
class EditorWidget : public QWidget
@ -44,4 +47,36 @@ public:
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

View File

@ -6,19 +6,7 @@
#include <QApplication>
#include <QScrollBar>
/* Used for generating transform matrices to map SVG coordinate space */
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[] =
const QString NaturalKeyNames[] =
{
QStringLiteral("C"),
QStringLiteral("D"),
@ -29,7 +17,7 @@ static const QString NaturalKeyNames[] =
QStringLiteral("B")
};
static const QString SharpKeyNames[] =
const QString SharpKeyNames[] =
{
QStringLiteral("Cs"),
QStringLiteral("Ds"),
@ -38,7 +26,7 @@ static const QString SharpKeyNames[] =
QStringLiteral("As")
};
static const QString KeyStrings[] =
const QString KeyStrings[] =
{
QStringLiteral("C"),
QStringLiteral("C#"),
@ -54,12 +42,12 @@ static const QString KeyStrings[] =
QStringLiteral("B")
};
static const int NaturalKeyNumbers[] =
const int NaturalKeyNumbers[] =
{
0, 2, 4, 5, 7, 9, 11
};
static const int SharpKeyNumbers[] =
const int SharpKeyNumbers[] =
{
1, 3, 6, 8, 10
};

View File

@ -6,6 +6,13 @@
#include <QSlider>
#include <QWheelEvent>
#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;

View File

@ -1,12 +1,600 @@
#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)
{
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)
: 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
#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
{
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:
explicit KeymapEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::KeymapNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void keyPressEvent(QKeyEvent* event);
};
#endif //AMUSE_KEYMAP_EDITOR_HPP

View File

@ -110,8 +110,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>1100</width>
<height>610</height>
<width>1103</width>
<height>606</height>
</rect>
</property>
</widget>
@ -151,6 +151,78 @@
<property name="bottomMargin">
<number>0</number>
</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>
<widget class="QScrollArea" name="keyboardScrollArea">
<property name="sizePolicy">
@ -189,7 +261,7 @@
<x>0</x>
<y>0</y>
<width>1501</width>
<height>85</height>
<height>80</height>
</rect>
</property>
<property name="sizePolicy">
@ -213,78 +285,6 @@
</widget>
</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>
<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>
</widget>
</widget>
@ -298,7 +298,7 @@
<x>0</x>
<y>0</y>
<width>1360</width>
<height>27</height>
<height>34</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
@ -325,7 +325,7 @@
</widget>
<widget class="QMenu" name="menuProject">
<property name="title">
<string>P&amp;roject</string>
<string>Pro&amp;ject</string>
</property>
<addaction name="actionNew_Subproject"/>
<addaction name="separator"/>

View File

@ -269,6 +269,10 @@ void ProjectModel::_resetModelData()
col.reserve(keymaps.size());
for (auto& keymap : SortUnorderedMap(keymaps))
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 =

View File

@ -230,7 +230,7 @@ public:
using SoundMacroNode = PoolObjectNode<amuse::SoundMacroId, amuse::SoundMacro, INode::Type::SoundMacro>;
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 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 SampleNode = PoolObjectNode<amuse::SampleId, amuse::SampleEntry, INode::Type::Sample>;

View File

@ -10,14 +10,6 @@
#include <QApplication>
#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)
: QPushButton(parent)
{

View File

@ -17,37 +17,6 @@ class SoundMacroEditor;
class SoundMacroListing;
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
{
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>
<name>ADSRControls</name>
<message>
<location filename="../ADSREditor.cpp" line="335"/>
<location filename="../ADSREditor.cpp" line="337"/>
<source>Change Attack</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="399"/>
<location filename="../ADSREditor.cpp" line="401"/>
<source>Change Decay</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="463"/>
<location filename="../ADSREditor.cpp" line="465"/>
<source>Change Sustain</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="528"/>
<location filename="../ADSREditor.cpp" line="532"/>
<source>Change Attack/Decay</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="602"/>
<location filename="../ADSREditor.cpp" line="610"/>
<source>Change Decay/Sustain</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="675"/>
<location filename="../ADSREditor.cpp" line="687"/>
<source>Change Release</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="765"/>
<location filename="../ADSREditor.cpp" line="789"/>
<source>Change DLS</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="852"/>
<location filename="../ADSREditor.cpp" line="876"/>
<source>Change Vel To Attack</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="905"/>
<location filename="../ADSREditor.cpp" line="929"/>
<source>Change Key To Decay</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="970"/>
<location filename="../ADSREditor.cpp" line="994"/>
<source>Attack</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="978"/>
<location filename="../ADSREditor.cpp" line="992"/>
<location filename="../ADSREditor.cpp" line="1017"/>
<location filename="../ADSREditor.cpp" line="1002"/>
<location filename="../ADSREditor.cpp" line="1016"/>
<location filename="../ADSREditor.cpp" line="1041"/>
<source> sec</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="984"/>
<location filename="../ADSREditor.cpp" line="1008"/>
<source>Decay</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="998"/>
<location filename="../ADSREditor.cpp" line="1022"/>
<source>Sustain</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="1003"/>
<location filename="../ADSREditor.cpp" line="1027"/>
<source> %</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="1009"/>
<location filename="../ADSREditor.cpp" line="1033"/>
<source>Release</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="1023"/>
<location filename="../ADSREditor.cpp" line="1047"/>
<source>DLS</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="1031"/>
<location filename="../ADSREditor.cpp" line="1055"/>
<source>Vel To Attack</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="1043"/>
<location filename="../ADSREditor.cpp" line="1067"/>
<source>Key To Decay</source>
<translation type="unfinished"></translation>
</message>
@ -99,7 +99,7 @@
<context>
<name>CommandWidget</name>
<message>
<location filename="../SoundMacroEditor.cpp" line="350"/>
<location filename="../SoundMacroEditor.cpp" line="342"/>
<source>Change %1</source>
<translation type="unfinished"></translation>
</message>
@ -107,32 +107,80 @@
<context>
<name>CurveControls</name>
<message>
<location filename="../CurveEditor.cpp" line="16"/>
<location filename="../CurveEditor.cpp" line="18"/>
<source>Edit Curve</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../CurveEditor.cpp" line="228"/>
<location filename="../CurveEditor.cpp" line="230"/>
<source>Did not evaluate as a number</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../CurveEditor.cpp" line="253"/>
<location filename="../CurveEditor.cpp" line="255"/>
<source>Expression</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../CurveEditor.cpp" line="262"/>
<location filename="../CurveEditor.cpp" line="264"/>
<source>Apply</source>
<translation type="unfinished"></translation>
</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>
<translation type="unfinished"></translation>
</message>
</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>
<name>MainWindow</name>
<message>
@ -153,7 +201,7 @@
</message>
<message>
<location filename="../MainWindow.ui" line="328"/>
<source>P&amp;roject</source>
<source>Pro&amp;ject</source>
<translation type="unfinished"></translation>
</message>
<message>
@ -522,15 +570,23 @@
<context>
<name>ModulationSlider</name>
<message>
<location filename="../KeyboardWidget.cpp" line="267"/>
<location filename="../KeyboardWidget.cpp" line="255"/>
<source>Modulation: %1</source>
<translation type="unfinished"></translation>
</message>
</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>
<name>PitchSlider</name>
<message>
<location filename="../KeyboardWidget.cpp" line="276"/>
<location filename="../KeyboardWidget.cpp" line="264"/>
<source>Pitch: %1</source>
<translation type="unfinished"></translation>
</message>
@ -558,17 +614,17 @@
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="275"/>
<location filename="../ProjectModel.cpp" line="279"/>
<source>Layers</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="282"/>
<location filename="../ProjectModel.cpp" line="286"/>
<source>Samples</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ProjectModel.cpp" line="434"/>
<location filename="../ProjectModel.cpp" line="438"/>
<source>Delete %1</source>
<translation type="unfinished"></translation>
</message>
@ -647,72 +703,72 @@
<context>
<name>SoundMacroCatalogue</name>
<message>
<location filename="../SoundMacroEditor.cpp" line="977"/>
<location filename="../SoundMacroEditor.cpp" line="969"/>
<source>Control</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SoundMacroEditor.cpp" line="978"/>
<location filename="../SoundMacroEditor.cpp" line="970"/>
<source>Pitch</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SoundMacroEditor.cpp" line="979"/>
<location filename="../SoundMacroEditor.cpp" line="971"/>
<source>Sample</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SoundMacroEditor.cpp" line="980"/>
<location filename="../SoundMacroEditor.cpp" line="972"/>
<source>Setup</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SoundMacroEditor.cpp" line="981"/>
<location filename="../SoundMacroEditor.cpp" line="973"/>
<source>Special</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SoundMacroEditor.cpp" line="982"/>
<location filename="../SoundMacroEditor.cpp" line="974"/>
<source>Structure</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SoundMacroEditor.cpp" line="983"/>
<location filename="../SoundMacroEditor.cpp" line="975"/>
<source>Volume</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SoundMacroEditor.cpp" line="988"/>
<location filename="../SoundMacroEditor.cpp" line="980"/>
<source>Commands to control the voice</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SoundMacroEditor.cpp" line="989"/>
<location filename="../SoundMacroEditor.cpp" line="981"/>
<source>Commands to control the voice&apos;s pitch</source>
<translation type="unfinished"></translation>
</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>
<translation type="unfinished"></translation>
</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>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SoundMacroEditor.cpp" line="992"/>
<location filename="../SoundMacroEditor.cpp" line="984"/>
<source>Miscellaneous commands</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SoundMacroEditor.cpp" line="993"/>
<location filename="../SoundMacroEditor.cpp" line="985"/>
<source>Commands to control macro branching</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SoundMacroEditor.cpp" line="994"/>
<location filename="../SoundMacroEditor.cpp" line="986"/>
<source>Commands to control the voice&apos;s volume</source>
<translation type="unfinished"></translation>
</message>
@ -720,7 +776,7 @@
<context>
<name>SoundMacroDeleteButton</name>
<message>
<location filename="../SoundMacroEditor.cpp" line="153"/>
<location filename="../SoundMacroEditor.cpp" line="145"/>
<source>Delete this SoundMacro</source>
<translation type="unfinished"></translation>
</message>
@ -728,17 +784,17 @@
<context>
<name>SoundMacroListing</name>
<message>
<location filename="../SoundMacroEditor.cpp" line="676"/>
<location filename="../SoundMacroEditor.cpp" line="668"/>
<source>Reorder %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SoundMacroEditor.cpp" line="808"/>
<location filename="../SoundMacroEditor.cpp" line="800"/>
<source>Insert %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SoundMacroEditor.cpp" line="861"/>
<location filename="../SoundMacroEditor.cpp" line="853"/>
<source>Delete %1</source>
<translation type="unfinished"></translation>
</message>
@ -754,7 +810,7 @@
<context>
<name>TargetButton</name>
<message>
<location filename="../SoundMacroEditor.cpp" line="27"/>
<location filename="../SoundMacroEditor.cpp" line="19"/>
<source>Set step with target click</source>
<translation type="unfinished"></translation>
</message>
@ -762,7 +818,7 @@
<context>
<name>VelocitySlider</name>
<message>
<location filename="../KeyboardWidget.cpp" line="258"/>
<location filename="../KeyboardWidget.cpp" line="246"/>
<source>Velocity: %1</source>
<translation type="unfinished"></translation>
</message>

View File

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

View File

@ -1310,9 +1310,9 @@ struct Keymap : BigDNA
{
AT_DECL_DNA_YAML
SoundMacroIdDNA<athena::Big> macro;
Value<atInt8> transpose;
Value<atInt8> pan; /* -128 for surround-channel only */
Value<atInt8> prioOffset;
Value<atInt8> transpose = 0;
Value<atInt8> pan = 64; /* -128 for surround-channel only */
Value<atInt8> prioOffset = 0;
Keymap() = default;
@ -1331,6 +1331,11 @@ struct Keymap : BigDNA
ret.prioOffset = prioOffset;
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) */
@ -1390,7 +1395,7 @@ class AudioGroupPool
{
std::unordered_map<SoundMacroId, ObjToken<SoundMacro>> m_soundMacros;
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;
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<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; }
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<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; }
const SoundMacro* soundMacro(ObjectId id) const;

View File

@ -152,10 +152,14 @@ AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r)
ObjectHeader<DNAE> objHead;
atInt64 startPos = r.position();
objHead.read(r);
KeymapDNA<DNAE> kmData;
kmData.read(r);
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);
}
}
@ -311,12 +315,17 @@ AudioGroupPool AudioGroupPool::CreateAudioGroupPool(SystemStringView groupPath)
{
ret.m_keymaps.reserve(r.getCurNode()->m_mapChildren.size());
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)];
kmOut = MakeObj<Keymap>();
kmOut->read(r);
kmOut = MakeObj<std::array<Keymap, 128>>();
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"))
@ -388,7 +397,7 @@ const Keymap* AudioGroupPool::keymap(ObjectId id) const
auto search = m_keymaps.find(id);
if (search == m_keymaps.cend())
return nullptr;
return search->second.get();
return search->second.get()->data();
}
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))
{
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);
p.second.get()->write(w);
for (const auto& km : *p.second.get())
{
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;
bool ret = loadMacroObject(km.macro.id, 0, ticksPerSec, midiKey, midiVel, midiMod, pushPc);
m_curVol = 1.f;
_setPan((km.pan - 64) / 64.f);
_setSurroundPan(-1.f);
if (km.pan == -128)
{
_setSurroundPan(1.f);
}
else
{
_setPan((km.pan - 64) / 64.f);
_setSurroundPan(-1.f);
}
return ret;
}