#include "ADSREditor.hpp" #include "MainWindow.hpp" #include #include #include #include #include const QColor Red = QColor(255, 127, 127); const QColor Green = QColor(127, 255, 127); const QColor Blue = QColor(150, 150, 255); ADSREditor* ADSRView::getEditor() const { return qobject_cast(parentWidget()); } void ADSRView::loadData(ProjectModel::ADSRNode* node) { m_node = node; } void ADSRView::unloadData() { m_node.reset(); } ProjectModel::INode* ADSRView::currentNode() const { return m_node.get(); } void ADSRView::paintEvent(QPaintEvent* ev) { if (!m_node) { return; } QPainter painter(this); painter.setRenderHint(QPainter::Antialiasing); const amuse::ITable& table = **m_node->m_obj; qreal adTime = 0.0; qreal aTime = 0.0; qreal relTime = 0.0; qreal sustain = 0.0; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { const auto& adsr = static_cast(table); adTime += adsr.getAttack(); aTime = adsr.getAttack(); adTime += adsr.getDecay(); sustain = adsr.getSustain(); relTime = adsr.getRelease(); } else if (table.Isa() == amuse::ITable::Type::ADSR) { const auto& adsr = static_cast(table); adTime += adsr.getAttack(); aTime = adsr.getAttack(); adTime += adsr.getDecay(); sustain = adsr.getSustain(); relTime = adsr.getRelease(); } const qreal totalTime = adTime + 1.0 + relTime; const qreal deviceRatio = devicePixelRatioF(); const qreal penWidth = std::max(std::floor(deviceRatio), 1.0) / deviceRatio; painter.setPen(QPen(QColor(127, 127, 127), penWidth)); painter.setFont(m_gridFont); const qreal yIncrement = (height() - 16.0) / 10.0; for (size_t i = 0; i < m_percentTexts.size(); ++i) { const qreal thisY = qreal(i) * yIncrement; const qreal textY = thisY - (i == 0 ? 2.0 : (i == 10 ? 16.0 : 8.0)); painter.drawStaticText(QPointF(0.0, textY), m_percentTexts[i]); painter.drawLine(QPointF(30.0, thisY), QPointF(width(), thisY)); } const size_t maxTime = size_t(std::max(adTime, relTime)) + 1; if (m_timeTexts.size() < maxTime) { m_timeTexts.reserve(maxTime); for (size_t i = m_timeTexts.size(); i < maxTime; ++i) { m_timeTexts.emplace_back(); QStaticText& text = m_timeTexts.back(); text.setText(QStringLiteral("%1.0s").arg(i)); text.setTextWidth(32.0); text.setTextOption(QTextOption(Qt::AlignHCenter | Qt::AlignBaseline)); } } qreal xBase = 30.0; const qreal xIncrement = (width() - xBase) / totalTime; for (size_t i = 0; i <= size_t(adTime); ++i) { const qreal thisX = i * xIncrement + xBase; const qreal textX = thisX - 15.0; painter.drawStaticText(QPointF(textX, height() - 16.0), m_timeTexts[i]); painter.drawLine(QPointF(thisX, 0.0), QPointF(thisX, height() - 16.0)); } xBase = (width() - 30.0) * ((adTime + 1.0) / totalTime) + 30.0; for (size_t i = 0; i <= size_t(relTime); ++i) { const qreal thisX = i * xIncrement + xBase; const qreal textX = thisX - 15.0; painter.drawStaticText(QPointF(textX, height() - 16.0), m_timeTexts[i]); painter.drawLine(QPointF(thisX, 0.0), QPointF(thisX, height() - 16.0)); } const qreal sustainY = (height() - 16.0) - (height() - 16.0) * sustain; const auto pt0 = QPointF(30.0, height() - 16.0); const auto pt1 = QPointF((width() - 30.0) * (aTime / totalTime) + 30.0, 0.0); const auto pt2 = QPointF((width() - 30.0) * (adTime / totalTime) + 30.0, sustainY); const auto pt3 = QPointF((width() - 30.0) * ((adTime + 1.0) / totalTime) + 30.0, sustainY); const auto pt4 = QPointF(width(), height() - 16.0); painter.setPen(QPen(Red, penWidth * 3.0)); painter.drawLine(pt0, pt1); painter.setPen(QPen(Green, penWidth * 3.0)); painter.drawLine(pt1, pt2); painter.setPen(QPen(Qt::white, penWidth * 3.0)); painter.drawLine(pt2, pt3); painter.setPen(QPen(Blue, penWidth * 3.0)); painter.drawLine(pt3, pt4); painter.setPen(Qt::NoPen); painter.setBrush(Green); painter.drawEllipse(pt1, 8.0, 8.0); painter.setBrush(Qt::white); painter.drawEllipse(pt2, 8.0, 8.0); painter.setBrush(Blue); painter.drawEllipse(pt3, 8.0, 8.0); } static qreal PointDistanceSq(const QPointF& p1, const QPointF& p2) { const QPointF delta = p1 - p2; return QPointF::dotProduct(delta, delta); } static qreal PointDistance(const QPointF& p1, const QPointF& p2) { return std::sqrt(PointDistanceSq(p1, p2)); } void ADSRView::mousePressEvent(QMouseEvent* ev) { if (!m_node) return; const amuse::ITable& table = **m_node->m_obj; qreal adTime = 0.0; qreal aTime = 0.0; qreal relTime = 0.0; qreal sustain = 0.0; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { const auto& adsr = static_cast(table); adTime += adsr.getAttack(); aTime = adsr.getAttack(); adTime += adsr.getDecay(); sustain = adsr.getSustain(); relTime = adsr.getRelease(); } else if (table.Isa() == amuse::ITable::Type::ADSR) { const auto& adsr = static_cast(table); adTime += adsr.getAttack(); aTime = adsr.getAttack(); adTime += adsr.getDecay(); sustain = adsr.getSustain(); relTime = adsr.getRelease(); } const qreal totalTime = adTime + 1.0 + relTime; const qreal sustainY = (height() - 16.0) - (height() - 16.0) * sustain; const QPointF points[] = { QPointF((width() - 30.0) * (aTime / totalTime) + 30.0, 0.0), QPointF((width() - 30.0) * (adTime / totalTime) + 30.0, sustainY), QPointF((width() - 30.0) * ((adTime + 1.0) / totalTime) + 30.0, sustainY), }; const qreal dists[] = { PointDistance(ev->localPos(), points[0]), PointDistance(ev->localPos(), points[1]), PointDistance(ev->localPos(), points[2]), }; int minDist = 0; if (dists[1] < dists[minDist] + 8.0) /* pt1 overlaps pt0, so include radius in test */ minDist = 1; if (dists[2] < dists[minDist]) minDist = 2; if (dists[minDist] < 10.0) { ++m_cycleIdx; m_dragPoint = minDist; mouseMoveEvent(ev); } } void ADSRView::mouseReleaseEvent(QMouseEvent* ev) { m_dragPoint = -1; } void ADSRView::mouseMoveEvent(QMouseEvent* ev) { const amuse::ITable& table = **m_node->m_obj; ADSRControls* ctrls = getEditor()->m_controls; qreal adTime = 0.0; qreal aTime = 0.0; qreal relTime = 0.0; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { const auto& adsr = static_cast(table); adTime += adsr.getAttack(); aTime = adsr.getAttack(); adTime += adsr.getDecay(); relTime = adsr.getRelease(); } else if (table.Isa() == amuse::ITable::Type::ADSR) { const auto& adsr = static_cast(table); adTime += adsr.getAttack(); aTime = adsr.getAttack(); adTime += adsr.getDecay(); relTime = adsr.getRelease(); } qreal totalTime = adTime + 1.0 + relTime; if (m_dragPoint == 0) { const qreal newAttack = std::max(0.0, (ev->localPos().x() - 30.0) / (width() - 30.0) * totalTime); const qreal delta = newAttack - aTime; ctrls->setAttackAndDecay(newAttack, std::max(0.0, ctrls->m_decay->value() - delta), m_cycleIdx); } else if (m_dragPoint == 1) { const qreal newDecay = std::max(0.0, (ev->localPos().x() - 30.0) * totalTime / (width() - 30.0) - aTime); const qreal newSustain = (-ev->localPos().y() + (height() - 16.0)) / (height() - 16.0); ctrls->setDecayAndSustain(newDecay, newSustain * 100.0, m_cycleIdx); } else if (m_dragPoint == 2) { const 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); } } ADSRView::ADSRView(QWidget* parent) : QWidget(parent) { for (size_t i = 0; i < m_percentTexts.size(); ++i) { m_percentTexts[i].setText(QStringLiteral("%1%").arg(100 - i * 10)); m_percentTexts[i].setTextOption(QTextOption(Qt::AlignVCenter | Qt::AlignRight)); m_percentTexts[i].setTextWidth(28.0); } m_gridFont.setPointSize(8); } ADSRView::~ADSRView() = default; ADSREditor* ADSRControls::getEditor() const { return qobject_cast(parentWidget()); } void ADSRControls::loadData() { m_enableUpdate = false; const amuse::ITable& table = **getEditor()->m_adsrView->m_node->m_obj; m_attack->setDisabled(false); m_decay->setDisabled(false); m_sustain->setDisabled(false); m_release->setDisabled(false); m_dls->setDisabled(false); if (table.Isa() == amuse::ITable::Type::ADSRDLS) { const auto& adsr = static_cast(table); m_attack->setValue(adsr.getAttack()); m_decay->setValue(adsr.getDecay()); m_sustain->setValue(adsr.getSustain() * 100.0); m_release->setValue(adsr.getRelease()); m_dls->setChecked(true); m_velToAttackLab->setVisible(true); m_velToAttack->setVisible(true); m_velToAttack->setValue(adsr._getVelToAttack()); m_keyToDecayLab->setVisible(true); m_keyToDecay->setVisible(true); m_keyToDecay->setValue(adsr._getKeyToDecay()); } else if (table.Isa() == amuse::ITable::Type::ADSR) { const auto& adsr = static_cast(table); m_attack->setValue(adsr.getAttack()); m_decay->setValue(adsr.getDecay()); m_sustain->setValue(adsr.getSustain() * 100.0); m_release->setValue(adsr.getRelease()); m_dls->setChecked(false); m_velToAttackLab->setVisible(false); m_velToAttack->setVisible(false); m_keyToDecayLab->setVisible(false); m_keyToDecay->setVisible(false); } else { unloadData(); } m_enableUpdate = true; } void ADSRControls::unloadData() { m_attack->setDisabled(true); m_decay->setDisabled(true); m_sustain->setDisabled(true); m_release->setDisabled(true); m_dls->setDisabled(true); m_velToAttackLab->setVisible(false); m_velToAttack->setVisible(false); m_keyToDecayLab->setVisible(false); m_keyToDecay->setVisible(false); } class ADSRAttackUndoCommand : public EditorUndoCommand { double m_redoVal, m_undoVal; bool m_undid = false; public: ADSRAttackUndoCommand(double redoVal, amuse::ObjToken node) : EditorUndoCommand(node.get(), ADSRControls::tr("Change Attack")), m_redoVal(redoVal) {} void undo() override { m_undid = true; amuse::ITable& table = **m_node.cast()->m_obj; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { auto& adsr = static_cast(table); adsr.setAttack(m_undoVal); } else if (table.Isa() == amuse::ITable::Type::ADSR) { auto& adsr = static_cast(table); adsr.setAttack(m_undoVal); } EditorUndoCommand::undo(); } void redo() override { amuse::ITable& table = **m_node.cast()->m_obj; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { auto& adsr = static_cast(table); m_undoVal = adsr.getAttack(); adsr.setAttack(m_redoVal); } else if (table.Isa() == amuse::ITable::Type::ADSR) { auto& adsr = static_cast(table); m_undoVal = adsr.getAttack(); adsr.setAttack(m_redoVal); } if (m_undid) EditorUndoCommand::redo(); } bool mergeWith(const QUndoCommand* other) override { if (other->id() == id()) { m_redoVal = static_cast(other)->m_redoVal; return true; } return false; } int id() const override { return int(Id::ADSRAttack); } }; void ADSRControls::attackChanged(double val) { if (m_enableUpdate) { ADSRView* view = getEditor()->m_adsrView; g_MainWindow->pushUndoCommand(new ADSRAttackUndoCommand(val, view->m_node)); view->update(); } } class ADSRDecayUndoCommand : public EditorUndoCommand { double m_redoVal, m_undoVal; bool m_undid = false; public: ADSRDecayUndoCommand(double redoVal, amuse::ObjToken node) : EditorUndoCommand(node.get(), ADSRControls::tr("Change Decay")), m_redoVal(redoVal) {} void undo() override { m_undid = true; amuse::ITable& table = **m_node.cast()->m_obj; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { auto& adsr = static_cast(table); adsr.setDecay(m_undoVal); } else if (table.Isa() == amuse::ITable::Type::ADSR) { auto& adsr = static_cast(table); adsr.setDecay(m_undoVal); } EditorUndoCommand::undo(); } void redo() override { amuse::ITable& table = **m_node.cast()->m_obj; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { auto& adsr = static_cast(table); m_undoVal = adsr.getDecay(); adsr.setDecay(m_redoVal); } else if (table.Isa() == amuse::ITable::Type::ADSR) { auto& adsr = static_cast(table); m_undoVal = adsr.getDecay(); adsr.setDecay(m_redoVal); } if (m_undid) EditorUndoCommand::redo(); } bool mergeWith(const QUndoCommand* other) override { if (other->id() == id()) { m_redoVal = static_cast(other)->m_redoVal; return true; } return false; } int id() const override { return int(Id::ADSRDecay); } }; void ADSRControls::decayChanged(double val) { if (m_enableUpdate) { ADSRView* view = getEditor()->m_adsrView; g_MainWindow->pushUndoCommand(new ADSRDecayUndoCommand(val, view->m_node)); view->update(); } } class ADSRSustainUndoCommand : public EditorUndoCommand { double m_redoVal, m_undoVal; bool m_undid = false; public: ADSRSustainUndoCommand(double redoVal, amuse::ObjToken node) : EditorUndoCommand(node.get(), ADSRControls::tr("Change Sustain")), m_redoVal(redoVal) {} void undo() override { m_undid = true; amuse::ITable& table = **m_node.cast()->m_obj; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { auto& adsr = static_cast(table); adsr.setSustain(m_undoVal); } else if (table.Isa() == amuse::ITable::Type::ADSR) { auto& adsr = static_cast(table); adsr.setSustain(m_undoVal); } EditorUndoCommand::undo(); } void redo() override { amuse::ITable& table = **m_node.cast()->m_obj; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { auto& adsr = static_cast(table); m_undoVal = adsr.getSustain(); adsr.setSustain(m_redoVal); } else if (table.Isa() == amuse::ITable::Type::ADSR) { auto& adsr = static_cast(table); m_undoVal = adsr.getSustain(); adsr.setSustain(m_redoVal); } if (m_undid) EditorUndoCommand::redo(); } bool mergeWith(const QUndoCommand* other) override { if (other->id() == id()) { m_redoVal = static_cast(other)->m_redoVal; return true; } return false; } int id() const override { return int(Id::ADSRSustain); } }; void ADSRControls::sustainChanged(double val) { if (m_enableUpdate) { ADSRView* view = getEditor()->m_adsrView; g_MainWindow->pushUndoCommand(new ADSRSustainUndoCommand(val / 100.0, view->m_node)); view->update(); } } 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, uint64_t cycleCount, amuse::ObjToken node) : EditorUndoCommand(node.get(), ADSRControls::tr("Change Attack/Decay")) , m_redoAttack(redoAttack) , m_redoDecay(redoDecay) , m_cycleCount(cycleCount) {} void undo() override { m_undid = true; amuse::ITable& table = **m_node.cast()->m_obj; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { auto& adsr = static_cast(table); adsr.setAttack(m_undoAttack); adsr.setDecay(m_undoDecay); } else if (table.Isa() == amuse::ITable::Type::ADSR) { auto& adsr = static_cast(table); adsr.setAttack(m_undoAttack); adsr.setDecay(m_undoDecay); } EditorUndoCommand::undo(); } void redo() override { amuse::ITable& table = **m_node.cast()->m_obj; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { auto& adsr = static_cast(table); m_undoAttack = adsr.getAttack(); m_undoDecay = adsr.getDecay(); adsr.setAttack(m_redoAttack); adsr.setDecay(m_redoDecay); } else if (table.Isa() == amuse::ITable::Type::ADSR) { auto& adsr = static_cast(table); m_undoAttack = adsr.getAttack(); m_undoDecay = adsr.getDecay(); adsr.setAttack(m_redoAttack); adsr.setDecay(m_redoDecay); } if (m_undid) EditorUndoCommand::redo(); } bool mergeWith(const QUndoCommand* other) override { const auto* const command = static_cast(other); if (other->id() == id() && m_cycleCount == command->m_cycleCount) { m_redoAttack = command->m_redoAttack; m_redoDecay = command->m_redoDecay; return true; } return false; } int id() const override { return int(Id::ADSRAttackAndDecay); } }; void ADSRControls::setAttackAndDecay(double attack, double decay, uint64_t cycleCount) { m_enableUpdate = false; m_attack->setValue(attack); m_decay->setValue(decay); m_enableUpdate = true; ADSRView* view = getEditor()->m_adsrView; g_MainWindow->pushUndoCommand( new ADSRAttackAndDecayUndoCommand(m_attack->value(), m_decay->value(), cycleCount, view->m_node)); view->update(); } 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, uint64_t cycleCount, amuse::ObjToken node) : EditorUndoCommand(node.get(), ADSRControls::tr("Change Decay/Sustain")) , m_redoDecay(redoDecay) , m_redoSustain(redoSustain) , m_cycleCount(cycleCount) {} void undo() override { m_undid = true; amuse::ITable& table = **m_node.cast()->m_obj; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { auto& adsr = static_cast(table); adsr.setDecay(m_undoDecay); adsr.setSustain(m_undoSustain); } else if (table.Isa() == amuse::ITable::Type::ADSR) { auto& adsr = static_cast(table); adsr.setDecay(m_undoDecay); adsr.setSustain(m_undoSustain); } EditorUndoCommand::undo(); } void redo() override { amuse::ITable& table = **m_node.cast()->m_obj; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { auto& adsr = static_cast(table); m_undoDecay = adsr.getDecay(); m_undoSustain = adsr.getSustain(); adsr.setDecay(m_redoDecay); adsr.setSustain(m_redoSustain); } else if (table.Isa() == amuse::ITable::Type::ADSR) { auto& adsr = static_cast(table); m_undoDecay = adsr.getDecay(); m_undoSustain = adsr.getSustain(); adsr.setDecay(m_redoDecay); adsr.setSustain(m_redoSustain); } if (m_undid) EditorUndoCommand::redo(); } bool mergeWith(const QUndoCommand* other) override { const auto* const command = static_cast(other); if (other->id() == id() && m_cycleCount == command->m_cycleCount) { m_redoDecay = command->m_redoDecay; m_redoSustain = command->m_redoSustain; return true; } return false; } int id() const override { return int(Id::ADSRDecayAndSustain); } }; void ADSRControls::setDecayAndSustain(double decay, double sustain, uint64_t cycleCount) { m_enableUpdate = false; m_decay->setValue(decay); m_sustain->setValue(sustain); m_enableUpdate = true; ADSRView* view = getEditor()->m_adsrView; 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, uint64_t cycleCount, amuse::ObjToken node) : EditorUndoCommand(node.get(), ADSRControls::tr("Change Release")), m_redoVal(redoVal), m_cycleCount(cycleCount) {} void undo() override { m_undid = true; amuse::ITable& table = **m_node.cast()->m_obj; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { auto& adsr = static_cast(table); adsr.setRelease(m_undoVal); } else if (table.Isa() == amuse::ITable::Type::ADSR) { auto& adsr = static_cast(table); adsr.setRelease(m_undoVal); } EditorUndoCommand::undo(); } void redo() override { amuse::ITable& table = **m_node.cast()->m_obj; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { auto& adsr = static_cast(table); m_undoVal = adsr.getRelease(); adsr.setRelease(m_redoVal); } else if (table.Isa() == amuse::ITable::Type::ADSR) { auto& adsr = static_cast(table); m_undoVal = adsr.getRelease(); adsr.setRelease(m_redoVal); } if (m_undid) EditorUndoCommand::redo(); } bool mergeWith(const QUndoCommand* other) override { const auto* const command = static_cast(other); if (other->id() == id() && m_cycleCount == command->m_cycleCount) { m_redoVal = command->m_redoVal; return true; } return false; } int id() const override { 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, ~0ull, view->m_node)); view->update(); } } template static std::unique_ptr MakeAlternateVersion(amuse::ITable& table) { std::unique_ptr ret = std::make_unique(); if (table.Isa() == amuse::ITable::Type::ADSRDLS) { auto& adsr = static_cast(table); ret->setAttack(adsr.getAttack()); ret->setDecay(adsr.getDecay()); ret->setSustain(adsr.getSustain()); ret->setRelease(adsr.getRelease()); } else if (table.Isa() == amuse::ITable::Type::ADSR) { auto& adsr = static_cast(table); ret->setAttack(adsr.getAttack()); ret->setDecay(adsr.getDecay()); ret->setSustain(adsr.getSustain()); ret->setRelease(adsr.getRelease()); } return ret; } class ADSRDLSUndoCommand : public EditorUndoCommand { bool m_redoVal; double m_redoVelToAttack, m_redoKeyToDecay; double m_undoVelToAttack, m_undoKeyToDecay; bool m_undid = false; public: ADSRDLSUndoCommand(bool redoVal, double redoVelToAttack, double redoKeyToDecay, amuse::ObjToken node) : EditorUndoCommand(node.get(), ADSRControls::tr("Change DLS")) , m_redoVal(redoVal) , m_redoVelToAttack(redoVelToAttack) , m_redoKeyToDecay(redoKeyToDecay) {} void undo() override { m_undid = true; std::unique_ptr& table = *m_node.cast()->m_obj; if ((table->Isa() == amuse::ITable::Type::ADSRDLS && !m_redoVal) || (table->Isa() == amuse::ITable::Type::ADSR && m_redoVal)) { return; } std::unique_ptr oldTable = std::move(table); if (!m_redoVal) { table = MakeAlternateVersion(*oldTable); static_cast(*table)._setVelToAttack(m_undoVelToAttack); static_cast(*table)._setKeyToDecay(m_undoKeyToDecay); } else { table = MakeAlternateVersion(*oldTable); } EditorUndoCommand::undo(); } void redo() override { std::unique_ptr& table = *m_node.cast()->m_obj; if ((table->Isa() == amuse::ITable::Type::ADSRDLS && m_redoVal) || (table->Isa() == amuse::ITable::Type::ADSR && !m_redoVal)) { return; } std::unique_ptr oldTable = std::move(table); if (m_redoVal) { table = MakeAlternateVersion(*oldTable); static_cast(*table)._setVelToAttack(m_redoVelToAttack); static_cast(*table)._setKeyToDecay(m_redoKeyToDecay); } else { if (oldTable->Isa() == amuse::ITable::Type::ADSRDLS) { m_undoVelToAttack = static_cast(*oldTable)._getVelToAttack(); m_undoKeyToDecay = static_cast(*oldTable)._getKeyToDecay(); } table = MakeAlternateVersion(*oldTable); } if (m_undid) EditorUndoCommand::redo(); } bool mergeWith(const QUndoCommand* other) override { if (other->id() == id()) { const auto* const command = static_cast(other); m_redoVal = command->m_redoVal; m_redoVelToAttack = command->m_redoVelToAttack; m_redoKeyToDecay = command->m_redoKeyToDecay; return true; } return false; } int id() const override { return int(Id::ADSRDLS); } }; void ADSRControls::dlsStateChanged(int state) { if (m_enableUpdate) { m_velToAttackLab->setVisible(state == Qt::Checked); m_velToAttack->setVisible(state == Qt::Checked); m_keyToDecayLab->setVisible(state == Qt::Checked); m_keyToDecay->setVisible(state == Qt::Checked); ADSRView* view = getEditor()->m_adsrView; g_MainWindow->pushUndoCommand( new ADSRDLSUndoCommand(state == Qt::Checked, m_velToAttack->value(), m_keyToDecay->value(), view->m_node)); view->update(); } } class ADSRVelToAttackUndoCommand : public EditorUndoCommand { double m_redoVal, m_undoVal; bool m_undid = false; public: ADSRVelToAttackUndoCommand(double redoVal, amuse::ObjToken node) : EditorUndoCommand(node.get(), ADSRControls::tr("Change Vel To Attack")), m_redoVal(redoVal) {} void undo() override { m_undid = true; amuse::ITable& table = **m_node.cast()->m_obj; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { auto& adsr = static_cast(table); adsr._setVelToAttack(m_undoVal); } EditorUndoCommand::undo(); } void redo() override { amuse::ITable& table = **m_node.cast()->m_obj; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { auto& adsr = static_cast(table); m_undoVal = adsr._getVelToAttack(); adsr._setVelToAttack(m_redoVal); } if (m_undid) EditorUndoCommand::redo(); } bool mergeWith(const QUndoCommand* other) override { if (other->id() == id()) { m_redoVal = static_cast(other)->m_redoVal; return true; } return false; } int id() const override { return int(Id::ADSRVelToAttack); } }; void ADSRControls::velToAttackChanged(double val) { if (m_enableUpdate) { ADSRView* view = getEditor()->m_adsrView; g_MainWindow->pushUndoCommand(new ADSRVelToAttackUndoCommand(val, view->m_node)); view->update(); } } class ADSRKeyToDecayUndoCommand : public EditorUndoCommand { double m_redoVal, m_undoVal; bool m_undid = false; public: ADSRKeyToDecayUndoCommand(double redoVal, amuse::ObjToken node) : EditorUndoCommand(node.get(), ADSRControls::tr("Change Key To Decay")), m_redoVal(redoVal) {} void undo() override { m_undid = true; amuse::ITable& table = **m_node.cast()->m_obj; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { auto& adsr = static_cast(table); adsr._setKeyToDecay(m_undoVal); } EditorUndoCommand::undo(); } void redo() override { amuse::ITable& table = **m_node.cast()->m_obj; if (table.Isa() == amuse::ITable::Type::ADSRDLS) { auto& adsr = static_cast(table); m_undoVal = adsr._getKeyToDecay(); adsr._setKeyToDecay(m_redoVal); } if (m_undid) EditorUndoCommand::redo(); } bool mergeWith(const QUndoCommand* other) override { if (other->id() == id()) { m_redoVal = static_cast(other)->m_redoVal; return true; } return false; } int id() const override { return int(Id::ADSRKeyToDecay); } }; void ADSRControls::keyToDecayChanged(double val) { if (m_enableUpdate) { ADSRView* view = getEditor()->m_adsrView; g_MainWindow->pushUndoCommand(new ADSRKeyToDecayUndoCommand(val, view->m_node)); view->update(); } } ADSRControls::ADSRControls(QWidget* parent) : QFrame(parent) { setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); setFixedHeight(100); setFrameShape(QFrame::StyledPanel); setFrameShadow(QFrame::Sunken); setBackgroundRole(QPalette::Base); setAutoFillBackground(true); auto* const mainLayout = new QHBoxLayout; auto* const leftLayout = new QGridLayout; QPalette palette = QWidget::palette(); palette.setColor(QPalette::Base, palette.color(QPalette::Window)); palette.setColor(QPalette::Text, Red); QLabel* lab = new QLabel(tr("Attack")); lab->setPalette(palette); leftLayout->addWidget(lab, 0, 0); m_attack = new QDoubleSpinBox; m_attack->setDisabled(true); m_attack->setRange(0, 65.535); m_attack->setDecimals(3); m_attack->setSingleStep(0.1); m_attack->setSuffix(tr(" sec")); m_attack->setPalette(palette); connect(m_attack, qOverload(&QDoubleSpinBox::valueChanged), this, &ADSRControls::attackChanged); leftLayout->addWidget(m_attack, 1, 0); palette.setColor(QPalette::Text, Green); lab = new QLabel(tr("Decay")); lab->setPalette(palette); leftLayout->addWidget(lab, 0, 1); m_decay = new QDoubleSpinBox; m_decay->setDisabled(true); m_decay->setRange(0, 65.535); m_decay->setDecimals(3); m_decay->setSingleStep(0.1); m_decay->setSuffix(tr(" sec")); m_decay->setPalette(palette); connect(m_decay, qOverload(&QDoubleSpinBox::valueChanged), this, &ADSRControls::decayChanged); leftLayout->addWidget(m_decay, 1, 1); palette.setColor(QPalette::Text, Qt::white); leftLayout->addWidget(new QLabel(tr("Sustain")), 0, 2); m_sustain = new QDoubleSpinBox; m_sustain->setDisabled(true); m_sustain->setRange(0.0, 100.0); m_sustain->setDecimals(3); m_sustain->setSuffix(tr(" %")); m_sustain->setPalette(palette); connect(m_sustain, qOverload(&QDoubleSpinBox::valueChanged), this, &ADSRControls::sustainChanged); leftLayout->addWidget(m_sustain, 1, 2); palette.setColor(QPalette::Text, Blue); lab = new QLabel(tr("Release")); lab->setPalette(palette); leftLayout->addWidget(lab, 0, 3); m_release = new QDoubleSpinBox; m_release->setDisabled(true); m_release->setRange(0.0, 65.535); m_release->setDecimals(3); m_release->setSingleStep(0.1); m_release->setSuffix(tr(" sec")); m_release->setPalette(palette); connect(m_release, qOverload(&QDoubleSpinBox::valueChanged), this, &ADSRControls::releaseChanged); leftLayout->addWidget(m_release, 1, 3); palette.setColor(QPalette::Text, Qt::white); leftLayout->addWidget(new QLabel(tr("DLS")), 0, 4); m_dls = new QCheckBox; m_dls->setPalette(palette); m_dls->setDisabled(true); m_dls->setChecked(false); connect(m_dls, qOverload(&QCheckBox::stateChanged), this, &ADSRControls::dlsStateChanged); leftLayout->addWidget(m_dls, 1, 4); m_velToAttackLab = new QLabel(tr("Vel To Attack")); m_velToAttackLab->setVisible(false); leftLayout->addWidget(m_velToAttackLab, 0, 5); m_velToAttack = new QDoubleSpinBox; m_velToAttack->setVisible(false); m_velToAttack->setRange(0.0, 9999.999); m_velToAttack->setDecimals(3); m_velToAttack->setSingleStep(1.0); m_velToAttack->setPalette(palette); connect(m_velToAttack, qOverload(&QDoubleSpinBox::valueChanged), this, &ADSRControls::velToAttackChanged); leftLayout->addWidget(m_velToAttack, 1, 5); m_keyToDecayLab = new QLabel(tr("Key To Decay")); m_keyToDecayLab->setVisible(false); leftLayout->addWidget(m_keyToDecayLab, 0, 6); m_keyToDecay = new QDoubleSpinBox; m_keyToDecay->setVisible(false); m_keyToDecay->setRange(0.0, 9999.999); m_keyToDecay->setDecimals(3); m_keyToDecay->setSingleStep(1.0); m_keyToDecay->setPalette(palette); connect(m_keyToDecay, qOverload(&QDoubleSpinBox::valueChanged), this, &ADSRControls::keyToDecayChanged); leftLayout->addWidget(m_keyToDecay, 1, 6); leftLayout->setColumnMinimumWidth(0, 75); leftLayout->setColumnStretch(0, 1); leftLayout->setColumnMinimumWidth(1, 75); leftLayout->setColumnStretch(1, 1); leftLayout->setColumnMinimumWidth(2, 75); leftLayout->setColumnStretch(2, 1); leftLayout->setColumnMinimumWidth(3, 75); leftLayout->setColumnStretch(3, 1); leftLayout->setColumnMinimumWidth(4, 50); leftLayout->setColumnStretch(4, 0); leftLayout->setColumnMinimumWidth(5, 75); leftLayout->setColumnStretch(5, 1); leftLayout->setColumnMinimumWidth(6, 75); leftLayout->setColumnStretch(6, 1); leftLayout->setRowMinimumHeight(0, 22); leftLayout->setRowMinimumHeight(1, 37); leftLayout->setContentsMargins(10, 6, 0, 14); mainLayout->addLayout(leftLayout); mainLayout->addStretch(); setLayout(mainLayout); } ADSRControls::~ADSRControls() = default; bool ADSREditor::loadData(ProjectModel::ADSRNode* node) { m_adsrView->loadData(node); m_controls->loadData(); return true; } void ADSREditor::unloadData() { m_adsrView->unloadData(); m_controls->unloadData(); } ProjectModel::INode* ADSREditor::currentNode() const { return m_adsrView->currentNode(); } ADSREditor::ADSREditor(QWidget* parent) : EditorWidget(parent), m_adsrView(new ADSRView), m_controls(new ADSRControls) { auto* const layout = new QVBoxLayout; layout->setContentsMargins(QMargins()); layout->setSpacing(1); layout->addWidget(m_adsrView); layout->addWidget(m_controls); setLayout(layout); } ADSREditor::~ADSREditor() = default;