Finish CurveEditor implementation

This commit is contained in:
Jack Andersen 2018-08-04 12:05:01 -10:00
parent c2a242022a
commit 2abed18784
8 changed files with 449 additions and 40 deletions

View File

@ -30,6 +30,9 @@ ProjectModel::INode* ADSRView::currentNode() const
void ADSRView::paintEvent(QPaintEvent* ev)
{
if (!m_node)
return;
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
@ -232,17 +235,17 @@ 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, ctrls->m_decay->value() - delta);
ctrls->setAttackAndDecay(newAttack, std::max(0.0, ctrls->m_decay->value() - delta));
}
else if (m_dragPoint == 1)
{
qreal newDecay = (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);
ctrls->setDecayAndSustain(newDecay, newSustain * 100.0);
}
else if (m_dragPoint == 2)
{
qreal newRelease = (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->m_release->setValue(newRelease);
}
}
@ -961,6 +964,7 @@ ADSRControls::ADSRControls(QWidget* parent)
QGridLayout* leftLayout = new QGridLayout;
QPalette palette = QWidget::palette();
palette.setColor(QPalette::Base, palette.color(QPalette::Background));
palette.setColor(QPalette::Text, Red);
QLabel* lab = new QLabel(tr("Attack"));
@ -990,12 +994,14 @@ ADSRControls::ADSRControls(QWidget* parent)
connect(m_decay, SIGNAL(valueChanged(double)), this, SLOT(decayChanged(double)));
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, SIGNAL(valueChanged(double)), this, SLOT(sustainChanged(double)));
leftLayout->addWidget(m_sustain, 1, 2);
@ -1013,10 +1019,9 @@ ADSRControls::ADSRControls(QWidget* parent)
connect(m_release, SIGNAL(valueChanged(double)), this, SLOT(releaseChanged(double)));
leftLayout->addWidget(m_release, 1, 3);
palette.setColor(QPalette::Text, Qt::white);
leftLayout->addWidget(new QLabel(tr("DLS")), 0, 4);
m_dls = new QCheckBox;
palette = m_dls->palette();
palette.setColor(QPalette::Base, palette.color(QPalette::Background));
m_dls->setPalette(palette);
m_dls->setDisabled(true);
m_dls->setChecked(false);
@ -1031,6 +1036,7 @@ ADSRControls::ADSRControls(QWidget* parent)
m_velToAttack->setRange(0.0, 9999.999);
m_velToAttack->setDecimals(3);
m_velToAttack->setSingleStep(1.0);
m_velToAttack->setPalette(palette);
connect(m_velToAttack, SIGNAL(valueChanged(double)), this, SLOT(velToAttackChanged(double)));
leftLayout->addWidget(m_velToAttack, 1, 5);
@ -1042,6 +1048,7 @@ ADSRControls::ADSRControls(QWidget* parent)
m_keyToDecay->setRange(0.0, 9999.999);
m_keyToDecay->setDecimals(3);
m_keyToDecay->setSingleStep(1.0);
m_keyToDecay->setPalette(palette);
connect(m_keyToDecay, SIGNAL(valueChanged(double)), this, SLOT(keyToDecayChanged(double)));
leftLayout->addWidget(m_keyToDecay, 1, 6);

View File

@ -6,6 +6,7 @@ find_package(Qt5Widgets)
find_package(Qt5Network)
find_package(Qt5Xml)
find_package(Qt5Svg)
find_package(Qt5Script)
find_package(Qt5LinguistTools)
if(WIN32)
@ -80,4 +81,5 @@ target_link_libraries(amuse-gui ${PLAT_LIBS}
${Qt5Network_LIBRARIES}
${Qt5Xml_LIBRARIES}
${Qt5Svg_LIBRARIES}
${Qt5Script_LIBRARIES}
amuse boo ${BOO_SYS_LIBS} logvisor athena-core athena-libyaml xxhash ${ZLIB_LIBRARIES} ${LZO_LIB})

View File

@ -1,12 +1,327 @@
#include "CurveEditor.hpp"
#include "MainWindow.hpp"
#include <QHBoxLayout>
#include <QGridLayout>
#include <QPainter>
#include <QScriptValueIterator>
#include <QMouseEvent>
class CurveEditUndoCommand : public EditorUndoCommand
{
uint8_t m_redoData[128];
uint8_t m_undoData[128];
bool m_undid = false;
public:
CurveEditUndoCommand(const uint8_t* redoData, amuse::ObjToken<ProjectModel::CurveNode> node)
: EditorUndoCommand(node.get(), CurveControls::tr("Edit Curve"))
{
std::memcpy(m_redoData, redoData, 128);
}
void undo()
{
m_undid = true;
amuse::ITable& table = **m_node.cast<ProjectModel::CurveNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::Curve)
{
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
curve.data.resize(128);
std::memcpy(curve.data.data(), m_undoData, 128);
}
EditorUndoCommand::undo();
}
void redo()
{
amuse::ITable& table = **m_node.cast<ProjectModel::CurveNode>()->m_obj;
if (table.Isa() == amuse::ITable::Type::Curve)
{
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
curve.data.resize(128);
std::memcpy(m_undoData, curve.data.data(), 128);
std::memcpy(curve.data.data(), m_redoData, 128);
}
if (m_undid)
EditorUndoCommand::redo();
}
bool mergeWith(const QUndoCommand* other)
{
if (other->id() == id())
{
std::memcpy(m_redoData, static_cast<const CurveEditUndoCommand*>(other)->m_redoData, 128);
return true;
}
return false;
}
int id() const { return int(Id::CurveEdit); }
};
CurveEditor* CurveView::getEditor() const
{
return qobject_cast<CurveEditor*>(parentWidget());
}
void CurveView::loadData(ProjectModel::CurveNode* node)
{
m_node = node;
}
void CurveView::unloadData()
{
m_node.reset();
}
ProjectModel::INode* CurveView::currentNode() const
{
return m_node.get();
}
void CurveView::paintEvent(QPaintEvent* ev)
{
if (!m_node)
return;
amuse::ITable& table = **m_node->m_obj;
if (table.Isa() != amuse::ITable::Type::Curve)
return;
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
qreal deviceRatio = devicePixelRatioF();
qreal penWidth = std::max(std::floor(deviceRatio), 1.0) / deviceRatio;
painter.setPen(QPen(QColor(127, 127, 127), penWidth));
painter.setFont(m_gridFont);
qreal yIncrement = (height() - 16.0) / 10.0;
for (int i = 0; i < 11; ++i)
{
qreal thisY = i * yIncrement;
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));
}
qreal xIncrement = (width() - 30.0) / 10.0;
for (int i = 0; i < 11; ++i)
{
qreal thisX = i * xIncrement + 30.0;
qreal textX = thisX - (i == 10 ? 30.0 : 15.0);
painter.drawStaticText(QPointF(textX, height() - 16.0), m_percentTextsCenter[i]);
painter.drawLine(QPointF(thisX, 0.0), QPointF(thisX, height() - 16.0));
}
int i;
xIncrement = (width() - 30.0) / 127.0;
QPointF lastPt;
painter.setPen(QPen(Qt::white, penWidth * 3.0));
for (i = 0; i < curve.data.size(); ++i)
{
QPointF thisPt(i * xIncrement + 30.0, (height() - 16.0) - (height() - 16.0) * (curve.data[i] / 127.0));
if (i)
painter.drawLine(lastPt, thisPt);
lastPt = thisPt;
}
for (; i < 128; ++i)
{
QPointF thisPt(i * xIncrement + 30.0, height() - 16.0);
if (i)
painter.drawLine(lastPt, thisPt);
lastPt = thisPt;
}
}
void CurveView::mousePressEvent(QMouseEvent* ev)
{
mouseMoveEvent(ev);
}
void CurveView::mouseMoveEvent(QMouseEvent* ev)
{
CurveView* view = getEditor()->m_curveView;
amuse::ITable& table = **view->m_node->m_obj;
if (table.Isa() != amuse::ITable::Type::Curve)
return;
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
qreal xIncrement = (width() - 30.0) / 127.0;
int idx = int(std::round((ev->localPos().x() - 30.0) / xIncrement));
if (idx < 0 || idx > 127)
return;
int val = 127 - amuse::clamp(0, int(std::round(ev->localPos().y() / (height() - 16.0) * 127.0)), 127);
curve.data.resize(128);
uint8_t newData[128];
std::memcpy(newData, curve.data.data(), 128);
newData[idx] = uint8_t(val);
g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, m_node));
update();
}
CurveView::CurveView(QWidget* parent)
: QWidget(parent)
{
for (int i = 0; i < 11; ++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_percentTextsCenter[i].setText(QStringLiteral("%1%").arg(i * 10));
m_percentTextsCenter[i].setTextOption(QTextOption(Qt::AlignBaseline | Qt::AlignCenter));
m_percentTextsCenter[i].setTextWidth(28.0);
}
m_gridFont.setPointSize(8);
}
CurveEditor* CurveControls::getEditor() const
{
return qobject_cast<CurveEditor*>(parentWidget());
}
void CurveControls::loadData()
{
m_lineEdit->setDisabled(false);
m_setExpr->setDisabled(false);
}
void CurveControls::unloadData()
{
m_lineEdit->setDisabled(true);
m_setExpr->setDisabled(true);
}
void CurveControls::exprCommit()
{
CurveView* view = getEditor()->m_curveView;
amuse::ITable& table = **view->m_node->m_obj;
if (table.Isa() != amuse::ITable::Type::Curve)
return;
amuse::Curve& curve = static_cast<amuse::Curve&>(table);
QScriptSyntaxCheckResult res = QScriptEngine::checkSyntax(m_lineEdit->text());
if (res.state() != QScriptSyntaxCheckResult::Valid)
{
m_errLabel->setText(res.errorMessage());
return;
}
m_errLabel->setText(QString());
curve.data.resize(128);
uint8_t newData[128];
std::memcpy(newData, curve.data.data(), 128);
QScriptProgram prog(m_lineEdit->text());
bool notANumber = false;
for (int i = 0; i < 128; ++i)
{
m_engine.globalObject().setProperty(QStringLiteral("x"), i / 127.0);
QScriptValue val = m_engine.evaluate(prog);
if (val.isNumber())
{
newData[i] = uint8_t(amuse::clamp(0, int(std::round(val.toNumber() * 127.0)), 127));
}
else
{
notANumber = true;
newData[i] = 0;
}
}
if (notANumber)
m_errLabel->setText(tr("Did not evaluate as a number"));
g_MainWindow->pushUndoCommand(new CurveEditUndoCommand(newData, view->m_node));
view->update();
}
void CurveControls::resizeEvent(QResizeEvent* ev)
{
m_errLabel->setGeometry(22, 78, width() - 44, 14);
}
CurveControls::CurveControls(QWidget* parent)
: QFrame(parent)
{
setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
setFixedHeight(100);
setFrameShape(QFrame::StyledPanel);
setFrameShadow(QFrame::Sunken);
setBackgroundRole(QPalette::Base);
setAutoFillBackground(true);
QHBoxLayout* mainLayout = new QHBoxLayout;
QGridLayout* leftLayout = new QGridLayout;
leftLayout->addWidget(new QLabel(tr("Expression")), 0, 0);
m_lineEdit = new QLineEdit;
m_lineEdit->setDisabled(true);
QPalette palette = m_lineEdit->palette();
palette.setColor(QPalette::Base, palette.color(QPalette::Background));
m_lineEdit->setPalette(palette);
connect(m_lineEdit, SIGNAL(returnPressed()), this, SLOT(exprCommit()));
leftLayout->addWidget(m_lineEdit, 1, 0);
m_setExpr = new QPushButton(tr("Apply"));
m_setExpr->setDisabled(true);
connect(m_setExpr, SIGNAL(clicked(bool)), this, SLOT(exprCommit()));
leftLayout->addWidget(m_setExpr, 1, 1);
m_errLabel = new QLabel(this);
QFont font = m_errLabel->font();
font.setPointSize(8);
m_errLabel->setFont(font);
palette.setColor(QPalette::Text, Qt::red);
m_errLabel->setPalette(palette);
leftLayout->setColumnMinimumWidth(0, 500);
leftLayout->setColumnStretch(0, 1);
leftLayout->setColumnMinimumWidth(1, 75);
leftLayout->setColumnStretch(1, 0);
leftLayout->setRowMinimumHeight(0, 22);
leftLayout->setRowMinimumHeight(1, 37);
leftLayout->setContentsMargins(10, 6, 10, 14);
mainLayout->addLayout(leftLayout);
setLayout(mainLayout);
QScriptValueIterator it(m_engine.globalObject().property(QStringLiteral("Math")));
QString docStr = tr("Expression interpreter mapping x:[0,1] to y:[0,1] with the following constants and functions available:\n");
bool needsComma = false;
while (it.hasNext())
{
it.next();
m_engine.globalObject().setProperty(it.name(), it.value());
if (needsComma)
docStr += QStringLiteral(", ");
needsComma = true;
docStr += it.name();
}
m_lineEdit->setToolTip(docStr);
}
bool CurveEditor::loadData(ProjectModel::CurveNode* node)
{
return false;
m_curveView->loadData(node);
m_controls->loadData();
return true;
}
void CurveEditor::unloadData()
{
m_curveView->unloadData();
m_controls->unloadData();
}
ProjectModel::INode* CurveEditor::currentNode() const
{
return m_curveView->currentNode();
}
CurveEditor::CurveEditor(QWidget* parent)
: EditorWidget(parent)
: EditorWidget(parent), m_curveView(new CurveView), m_controls(new CurveControls)
{
QVBoxLayout* layout = new QVBoxLayout;
layout->setContentsMargins(QMargins());
layout->setSpacing(1);
layout->addWidget(m_curveView);
layout->addWidget(m_controls);
setLayout(layout);
}

View File

@ -2,14 +2,65 @@
#define AMUSE_CURVE_EDITOR_HPP
#include "EditorWidget.hpp"
#include <QFrame>
#include <QLabel>
#include <QStaticText>
#include <QLineEdit>
#include <QPushButton>
#include <QScriptEngine>
class CurveEditor;
class CurveView : public QWidget
{
Q_OBJECT
friend class CurveControls;
amuse::ObjToken<ProjectModel::CurveNode> m_node;
QFont m_gridFont;
QStaticText m_percentTexts[11];
QStaticText m_percentTextsCenter[11];
CurveEditor* getEditor() const;
public:
explicit CurveView(QWidget* parent = Q_NULLPTR);
void loadData(ProjectModel::CurveNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
void paintEvent(QPaintEvent* ev);
void mousePressEvent(QMouseEvent* ev);
void mouseMoveEvent(QMouseEvent* ev);
};
class CurveControls : public QFrame
{
Q_OBJECT
friend class CurveView;
QLineEdit* m_lineEdit;
QLabel* m_errLabel;
QPushButton* m_setExpr;
QScriptEngine m_engine;
CurveEditor* getEditor() const;
public:
explicit CurveControls(QWidget* parent = Q_NULLPTR);
void loadData();
void unloadData();
void resizeEvent(QResizeEvent* ev);
public slots:
void exprCommit();
};
class CurveEditor : public EditorWidget
{
Q_OBJECT
friend class CurveView;
friend class CurveControls;
CurveView* m_curveView;
CurveControls* m_controls;
public:
explicit CurveEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::CurveNode* node);
void unloadData();
ProjectModel::INode* currentNode() const;
};
#endif //AMUSE_CURVE_EDITOR_HPP

View File

@ -33,7 +33,8 @@ protected:
ADSRRelease,
ADSRDLS,
ADSRVelToAttack,
ADSRKeyToDecay
ADSRKeyToDecay,
CurveEdit
};
public:
EditorUndoCommand(amuse::ObjToken<ProjectModel::INode> node,

View File

@ -764,6 +764,9 @@ SampleControls::SampleControls(QWidget* parent)
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;
@ -777,8 +780,6 @@ SampleControls::SampleControls(QWidget* parent)
leftLayout->addWidget(new QLabel(tr("Loop")), 0, 1);
m_loopCheck = new QCheckBox;
QPalette palette = m_loopCheck->palette();
palette.setColor(QPalette::Base, palette.color(QPalette::Background));
m_loopCheck->setPalette(palette);
m_loopCheck->setDisabled(true);
connect(m_loopCheck, SIGNAL(stateChanged(int)), this, SLOT(loopStateChanged(int)));
@ -786,18 +787,21 @@ SampleControls::SampleControls(QWidget* parent)
leftLayout->addWidget(new QLabel(tr("Start")), 0, 2);
m_loopStart = new QSpinBox;
m_loopStart->setPalette(palette);
m_loopStart->setDisabled(true);
connect(m_loopStart, SIGNAL(valueChanged(int)), this, SLOT(startValueChanged(int)));
leftLayout->addWidget(m_loopStart, 1, 2);
leftLayout->addWidget(new QLabel(tr("End")), 0, 3);
m_loopEnd = new QSpinBox;
m_loopEnd->setPalette(palette);
m_loopEnd->setDisabled(true);
connect(m_loopEnd, SIGNAL(valueChanged(int)), this, SLOT(endValueChanged(int)));
leftLayout->addWidget(m_loopEnd, 1, 3);
leftLayout->addWidget(new QLabel(tr("Base Pitch")), 0, 4);
m_basePitch = new QSpinBox;
m_basePitch->setPalette(palette);
m_basePitch->setDisabled(true);
m_basePitch->setMinimum(0);
m_basePitch->setMaximum(127);

View File

@ -4,94 +4,94 @@
<context>
<name>ADSRControls</name>
<message>
<location filename="../ADSREditor.cpp" line="332"/>
<location filename="../ADSREditor.cpp" line="335"/>
<source>Change Attack</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="396"/>
<location filename="../ADSREditor.cpp" line="399"/>
<source>Change Decay</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="460"/>
<location filename="../ADSREditor.cpp" line="463"/>
<source>Change Sustain</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="525"/>
<location filename="../ADSREditor.cpp" line="528"/>
<source>Change Attack/Decay</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="599"/>
<location filename="../ADSREditor.cpp" line="602"/>
<source>Change Decay/Sustain</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="672"/>
<location filename="../ADSREditor.cpp" line="675"/>
<source>Change Release</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="762"/>
<location filename="../ADSREditor.cpp" line="765"/>
<source>Change DLS</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="849"/>
<location filename="../ADSREditor.cpp" line="852"/>
<source>Change Vel To Attack</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="902"/>
<location filename="../ADSREditor.cpp" line="905"/>
<source>Change Key To Decay</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="966"/>
<location filename="../ADSREditor.cpp" line="970"/>
<source>Attack</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="974"/>
<location filename="../ADSREditor.cpp" line="988"/>
<location filename="../ADSREditor.cpp" line="1011"/>
<location filename="../ADSREditor.cpp" line="978"/>
<location filename="../ADSREditor.cpp" line="992"/>
<location filename="../ADSREditor.cpp" line="1017"/>
<source> sec</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="980"/>
<location filename="../ADSREditor.cpp" line="984"/>
<source>Decay</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="993"/>
<location filename="../ADSREditor.cpp" line="998"/>
<source>Sustain</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="998"/>
<location filename="../ADSREditor.cpp" line="1003"/>
<source> %</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="1003"/>
<location filename="../ADSREditor.cpp" line="1009"/>
<source>Release</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="1016"/>
<location filename="../ADSREditor.cpp" line="1023"/>
<source>DLS</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="1026"/>
<location filename="../ADSREditor.cpp" line="1031"/>
<source>Vel To Attack</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../ADSREditor.cpp" line="1037"/>
<location filename="../ADSREditor.cpp" line="1043"/>
<source>Key To Decay</source>
<translation type="unfinished"></translation>
</message>
@ -104,6 +104,35 @@
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>CurveControls</name>
<message>
<location filename="../CurveEditor.cpp" line="16"/>
<source>Edit Curve</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../CurveEditor.cpp" line="228"/>
<source>Did not evaluate as a number</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../CurveEditor.cpp" line="253"/>
<source>Expression</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../CurveEditor.cpp" line="262"/>
<source>Apply</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../CurveEditor.cpp" line="286"/>
<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>MainWindow</name>
<message>
@ -554,7 +583,7 @@
<message>
<location filename="../SampleEditor.cpp" line="507"/>
<location filename="../SampleEditor.cpp" line="522"/>
<location filename="../SampleEditor.cpp" line="778"/>
<location filename="../SampleEditor.cpp" line="781"/>
<source>Loop</source>
<translation type="unfinished"></translation>
</message>
@ -590,27 +619,27 @@
</message>
<message>
<location filename="../SampleEditor.cpp" line="752"/>
<location filename="../SampleEditor.cpp" line="818"/>
<location filename="../SampleEditor.cpp" line="822"/>
<source>Nothing Loaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SampleEditor.cpp" line="771"/>
<location filename="../SampleEditor.cpp" line="774"/>
<source>Zoom</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SampleEditor.cpp" line="787"/>
<location filename="../SampleEditor.cpp" line="788"/>
<source>Start</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SampleEditor.cpp" line="793"/>
<location filename="../SampleEditor.cpp" line="795"/>
<source>End</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../SampleEditor.cpp" line="799"/>
<location filename="../SampleEditor.cpp" line="802"/>
<source>Base Pitch</source>
<translation type="unfinished"></translation>
</message>

View File

@ -715,9 +715,9 @@ bool SoundMacro::CmdScaleVolume::Do(SoundMacroState& st, Voice& vox) const
if (table.id != 0)
{
const Curve* curveData = vox.getAudioGroup().getPool().tableAsCurves(table.id);
if (curveData)
if (curveData && curveData->data.size() >= 128)
{
vox.m_curVol = curveData->data.at(eval) / 127.f;
vox.m_curVol = curveData->data[eval] / 127.f;
return false;
}
}