mirror of https://github.com/AxioDL/amuse.git
Initial SampleEditor
This commit is contained in:
parent
16745c9bf8
commit
8d24e19989
|
@ -19,13 +19,13 @@ public:
|
|||
class EditorUndoCommand : public QUndoCommand
|
||||
{
|
||||
protected:
|
||||
std::shared_ptr<ProjectModel::INode> m_node;
|
||||
amuse::ObjToken<ProjectModel::INode> m_node;
|
||||
enum class Id
|
||||
{
|
||||
SMChangeVal,
|
||||
};
|
||||
public:
|
||||
EditorUndoCommand(std::shared_ptr<ProjectModel::INode> node,
|
||||
EditorUndoCommand(amuse::ObjToken<ProjectModel::INode> node,
|
||||
const QString& text, QUndoCommand* parent = nullptr)
|
||||
: QUndoCommand(text, parent), m_node(node) {}
|
||||
void undo();
|
||||
|
|
|
@ -10,7 +10,7 @@ void MIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
|
|||
if (keySearch == m_chanVoxs.cend())
|
||||
return;
|
||||
|
||||
if (m_lastVoice->isDestroyed() || keySearch->second == m_lastVoice)
|
||||
if ((m_lastVoice && m_lastVoice->isDestroyed()) || keySearch->second == m_lastVoice)
|
||||
m_lastVoice.reset();
|
||||
keySearch->second->keyOff();
|
||||
m_keyoffVoxs.emplace(std::move(keySearch->second));
|
||||
|
@ -19,7 +19,7 @@ void MIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
|
|||
|
||||
void MIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity)
|
||||
{
|
||||
if (m_lastVoice->isDestroyed())
|
||||
if (m_lastVoice && m_lastVoice->isDestroyed())
|
||||
m_lastVoice.reset();
|
||||
|
||||
/* If portamento is enabled for voice, pre-empt spawning new voices */
|
||||
|
@ -76,6 +76,7 @@ void MIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value)
|
|||
for (auto& v : m_engine.getActiveVoices())
|
||||
v->setCtrlValue(control, value);
|
||||
}
|
||||
g_MainWindow->m_ctrlVals[control] = value;
|
||||
}
|
||||
|
||||
void MIDIReader::programChange(uint8_t chan, uint8_t program) {}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef AMUSE_MIDI_READER_HPP
|
||||
#define AMUSE_MIDI_READER_HPP
|
||||
|
||||
#include "amuse/Voice.hpp"
|
||||
#include "amuse/BooBackend.hpp"
|
||||
#include "amuse/Common.hpp"
|
||||
#include <unordered_set>
|
||||
|
|
|
@ -410,6 +410,7 @@ bool MainWindow::_setEditor(EditorWidget* editor)
|
|||
if (!editor || !editor->valid())
|
||||
{
|
||||
m_ui.editorContents->setCurrentWidget(m_faceSvg);
|
||||
updateWindowTitle();
|
||||
return false;
|
||||
}
|
||||
m_ui.editorContents->setCurrentWidget(editor);
|
||||
|
@ -540,6 +541,9 @@ bool MainWindow::openProject(const QString& path)
|
|||
QFileInfo(dir, QStringLiteral("!pool.yaml")).exists())
|
||||
dir.cdUp();
|
||||
|
||||
if (m_projectModel && m_projectModel->path() == dir.path())
|
||||
return true;
|
||||
|
||||
if (!setProjectPath(dir.path()))
|
||||
return false;
|
||||
|
||||
|
@ -612,6 +616,8 @@ void MainWindow::revertAction()
|
|||
|
||||
void MainWindow::reloadSampleDataAction()
|
||||
{
|
||||
closeEditor();
|
||||
|
||||
ProjectModel* model = m_projectModel;
|
||||
if (!m_projectModel)
|
||||
return;
|
||||
|
|
|
@ -54,7 +54,8 @@ QVariant NullItemProxyModel::data(const QModelIndex& proxyIndex, int role) const
|
|||
|
||||
ProjectModel::INode::INode(INode* parent, int row) : m_parent(parent), m_row(row)
|
||||
{
|
||||
m_nullChild = std::make_unique<NullNode>(this);
|
||||
auto nullNode = amuse::MakeObj<NullNode>(this);
|
||||
m_nullChild = nullNode.get();
|
||||
}
|
||||
|
||||
ProjectModel::CollectionNode* ProjectModel::GroupNode::getCollectionOfType(Type tp) const
|
||||
|
@ -96,7 +97,7 @@ ProjectModel::BasePoolObjectNode* ProjectModel::CollectionNode::nodeOfIndex(int
|
|||
ProjectModel::ProjectModel(const QString& path, QObject* parent)
|
||||
: QAbstractItemModel(parent), m_dir(path), m_nullProxy(this)
|
||||
{
|
||||
m_root = std::make_shared<RootNode>();
|
||||
m_root = amuse::MakeObj<RootNode>();
|
||||
|
||||
GroupNode::Icon = QIcon(":/icons/IconGroup.svg");
|
||||
SongGroupNode::Icon = QIcon(":/icons/IconSongGroup.svg");
|
||||
|
@ -202,7 +203,7 @@ void ProjectModel::_resetModelData()
|
|||
{
|
||||
beginResetModel();
|
||||
m_projectDatabase.setIdDatabases();
|
||||
m_root = std::make_shared<RootNode>();
|
||||
m_root = amuse::MakeObj<RootNode>();
|
||||
m_root->reserve(m_groups.size());
|
||||
for (auto it = m_groups.begin() ; it != m_groups.end() ; ++it)
|
||||
{
|
||||
|
@ -427,7 +428,7 @@ bool ProjectModel::canEdit(const QModelIndex& index) const
|
|||
class DeleteNodeUndoCommand : public QUndoCommand
|
||||
{
|
||||
QModelIndex m_deleteIdx;
|
||||
std::shared_ptr<ProjectModel::INode> m_node;
|
||||
amuse::ObjToken<ProjectModel::INode> m_node;
|
||||
public:
|
||||
DeleteNodeUndoCommand(const QModelIndex& index)
|
||||
: QUndoCommand(QUndoStack::tr("Delete %1").arg(index.data().toString())), m_deleteIdx(index) {}
|
||||
|
@ -442,14 +443,14 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
void ProjectModel::_undoDel(const QModelIndex& index, std::shared_ptr<ProjectModel::INode>&& n)
|
||||
void ProjectModel::_undoDel(const QModelIndex& index, amuse::ObjToken<ProjectModel::INode> n)
|
||||
{
|
||||
beginInsertRows(index.parent(), index.row(), index.row());
|
||||
node(index.parent())->insertChild(index.row(), std::move(n));
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
std::shared_ptr<ProjectModel::INode> ProjectModel::_redoDel(const QModelIndex& index)
|
||||
amuse::ObjToken<ProjectModel::INode> ProjectModel::_redoDel(const QModelIndex& index)
|
||||
{
|
||||
node(index)->depthTraverse([](INode* node)
|
||||
{
|
||||
|
@ -457,7 +458,7 @@ std::shared_ptr<ProjectModel::INode> ProjectModel::_redoDel(const QModelIndex& i
|
|||
return true;
|
||||
});
|
||||
beginRemoveRows(index.parent(), index.row(), index.row());
|
||||
std::shared_ptr<ProjectModel::INode> ret = node(index.parent())->removeChild(index.row());
|
||||
amuse::ObjToken<ProjectModel::INode> ret = node(index.parent())->removeChild(index.row());
|
||||
endRemoveRows();
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ private:
|
|||
std::map<QString, amuse::AudioGroupDatabase> m_groups;
|
||||
|
||||
public:
|
||||
class INode : public std::enable_shared_from_this<INode>
|
||||
class INode : public amuse::IObj
|
||||
{
|
||||
public:
|
||||
enum class Type
|
||||
|
@ -66,8 +66,8 @@ public:
|
|||
};
|
||||
protected:
|
||||
INode* m_parent;
|
||||
std::vector<std::shared_ptr<INode>> m_children;
|
||||
std::unique_ptr<INode> m_nullChild;
|
||||
std::vector<amuse::IObjToken<INode>> m_children;
|
||||
amuse::IObjToken<INode> m_nullChild;
|
||||
int m_row;
|
||||
public:
|
||||
virtual ~INode() = default;
|
||||
|
@ -95,14 +95,14 @@ public:
|
|||
m_nullChild->m_row = row;
|
||||
}
|
||||
|
||||
void insertChild(int row, std::shared_ptr<INode>&& n)
|
||||
void insertChild(int row, amuse::ObjToken<INode> n)
|
||||
{
|
||||
m_children.insert(m_children.begin() + row, std::move(n));
|
||||
m_children.insert(m_children.begin() + row, n.get());
|
||||
reindexRows(row);
|
||||
}
|
||||
std::shared_ptr<INode> removeChild(int row)
|
||||
amuse::ObjToken<INode> removeChild(int row)
|
||||
{
|
||||
std::shared_ptr<INode> ret = std::move(m_children[row]);
|
||||
amuse::ObjToken<INode> ret = m_children[row].get();
|
||||
m_children.erase(m_children.begin() + row);
|
||||
reindexRows(row);
|
||||
return ret;
|
||||
|
@ -112,7 +112,8 @@ public:
|
|||
template<class T, class... _Args>
|
||||
T& makeChild(_Args&&... args)
|
||||
{
|
||||
m_children.push_back(std::make_shared<T>(this, m_children.size(), std::forward<_Args>(args)...));
|
||||
auto tok = amuse::MakeObj<T>(this, m_children.size(), std::forward<_Args>(args)...);
|
||||
m_children.push_back(tok.get());
|
||||
m_nullChild->m_row = int(m_children.size());
|
||||
return static_cast<T&>(*m_children.back());
|
||||
}
|
||||
|
@ -161,9 +162,6 @@ public:
|
|||
|
||||
CollectionNode* getCollectionOfType(Type tp) const;
|
||||
amuse::AudioGroupDatabase* getAudioGroup() const { return &m_it->second; }
|
||||
|
||||
std::shared_ptr<GroupNode> shared_from_this()
|
||||
{ return std::static_pointer_cast<GroupNode>(INode::shared_from_this()); }
|
||||
};
|
||||
struct SongGroupNode : INode
|
||||
{
|
||||
|
@ -177,9 +175,6 @@ public:
|
|||
Type type() const { return Type::SongGroup; }
|
||||
QString text() const { return m_name; }
|
||||
QIcon icon() const { return Icon; }
|
||||
|
||||
std::shared_ptr<SongGroupNode> shared_from_this()
|
||||
{ return std::static_pointer_cast<SongGroupNode>(INode::shared_from_this()); }
|
||||
};
|
||||
struct SoundGroupNode : INode
|
||||
{
|
||||
|
@ -193,9 +188,6 @@ public:
|
|||
Type type() const { return Type::SoundGroup; }
|
||||
QString text() const { return m_name; }
|
||||
QIcon icon() const { return Icon; }
|
||||
|
||||
std::shared_ptr<SoundGroupNode> shared_from_this()
|
||||
{ return std::static_pointer_cast<SoundGroupNode>(INode::shared_from_this()); }
|
||||
};
|
||||
struct BasePoolObjectNode;
|
||||
struct CollectionNode : INode
|
||||
|
@ -215,9 +207,6 @@ public:
|
|||
int indexOfId(amuse::ObjectId id) const;
|
||||
amuse::ObjectId idOfIndex(int idx) const;
|
||||
BasePoolObjectNode* nodeOfIndex(int idx) const;
|
||||
|
||||
std::shared_ptr<CollectionNode> shared_from_this()
|
||||
{ return std::static_pointer_cast<CollectionNode>(INode::shared_from_this()); }
|
||||
};
|
||||
struct BasePoolObjectNode : INode
|
||||
{
|
||||
|
@ -237,9 +226,6 @@ public:
|
|||
: BasePoolObjectNode(parent, row, id, ID::CurNameDB->resolveNameFromId(id).data()), m_obj(obj) {}
|
||||
|
||||
Type type() const { return TP; }
|
||||
|
||||
std::shared_ptr<PoolObjectNode<ID, T, TP>> shared_from_this()
|
||||
{ return std::static_pointer_cast<PoolObjectNode<ID, T, TP>>(INode::shared_from_this()); }
|
||||
};
|
||||
using SoundMacroNode = PoolObjectNode<amuse::SoundMacroId, amuse::SoundMacro, INode::Type::SoundMacro>;
|
||||
using ADSRNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::ADSR>;
|
||||
|
@ -248,7 +234,7 @@ public:
|
|||
using LayersNode = PoolObjectNode<amuse::LayersId, std::vector<amuse::LayerMapping>, INode::Type::Layer>;
|
||||
using SampleNode = PoolObjectNode<amuse::SampleId, amuse::SampleEntry, INode::Type::Sample>;
|
||||
|
||||
std::shared_ptr<RootNode> m_root;
|
||||
amuse::ObjToken<RootNode> m_root;
|
||||
|
||||
bool m_needsReset = false;
|
||||
void _resetModelData();
|
||||
|
@ -276,8 +262,8 @@ public:
|
|||
INode* node(const QModelIndex& index) const;
|
||||
GroupNode* getGroupNode(INode* node) const;
|
||||
bool canEdit(const QModelIndex& index) const;
|
||||
void _undoDel(const QModelIndex& index, std::shared_ptr<ProjectModel::INode>&& node);
|
||||
std::shared_ptr<ProjectModel::INode> _redoDel(const QModelIndex& index);
|
||||
void _undoDel(const QModelIndex& index, amuse::ObjToken<ProjectModel::INode> node);
|
||||
amuse::ObjToken<ProjectModel::INode> _redoDel(const QModelIndex& index);
|
||||
void del(const QModelIndex& index);
|
||||
|
||||
QString path() const { return m_dir.path(); }
|
||||
|
|
|
@ -1,12 +1,213 @@
|
|||
#include "SampleEditor.hpp"
|
||||
#include "MainWindow.hpp"
|
||||
#include "amuse/DSPCodec.hpp"
|
||||
#include <QPainter>
|
||||
#include <QPaintEvent>
|
||||
|
||||
void SampleView::seekToSample(qreal sample)
|
||||
{
|
||||
sample = std::min(sample, qreal(m_sample->getNumSamples()));
|
||||
|
||||
if (m_sample->isFormatDSP())
|
||||
{
|
||||
if (sample < m_curSamplePos)
|
||||
{
|
||||
m_prev1 = m_prev2 = 0;
|
||||
m_curSamplePos = 0.0;
|
||||
}
|
||||
|
||||
uint32_t startBlock = uint32_t(m_curSamplePos) / 14;
|
||||
uint32_t startRem = uint32_t(m_curSamplePos) % 14;
|
||||
uint32_t endBlock = uint32_t(sample) / 14;
|
||||
uint32_t endRem = uint32_t(sample) % 14;
|
||||
|
||||
if (startRem)
|
||||
DSPDecompressFrameRangedStateOnly(m_sampleData + 8 * startBlock, m_sample->m_ADPCMParms.dsp.m_coefs,
|
||||
&m_prev1, &m_prev2, startRem, startBlock == endBlock ? endRem : 14);
|
||||
|
||||
for (uint32_t b = startBlock; b < endBlock; ++b)
|
||||
DSPDecompressFrameStateOnly(m_sampleData + 8 * b, m_sample->m_ADPCMParms.dsp.m_coefs,
|
||||
&m_prev1, &m_prev2, 14);
|
||||
|
||||
if (endRem)
|
||||
DSPDecompressFrameStateOnly(m_sampleData + 8 * endBlock, m_sample->m_ADPCMParms.dsp.m_coefs,
|
||||
&m_prev1, &m_prev2, endRem);
|
||||
}
|
||||
|
||||
m_curSamplePos = sample;
|
||||
}
|
||||
|
||||
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> SampleView::iterateSampleInterval(qreal interval)
|
||||
{
|
||||
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> ret = {{0.0, 1.0}, {0.0, -1.0}}; // min,max avg,peak
|
||||
qreal avg = 0.0;
|
||||
qreal div = 0.0;
|
||||
qreal endSample = std::min(m_curSamplePos + interval, qreal(m_sample->getNumSamples()));
|
||||
|
||||
auto accumulate = [&ret, &avg, &div](int16_t sample)
|
||||
{
|
||||
qreal sampleF = sample / 32768.0;
|
||||
avg += sampleF;
|
||||
div += 1.0;
|
||||
ret.first.second = std::min(ret.first.second, sampleF);
|
||||
ret.second.second = std::max(ret.second.second, sampleF);
|
||||
};
|
||||
|
||||
if (m_sample->isFormatDSP())
|
||||
{
|
||||
uint32_t startBlock = uint32_t(m_curSamplePos) / 14;
|
||||
uint32_t startRem = uint32_t(m_curSamplePos) % 14;
|
||||
uint32_t endBlock = uint32_t(endSample) / 14;
|
||||
uint32_t endRem = uint32_t(endSample) % 14;
|
||||
|
||||
int16_t sampleBlock[14];
|
||||
|
||||
if (startRem)
|
||||
{
|
||||
uint32_t end = startBlock == endBlock ? endRem : 14;
|
||||
DSPDecompressFrameRanged(sampleBlock, m_sampleData + 8 * startBlock, m_sample->m_ADPCMParms.dsp.m_coefs,
|
||||
&m_prev1, &m_prev2, startRem, end);
|
||||
for (int s = 0; s < end - startRem; ++s)
|
||||
accumulate(sampleBlock[s]);
|
||||
}
|
||||
|
||||
for (uint32_t b = startBlock; b < endBlock; ++b)
|
||||
{
|
||||
DSPDecompressFrame(sampleBlock, m_sampleData + 8 * b, m_sample->m_ADPCMParms.dsp.m_coefs,
|
||||
&m_prev1, &m_prev2, 14);
|
||||
for (int s = 0; s < 14; ++s)
|
||||
accumulate(sampleBlock[s]);
|
||||
}
|
||||
|
||||
if (endRem)
|
||||
{
|
||||
DSPDecompressFrame(sampleBlock, m_sampleData + 8 * endBlock, m_sample->m_ADPCMParms.dsp.m_coefs,
|
||||
&m_prev1, &m_prev2, endRem);
|
||||
for (int s = 0; s < endRem; ++s)
|
||||
accumulate(sampleBlock[s]);
|
||||
}
|
||||
}
|
||||
else if (m_sample->getSampleFormat() == amuse::SampleFormat::PCM_PC)
|
||||
{
|
||||
for (uint32_t s = uint32_t(m_curSamplePos); s < uint32_t(endSample); ++s)
|
||||
accumulate(reinterpret_cast<const int16_t*>(m_sampleData)[s]);
|
||||
}
|
||||
|
||||
m_curSamplePos = endSample;
|
||||
|
||||
if (div == 0.0)
|
||||
return {};
|
||||
|
||||
avg /= div;
|
||||
|
||||
if (avg > 0.0)
|
||||
{
|
||||
ret.first.first = ret.first.second;
|
||||
ret.second.first = avg;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret.first.first = avg;
|
||||
ret.second.first = ret.second.second;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void SampleView::paintEvent(QPaintEvent* ev)
|
||||
{
|
||||
QPainter painter(this);
|
||||
if (m_sample)
|
||||
{
|
||||
m_samplesPerPx = m_sample->getNumSamples() / (width() * devicePixelRatioF());
|
||||
|
||||
qreal rectStart = ev->rect().x();
|
||||
qreal startSample = rectStart * m_samplesPerPx;
|
||||
qreal deviceWidth = ev->rect().width() * devicePixelRatioF();
|
||||
qreal increment = 1.0 / devicePixelRatioF();
|
||||
qreal deviceSamplesPerPx = m_samplesPerPx / devicePixelRatioF();
|
||||
|
||||
QPen peakPen(QColor(255, 147, 41), increment);
|
||||
QPen avgPen(QColor(254, 177, 68), increment);
|
||||
|
||||
qreal scale = -height() / 2.0;
|
||||
qreal trans = height() / 2.0;
|
||||
|
||||
seekToSample(startSample);
|
||||
|
||||
for (qreal i = 0.0; i < deviceWidth; i += increment)
|
||||
{
|
||||
if (m_curSamplePos + deviceSamplesPerPx > m_sample->getNumSamples())
|
||||
break;
|
||||
auto avgPeak = iterateSampleInterval(deviceSamplesPerPx);
|
||||
painter.setPen(peakPen);
|
||||
painter.drawLine(QPointF(rectStart + i, avgPeak.first.second * scale + trans),
|
||||
QPointF(rectStart + i, avgPeak.second.second * scale + trans));
|
||||
painter.setPen(avgPen);
|
||||
painter.drawLine(QPointF(rectStart + i, avgPeak.first.first * scale + trans),
|
||||
QPointF(rectStart + i, avgPeak.second.first * scale + trans));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SampleView::mousePressEvent(QMouseEvent* ev)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SampleView::mouseReleaseEvent(QMouseEvent* ev)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SampleView::mouseMoveEvent(QMouseEvent* ev)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SampleView::loadData(ProjectModel::SampleNode* node)
|
||||
{
|
||||
m_node = node;
|
||||
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(m_node.get());
|
||||
std::tie(m_sample, m_sampleData) = group->getAudioGroup()->getSampleData(m_node->id(), m_node->m_obj.get());
|
||||
update();
|
||||
}
|
||||
|
||||
void SampleView::unloadData()
|
||||
{
|
||||
m_sample.reset();
|
||||
update();
|
||||
}
|
||||
|
||||
ProjectModel::INode* SampleView::currentNode() const
|
||||
{
|
||||
return m_node.get();
|
||||
}
|
||||
|
||||
SampleView::SampleView(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
{}
|
||||
|
||||
bool SampleEditor::loadData(ProjectModel::SampleNode* node)
|
||||
{
|
||||
return false;
|
||||
m_sampleView->loadData(node);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SampleEditor::unloadData()
|
||||
{
|
||||
m_sampleView->unloadData();
|
||||
}
|
||||
|
||||
ProjectModel::INode* SampleEditor::currentNode() const
|
||||
{
|
||||
return m_sampleView->currentNode();
|
||||
}
|
||||
|
||||
SampleEditor::SampleEditor(QWidget* parent)
|
||||
: EditorWidget(parent)
|
||||
: EditorWidget(parent), m_sampleView(new SampleView)
|
||||
{
|
||||
|
||||
QVBoxLayout* layout = new QVBoxLayout;
|
||||
layout->addWidget(m_sampleView);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
|
|
@ -2,13 +2,41 @@
|
|||
#define AMUSE_SAMPLE_EDITOR_HPP
|
||||
|
||||
#include "EditorWidget.hpp"
|
||||
#include "ProjectModel.hpp"
|
||||
|
||||
class SampleView : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
qreal m_samplesPerPx = 100.0;
|
||||
amuse::ObjToken<ProjectModel::SampleNode> m_node;
|
||||
amuse::ObjToken<amuse::SampleEntryData> m_sample;
|
||||
const unsigned char* m_sampleData = nullptr;
|
||||
qreal m_curSamplePos = 0.0;
|
||||
int16_t m_prev1 = 0;
|
||||
int16_t m_prev2 = 0;
|
||||
void seekToSample(qreal sample);
|
||||
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> iterateSampleInterval(qreal interval);
|
||||
public:
|
||||
explicit SampleView(QWidget* parent = Q_NULLPTR);
|
||||
void loadData(ProjectModel::SampleNode* node);
|
||||
void unloadData();
|
||||
ProjectModel::INode* currentNode() const;
|
||||
|
||||
void paintEvent(QPaintEvent* ev);
|
||||
void mousePressEvent(QMouseEvent* ev);
|
||||
void mouseReleaseEvent(QMouseEvent* ev);
|
||||
void mouseMoveEvent(QMouseEvent* ev);
|
||||
};
|
||||
|
||||
class SampleEditor : public EditorWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
SampleView* m_sampleView;
|
||||
public:
|
||||
explicit SampleEditor(QWidget* parent = Q_NULLPTR);
|
||||
bool loadData(ProjectModel::SampleNode* node);
|
||||
void unloadData();
|
||||
ProjectModel::INode* currentNode() const;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -321,8 +321,8 @@ class ValChangedUndoCommand : public EditorUndoCommand
|
|||
public:
|
||||
ValChangedUndoCommand(amuse::SoundMacro::ICmd* cmd, const QString& fieldName,
|
||||
const amuse::SoundMacro::CmdIntrospection::Field& field,
|
||||
int redoVal, std::shared_ptr<ProjectModel::SoundMacroNode> node)
|
||||
: EditorUndoCommand(node, QUndoStack::tr("Change %1").arg(fieldName)),
|
||||
int redoVal, amuse::ObjToken<ProjectModel::SoundMacroNode> node)
|
||||
: EditorUndoCommand(node.get(), QUndoStack::tr("Change %1").arg(fieldName)),
|
||||
m_cmd(cmd), m_field(field), m_redoVal(redoVal) {}
|
||||
void undo()
|
||||
{
|
||||
|
@ -434,6 +434,8 @@ void CommandWidget::boolChanged(int state)
|
|||
|
||||
void CommandWidget::numChanged(int value)
|
||||
{
|
||||
if (value < 0)
|
||||
return;
|
||||
if (m_introspection)
|
||||
{
|
||||
const amuse::SoundMacro::CmdIntrospection::Field& field =
|
||||
|
@ -445,6 +447,8 @@ void CommandWidget::numChanged(int value)
|
|||
|
||||
void CommandWidget::nodeChanged(int value)
|
||||
{
|
||||
if (value < 0)
|
||||
return;
|
||||
if (m_introspection)
|
||||
{
|
||||
FieldProjectNode* fieldW = static_cast<FieldProjectNode*>(sender());
|
||||
|
@ -643,8 +647,8 @@ class ReorderCommandsUndoCommand : public EditorUndoCommand
|
|||
int m_a, m_b;
|
||||
bool m_undid = false;
|
||||
public:
|
||||
ReorderCommandsUndoCommand(int a, int b, const QString& text, std::shared_ptr<ProjectModel::SoundMacroNode> node)
|
||||
: EditorUndoCommand(node, QUndoStack::tr("Reorder %1").arg(text)), m_a(a), m_b(b) {}
|
||||
ReorderCommandsUndoCommand(int a, int b, const QString& text, amuse::ObjToken<ProjectModel::SoundMacroNode> node)
|
||||
: EditorUndoCommand(node.get(), QUndoStack::tr("Reorder %1").arg(text)), m_a(a), m_b(b) {}
|
||||
void undo()
|
||||
{
|
||||
m_undid = true;
|
||||
|
@ -777,8 +781,8 @@ class InsertCommandUndoCommand : public EditorUndoCommand
|
|||
int m_insertIdx;
|
||||
std::unique_ptr<amuse::SoundMacro::ICmd> m_cmd;
|
||||
public:
|
||||
InsertCommandUndoCommand(int insertIdx, const QString& text, std::shared_ptr<ProjectModel::SoundMacroNode> node)
|
||||
: EditorUndoCommand(node, QUndoStack::tr("Insert %1").arg(text)), m_insertIdx(insertIdx) {}
|
||||
InsertCommandUndoCommand(int insertIdx, const QString& text, amuse::ObjToken<ProjectModel::SoundMacroNode> node)
|
||||
: EditorUndoCommand(node.get(), QUndoStack::tr("Insert %1").arg(text)), m_insertIdx(insertIdx) {}
|
||||
void undo()
|
||||
{
|
||||
m_cmd = static_cast<ProjectModel::SoundMacroNode*>(m_node.get())->
|
||||
|
@ -832,8 +836,8 @@ class DeleteCommandUndoCommand : public EditorUndoCommand
|
|||
std::unique_ptr<amuse::SoundMacro::ICmd> m_cmd;
|
||||
bool m_undid = false;
|
||||
public:
|
||||
DeleteCommandUndoCommand(int deleteIdx, const QString& text, std::shared_ptr<ProjectModel::SoundMacroNode> node)
|
||||
: EditorUndoCommand(node, QUndoStack::tr("Delete %1").arg(text)), m_deleteIdx(deleteIdx) {}
|
||||
DeleteCommandUndoCommand(int deleteIdx, const QString& text, amuse::ObjToken<ProjectModel::SoundMacroNode> node)
|
||||
: EditorUndoCommand(node.get(), QUndoStack::tr("Delete %1").arg(text)), m_deleteIdx(deleteIdx) {}
|
||||
void undo()
|
||||
{
|
||||
m_undid = true;
|
||||
|
@ -881,7 +885,7 @@ void SoundMacroListing::clear()
|
|||
|
||||
bool SoundMacroListing::loadData(ProjectModel::SoundMacroNode* node)
|
||||
{
|
||||
m_node = node->shared_from_this();
|
||||
m_node = node;
|
||||
clear();
|
||||
int i = 0;
|
||||
for (auto& cmd : node->m_obj->m_cmds)
|
||||
|
|
|
@ -127,7 +127,7 @@ class SoundMacroListing : public QWidget
|
|||
Q_OBJECT
|
||||
friend class CommandWidget;
|
||||
friend class SoundMacroEditor;
|
||||
std::shared_ptr<ProjectModel::SoundMacroNode> m_node;
|
||||
amuse::ObjToken<ProjectModel::SoundMacroNode> m_node;
|
||||
QVBoxLayout* m_layout;
|
||||
QLayoutItem* m_dragItem = nullptr;
|
||||
int m_origIdx = -1;
|
||||
|
|
|
@ -220,22 +220,22 @@
|
|||
<translation>Keine MIDI-Geräte gefunden</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location line="+201"/>
|
||||
<location line="+202"/>
|
||||
<source>New Project</source>
|
||||
<translation>Neues Projekt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location line="+60"/>
|
||||
<location line="+63"/>
|
||||
<source>Open Project</source>
|
||||
<translation>Offenes Projekt</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location line="-37"/>
|
||||
<location line="-40"/>
|
||||
<source>The directory at '%1' does not exist.</source>
|
||||
<translation>Das Verzeichnis '% 1' existiert nicht.</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location line="-468"/>
|
||||
<location line="-469"/>
|
||||
<source>Clear Recent Projects</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -251,46 +251,46 @@
|
|||
</message>
|
||||
<message>
|
||||
<location line="+31"/>
|
||||
<location line="+302"/>
|
||||
<location line="+303"/>
|
||||
<source>The directory at '%1' must not be empty.</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location line="-301"/>
|
||||
<location line="+302"/>
|
||||
<location line="-302"/>
|
||||
<location line="+303"/>
|
||||
<source>Directory empty</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location line="-189"/>
|
||||
<location line="-190"/>
|
||||
<source>SUSTAIN</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location line="+195"/>
|
||||
<location line="+196"/>
|
||||
<source>Bad Directory</source>
|
||||
<translation>Schlechtes Verzeichnis</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location line="+12"/>
|
||||
<location line="+15"/>
|
||||
<source>Opening</source>
|
||||
<translation>Öffnung</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location line="+0"/>
|
||||
<location line="+76"/>
|
||||
<location line="+78"/>
|
||||
<location line="+83"/>
|
||||
<location line="+45"/>
|
||||
<source>Scanning Project</source>
|
||||
<translation>Projekt scannen</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location line="-192"/>
|
||||
<location line="-194"/>
|
||||
<source>Opening %1</source>
|
||||
<translation>Eröffnung% 1</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location line="+64"/>
|
||||
<location line="+66"/>
|
||||
<source>Reloading Samples</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -391,7 +391,7 @@
|
|||
<context>
|
||||
<name>ProjectModel</name>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="+226"/>
|
||||
<location filename="../ProjectModel.cpp" line="+227"/>
|
||||
<source>Sound Macros</source>
|
||||
<translation>Sound-Makros</translation>
|
||||
</message>
|
||||
|
@ -442,7 +442,7 @@
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location line="+322"/>
|
||||
<location line="+326"/>
|
||||
<source>Reorder %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -542,7 +542,7 @@
|
|||
<context>
|
||||
<name>TargetButton</name>
|
||||
<message>
|
||||
<location filename="../SoundMacroEditor.cpp" line="-944"/>
|
||||
<location filename="../SoundMacroEditor.cpp" line="-948"/>
|
||||
<source>Set step with target click</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
|
|
@ -188,7 +188,7 @@ public:
|
|||
{
|
||||
atUint32 m_sampleOff = 0;
|
||||
atUint32 m_unk = 0;
|
||||
atUint8 m_pitch = 60;
|
||||
atUint8 m_pitch = 0;
|
||||
atUint16 m_sampleRate = 0;
|
||||
atUint32 m_numSamples = 0; // Top 8 bits is SampleFormat
|
||||
atUint32 m_loopStartSample = 0;
|
||||
|
@ -204,6 +204,16 @@ public:
|
|||
time_t m_looseModTime = 0;
|
||||
std::unique_ptr<uint8_t[]> m_looseData;
|
||||
|
||||
/* Use middle C when pitch is (impossibly low) default */
|
||||
atUint8 getPitch() const { return m_pitch == 0 ? atUint8(60) : m_pitch; }
|
||||
atUint32 getNumSamples() const { return m_numSamples & 0xffffff; }
|
||||
SampleFormat getSampleFormat() const { return SampleFormat(m_numSamples >> 24); }
|
||||
bool isFormatDSP() const
|
||||
{
|
||||
SampleFormat fmt = getSampleFormat();
|
||||
return fmt == SampleFormat::DSP || fmt == SampleFormat::DSP_DRUM;
|
||||
}
|
||||
|
||||
EntryData() = default;
|
||||
|
||||
template <athena::Endian DNAE>
|
||||
|
|
|
@ -150,45 +150,94 @@ public:
|
|||
};
|
||||
|
||||
template<class SubCls>
|
||||
class ObjToken;
|
||||
|
||||
template<class SubCls>
|
||||
class ObjWrapper : IObj
|
||||
class ObjWrapper : public IObj
|
||||
{
|
||||
friend class ObjToken<SubCls>;
|
||||
SubCls m_obj;
|
||||
SubCls* get() { return &m_obj; }
|
||||
const SubCls* get() const { return &m_obj; }
|
||||
public:
|
||||
template <class... _Args>
|
||||
ObjWrapper(_Args&&... args) : m_obj(std::forward<_Args>(args)...) {}
|
||||
SubCls* get() { return &m_obj; }
|
||||
const SubCls* get() const { return &m_obj; }
|
||||
};
|
||||
|
||||
template<class SubCls>
|
||||
class ObjToken
|
||||
class ObjTokenBase
|
||||
{
|
||||
ObjWrapper<SubCls>* m_obj = nullptr;
|
||||
protected:
|
||||
IObj* m_obj = nullptr;
|
||||
ObjTokenBase(IObj* obj) : m_obj(obj) { if (m_obj) m_obj->increment(); }
|
||||
public:
|
||||
ObjToken() = default;
|
||||
ObjToken(ObjWrapper<SubCls>* obj) : m_obj(obj) { if (m_obj) m_obj->increment(); }
|
||||
ObjToken(const ObjToken& other) : m_obj(other.m_obj) { if (m_obj) m_obj->increment(); }
|
||||
ObjToken(ObjToken&& other) : m_obj(other.m_obj) { other.m_obj = nullptr; }
|
||||
ObjToken& operator=(ObjWrapper<SubCls>* obj)
|
||||
{ if (m_obj) m_obj->decrement(); m_obj = obj; if (m_obj) m_obj->increment(); return *this; }
|
||||
ObjToken& operator=(const ObjToken& other)
|
||||
ObjTokenBase() = default;
|
||||
ObjTokenBase(const ObjTokenBase& other) : m_obj(other.m_obj) { if (m_obj) m_obj->increment(); }
|
||||
ObjTokenBase(ObjTokenBase&& other) : m_obj(other.m_obj) { other.m_obj = nullptr; }
|
||||
ObjTokenBase& operator=(const ObjTokenBase& other)
|
||||
{ if (m_obj) m_obj->decrement(); m_obj = other.m_obj; if (m_obj) m_obj->increment(); return *this; }
|
||||
ObjToken& operator=(ObjToken&& other)
|
||||
ObjTokenBase& operator=(ObjTokenBase&& other)
|
||||
{ if (m_obj) m_obj->decrement(); m_obj = other.m_obj; other.m_obj = nullptr; return *this; }
|
||||
~ObjToken() { if (m_obj) m_obj->decrement(); }
|
||||
SubCls* get() const { return m_obj->get(); }
|
||||
SubCls* operator->() const { return m_obj->get(); }
|
||||
SubCls& operator*() const { return *m_obj->get(); }
|
||||
~ObjTokenBase() { if (m_obj) m_obj->decrement(); }
|
||||
operator bool() const { return m_obj != nullptr; }
|
||||
void reset() { if (m_obj) m_obj->decrement(); m_obj = nullptr; }
|
||||
};
|
||||
|
||||
template<class SubCls, class Enable = void>
|
||||
class ObjToken : public ObjTokenBase<SubCls>
|
||||
{
|
||||
IObj*& _obj() { return ObjTokenBase<SubCls>::m_obj; }
|
||||
IObj*const& _obj() const { return ObjTokenBase<SubCls>::m_obj; }
|
||||
public:
|
||||
using ObjTokenBase<SubCls>::ObjTokenBase;
|
||||
ObjToken(ObjWrapper<SubCls>* obj) : ObjTokenBase<SubCls>(obj) {}
|
||||
ObjToken& operator=(ObjWrapper<SubCls>* obj)
|
||||
{ if (_obj()) _obj()->decrement(); _obj() = obj; if (_obj()) _obj()->increment(); return *this; }
|
||||
SubCls* get() const { return static_cast<ObjWrapper<SubCls>*>(_obj())->get(); }
|
||||
SubCls* operator->() const { return get(); }
|
||||
SubCls& operator*() const { return *get(); }
|
||||
};
|
||||
|
||||
template<class SubCls>
|
||||
class ObjToken<SubCls, typename std::enable_if_t<std::is_base_of_v<IObj, SubCls>>> : public ObjTokenBase<SubCls>
|
||||
{
|
||||
IObj*& _obj() { return ObjTokenBase<SubCls>::m_obj; }
|
||||
IObj*const& _obj() const { return ObjTokenBase<SubCls>::m_obj; }
|
||||
public:
|
||||
using ObjTokenBase<SubCls>::ObjTokenBase;
|
||||
ObjToken(IObj* obj) : ObjTokenBase<SubCls>(obj) {}
|
||||
ObjToken& operator=(IObj* obj)
|
||||
{ if (_obj()) _obj()->decrement(); _obj() = obj; if (_obj()) _obj()->increment(); return *this; }
|
||||
SubCls* get() const { return static_cast<SubCls*>(_obj()); }
|
||||
SubCls* operator->() const { return get(); }
|
||||
SubCls& operator*() const { return *get(); }
|
||||
template <class T>
|
||||
T* cast() const { return static_cast<T*>(_obj()); }
|
||||
};
|
||||
|
||||
/* ONLY USE WITH CLASSES DERIVED FROM IOBJ!
|
||||
* Bypasses type_traits tests for incomplete type definitions. */
|
||||
template<class SubCls>
|
||||
class IObjToken : public ObjTokenBase<SubCls>
|
||||
{
|
||||
IObj*& _obj() { return ObjTokenBase<SubCls>::m_obj; }
|
||||
IObj*const& _obj() const { return ObjTokenBase<SubCls>::m_obj; }
|
||||
public:
|
||||
using ObjTokenBase<SubCls>::ObjTokenBase;
|
||||
IObjToken(IObj* obj) : ObjTokenBase<SubCls>(obj) {}
|
||||
IObjToken& operator=(IObj* obj)
|
||||
{ if (_obj()) _obj()->decrement(); _obj() = obj; if (_obj()) _obj()->increment(); return *this; }
|
||||
SubCls* get() const { return static_cast<SubCls*>(_obj()); }
|
||||
SubCls* operator->() const { return get(); }
|
||||
SubCls& operator*() const { return *get(); }
|
||||
template <class T>
|
||||
T* cast() const { return static_cast<T*>(_obj()); }
|
||||
};
|
||||
|
||||
template <class Tp, class... _Args>
|
||||
static inline ObjToken<Tp> MakeObj(_Args&&... args)
|
||||
inline typename std::enable_if_t<std::is_base_of_v<IObj, Tp>, ObjToken<Tp>> MakeObj(_Args&&... args)
|
||||
{
|
||||
return new Tp(std::forward<_Args>(args)...);
|
||||
}
|
||||
|
||||
template <class Tp, class... _Args>
|
||||
inline typename std::enable_if_t<!std::is_base_of_v<IObj, Tp>, ObjToken<Tp>> MakeObj(_Args&&... args)
|
||||
{
|
||||
return new ObjWrapper<Tp>(std::forward<_Args>(args)...);
|
||||
}
|
||||
|
|
|
@ -28,4 +28,8 @@ unsigned DSPDecompressFrameStateOnly(const uint8_t* in,
|
|||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||
unsigned lastSample);
|
||||
|
||||
unsigned DSPDecompressFrameRangedStateOnly(const uint8_t* in,
|
||||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||
unsigned firstSample, unsigned lastSample);
|
||||
|
||||
#endif // _DSPCODEC_h
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
namespace amuse
|
||||
{
|
||||
struct StudioSend;
|
||||
|
||||
class Studio
|
||||
{
|
||||
|
@ -17,17 +18,7 @@ class Studio
|
|||
Submix m_master;
|
||||
Submix m_auxA;
|
||||
Submix m_auxB;
|
||||
struct StudioSend
|
||||
{
|
||||
ObjToken<Studio> m_targetStudio;
|
||||
float m_dryLevel;
|
||||
float m_auxALevel;
|
||||
float m_auxBLevel;
|
||||
StudioSend(ObjToken<Studio> studio, float dry, float auxA, float auxB)
|
||||
: m_targetStudio(studio), m_dryLevel(dry), m_auxALevel(auxA), m_auxBLevel(auxB)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
std::list<StudioSend> m_studiosOut;
|
||||
#ifndef NDEBUG
|
||||
bool _cyclicCheck(Studio* leaf);
|
||||
|
@ -48,6 +39,19 @@ public:
|
|||
|
||||
Engine& getEngine() { return m_engine; }
|
||||
};
|
||||
|
||||
struct StudioSend
|
||||
{
|
||||
ObjToken<Studio> m_targetStudio;
|
||||
float m_dryLevel;
|
||||
float m_auxALevel;
|
||||
float m_auxBLevel;
|
||||
StudioSend(ObjToken<Studio> studio, float dry, float auxA, float auxB)
|
||||
: m_targetStudio(studio), m_dryLevel(dry), m_auxALevel(auxA), m_auxBLevel(auxB)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __AMUSE_STUDIO_HPP__
|
||||
|
|
|
@ -157,8 +157,6 @@ void AudioGroupSampleDirectory::EntryData::loadLooseDSP(SystemStringView dspPath
|
|||
DSPADPCMHeader header;
|
||||
header.read(r);
|
||||
m_pitch = header.m_pitch;
|
||||
if (m_pitch == 0)
|
||||
m_pitch = 60;
|
||||
m_sampleRate = atUint16(header.x8_sample_rate);
|
||||
m_numSamples = header.x0_num_samples;
|
||||
if (header.xc_loop_flag)
|
||||
|
@ -209,8 +207,6 @@ void AudioGroupSampleDirectory::EntryData::loadLooseWAV(SystemStringView wavPath
|
|||
WAVSampleChunk smpl;
|
||||
smpl.read(r);
|
||||
m_pitch = atUint8(smpl.midiNote);
|
||||
if (m_pitch == 0)
|
||||
m_pitch = 60;
|
||||
|
||||
if (smpl.numSampleLoops)
|
||||
{
|
||||
|
@ -415,7 +411,7 @@ void AudioGroupSampleDirectory::extractAllWAV(amuse::SystemStringView destDir, c
|
|||
void AudioGroupSampleDirectory::_extractCompressed(SampleId id, const EntryData& ent,
|
||||
amuse::SystemStringView destDir, const unsigned char* samp)
|
||||
{
|
||||
SampleFormat fmt = SampleFormat(ent.m_numSamples >> 24);
|
||||
SampleFormat fmt = ent.getSampleFormat();
|
||||
if (fmt == SampleFormat::PCM || fmt == SampleFormat::PCM_PC)
|
||||
{
|
||||
_extractWAV(id, ent, destDir, samp);
|
||||
|
@ -430,7 +426,7 @@ void AudioGroupSampleDirectory::_extractCompressed(SampleId id, const EntryData&
|
|||
path += SampleId::CurNameDB->resolveNameFromId(id);
|
||||
#endif
|
||||
|
||||
uint32_t numSamples = ent.m_numSamples & 0xffffff;
|
||||
uint32_t numSamples = ent.getNumSamples();
|
||||
atUint64 dataLen;
|
||||
if (fmt == SampleFormat::DSP || fmt == SampleFormat::DSP_DRUM)
|
||||
{
|
||||
|
|
|
@ -151,3 +151,32 @@ unsigned DSPDecompressFrameStateOnly(const uint8_t* in,
|
|||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
unsigned DSPDecompressFrameRangedStateOnly(const uint8_t* in,
|
||||
const int16_t coefs[8][2], int16_t* prev1, int16_t* prev2,
|
||||
unsigned firstSample, unsigned lastSample)
|
||||
{
|
||||
uint8_t cIdx = (in[0]>>4) & 0xf;
|
||||
int32_t factor1 = coefs[cIdx][0];
|
||||
int32_t factor2 = coefs[cIdx][1];
|
||||
uint8_t exp = in[0] & 0xf;
|
||||
unsigned ret = 0;
|
||||
for (unsigned s=firstSample ; s<14 && s<lastSample ; ++s)
|
||||
{
|
||||
int32_t sampleData = (s&1)?
|
||||
NibbleToInt[(in[s/2+1])&0xf]:
|
||||
NibbleToInt[(in[s/2+1]>>4)&0xf];
|
||||
sampleData <<= exp;
|
||||
sampleData <<= 11;
|
||||
sampleData += 1024;
|
||||
sampleData +=
|
||||
factor1 * ((int32_t)(*prev1)) +
|
||||
factor2 * ((int32_t)(*prev2));
|
||||
sampleData >>= 11;
|
||||
sampleData = DSPSampClamp(sampleData);
|
||||
*prev2 = *prev1;
|
||||
*prev1 = sampleData;
|
||||
++ret;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -76,15 +76,15 @@ std::list<ObjToken<Sequencer>>::iterator Engine::_allocateSequencer(const AudioG
|
|||
const SongGroupIndex* songGroup = group.getProj().getSongGroupIndex(groupId);
|
||||
if (songGroup)
|
||||
{
|
||||
auto it = m_activeSequencers.emplace(m_activeSequencers.end(),
|
||||
MakeObj<Sequencer>(*this, group, groupId, songGroup, setupId, studio));
|
||||
amuse::ObjToken<Sequencer> tok = MakeObj<Sequencer>(*this, group, groupId, songGroup, setupId, studio);
|
||||
auto it = m_activeSequencers.emplace(m_activeSequencers.end(), tok);
|
||||
return it;
|
||||
}
|
||||
const SFXGroupIndex* sfxGroup = group.getProj().getSFXGroupIndex(groupId);
|
||||
if (sfxGroup)
|
||||
{
|
||||
auto it = m_activeSequencers.emplace(m_activeSequencers.end(),
|
||||
MakeObj<Sequencer>(*this, group, groupId, sfxGroup, studio));
|
||||
amuse::ObjToken<Sequencer> tok = MakeObj<Sequencer>(*this, group, groupId, sfxGroup, studio);
|
||||
auto it = m_activeSequencers.emplace(m_activeSequencers.end(), tok);
|
||||
return it;
|
||||
}
|
||||
return {};
|
||||
|
@ -306,6 +306,8 @@ ObjToken<Voice> Engine::macroStart(const AudioGroup* group, SoundMacroId id, uin
|
|||
return {};
|
||||
}
|
||||
|
||||
(*ret)->setVolume(1.f);
|
||||
(*ret)->setPan(0.f);
|
||||
return *ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -215,7 +215,7 @@ ObjToken<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velocity)
|
|||
if (m_parent->m_songGroup && !m_page)
|
||||
return {};
|
||||
|
||||
if (m_lastVoice->isDestroyed())
|
||||
if (m_lastVoice && m_lastVoice->isDestroyed())
|
||||
m_lastVoice.reset();
|
||||
|
||||
/* If portamento is enabled for voice, pre-empt spawning new voices */
|
||||
|
@ -304,7 +304,7 @@ void Sequencer::ChannelState::keyOff(uint8_t note, uint8_t velocity)
|
|||
if (keySearch == m_chanVoxs.cend())
|
||||
return;
|
||||
|
||||
if (m_lastVoice->isDestroyed() || keySearch->second == m_lastVoice)
|
||||
if ((m_lastVoice && m_lastVoice->isDestroyed()) || keySearch->second == m_lastVoice)
|
||||
m_lastVoice.reset();
|
||||
keySearch->second->keyOff();
|
||||
m_keyoffVoxs.emplace(std::move(keySearch->second));
|
||||
|
@ -428,7 +428,7 @@ void Sequencer::setTempo(double ticksPerSec) { m_ticksPerSec = ticksPerSec; }
|
|||
|
||||
void Sequencer::ChannelState::allOff()
|
||||
{
|
||||
if (m_lastVoice->isDestroyed())
|
||||
if (m_lastVoice && m_lastVoice->isDestroyed())
|
||||
m_lastVoice.reset();
|
||||
for (auto it = m_chanVoxs.begin(); it != m_chanVoxs.end();)
|
||||
{
|
||||
|
@ -481,7 +481,7 @@ void Sequencer::allOff(uint8_t chan, bool now)
|
|||
|
||||
void Sequencer::ChannelState::killKeygroup(uint8_t kg, bool now)
|
||||
{
|
||||
if (m_lastVoice->isDestroyed())
|
||||
if (m_lastVoice && m_lastVoice->isDestroyed())
|
||||
m_lastVoice.reset();
|
||||
|
||||
for (auto it = m_chanVoxs.begin(); it != m_chanVoxs.end();)
|
||||
|
|
|
@ -40,25 +40,25 @@ float SoundMacroState::Evaluator::evaluate(double time, const Voice& vox, const
|
|||
{
|
||||
case 128:
|
||||
/* Pitchbend */
|
||||
thisValue = vox.getPitchWheel();
|
||||
thisValue = (vox.getPitchWheel() * 0.5f + 0.5f) * 127.f;
|
||||
break;
|
||||
case 129:
|
||||
/* Aftertouch */
|
||||
thisValue = vox.getAftertouch() * (2.f / 127.f);
|
||||
thisValue = vox.getAftertouch();
|
||||
break;
|
||||
case 130:
|
||||
/* LFO1 */
|
||||
if (vox.m_lfoPeriods[0])
|
||||
thisValue = std::sin(time / vox.m_lfoPeriods[0] * 2.f * M_PIF);
|
||||
thisValue = (std::sin(time / vox.m_lfoPeriods[0] * 2.f * M_PIF) * 0.5f + 0.5f) * 127.f;
|
||||
break;
|
||||
case 131:
|
||||
/* LFO2 */
|
||||
if (vox.m_lfoPeriods[1])
|
||||
thisValue = std::sin(time / vox.m_lfoPeriods[1] * 2.f * M_PIF);
|
||||
thisValue = (std::sin(time / vox.m_lfoPeriods[1] * 2.f * M_PIF) * 0.5f + 0.5f) * 127.f;
|
||||
break;
|
||||
case 132:
|
||||
/* Surround panning */
|
||||
thisValue = vox.m_curSpan;
|
||||
thisValue = (vox.m_curSpan * 0.5f + 0.5f) * 127.f;
|
||||
break;
|
||||
case 133:
|
||||
/* Macro-starting key */
|
||||
|
@ -73,10 +73,7 @@ float SoundMacroState::Evaluator::evaluate(double time, const Voice& vox, const
|
|||
thisValue = clamp(0.f, float(st.m_execTime * 1000.f), 16383.f);
|
||||
break;
|
||||
default:
|
||||
if (comp.m_midiCtrl == 10) /* Centered pan computation */
|
||||
thisValue = vox.getCtrlValue(comp.m_midiCtrl) * (2.f / 127.f) - 1.f;
|
||||
else
|
||||
thisValue = vox.getCtrlValue(comp.m_midiCtrl) * (2.f / 127.f);
|
||||
thisValue = vox.getCtrlValue(comp.m_midiCtrl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,7 +119,7 @@ void Voice::_doKeyOff()
|
|||
void Voice::_setTotalPitch(int32_t cents, bool slew)
|
||||
{
|
||||
// fprintf(stderr, "PITCH %d %d \n", cents, slew);
|
||||
int32_t interval = clamp(0, cents, 12700) - m_curSample->m_pitch * 100;
|
||||
int32_t interval = clamp(0, cents, 12700) - m_curSample->getPitch() * 100;
|
||||
double ratio = std::exp2(interval / 1200.0) * m_dopplerRatio;
|
||||
m_sampleRate = m_curSample->m_sampleRate * ratio;
|
||||
m_backendVoice->setPitchRatio(ratio, slew);
|
||||
|
@ -176,8 +176,9 @@ std::unique_ptr<int8_t[]>& Voice::_ensureCtrlVals()
|
|||
|
||||
std::list<ObjToken<Voice>>::iterator Voice::_allocateVoice(double sampleRate, bool dynamicPitch)
|
||||
{
|
||||
auto it = m_childVoices.emplace(
|
||||
m_childVoices.end(), MakeObj<Voice>(m_engine, m_audioGroup, m_groupId, m_engine.m_nextVid++, m_emitter, m_studio));
|
||||
amuse::ObjToken<Voice> tok =
|
||||
MakeObj<Voice>(m_engine, m_audioGroup, m_groupId, m_engine.m_nextVid++, m_emitter, m_studio);
|
||||
auto it = m_childVoices.emplace(m_childVoices.end(), tok);
|
||||
m_childVoices.back()->m_backendVoice =
|
||||
m_engine.getBackend().allocateVoice(*m_childVoices.back(), sampleRate, dynamicPitch);
|
||||
return it;
|
||||
|
@ -290,11 +291,11 @@ void Voice::_procSamplePre(int16_t& samp)
|
|||
/* Apply tremolo */
|
||||
if (m_state.m_tremoloSel && (m_tremoloScale || m_tremoloModScale))
|
||||
{
|
||||
float t = m_state.m_tremoloSel.evaluate(m_voiceTime, *this, m_state) / 2.f;
|
||||
float t = m_state.m_tremoloSel.evaluate(m_voiceTime, *this, m_state) / 127.f;
|
||||
if (m_tremoloScale && m_tremoloModScale)
|
||||
{
|
||||
float fac = (1.0f - t) + (m_tremoloScale * t);
|
||||
float modT = m_state.m_modWheelSel ? (m_state.m_modWheelSel.evaluate(m_voiceTime, *this, m_state) / 2.f)
|
||||
float modT = m_state.m_modWheelSel ? (m_state.m_modWheelSel.evaluate(m_voiceTime, *this, m_state) / 127.f)
|
||||
: (getCtrlValue(1) / 127.f);
|
||||
float modFac = (1.0f - modT) + (m_tremoloModScale * modT);
|
||||
m_nextLevel *= fac * modFac;
|
||||
|
@ -306,7 +307,7 @@ void Voice::_procSamplePre(int16_t& samp)
|
|||
}
|
||||
else if (m_tremoloModScale)
|
||||
{
|
||||
float modT = m_state.m_modWheelSel ? (m_state.m_modWheelSel.evaluate(m_voiceTime, *this, m_state) / 2.f)
|
||||
float modT = m_state.m_modWheelSel ? (m_state.m_modWheelSel.evaluate(m_voiceTime, *this, m_state) / 127.f)
|
||||
: (getCtrlValue(1) / 127.f);
|
||||
float modFac = (1.0f - modT) + (m_tremoloModScale * modT);
|
||||
m_nextLevel *= modFac;
|
||||
|
@ -322,25 +323,25 @@ void Voice::_procSamplePre(int16_t& samp)
|
|||
template <typename T>
|
||||
T Voice::_procSampleMaster(double time, T samp)
|
||||
{
|
||||
float evalVol = m_state.m_volumeSel ? (m_state.m_volumeSel.evaluate(time, *this, m_state) / 2.f) : 1.f;
|
||||
float evalVol = m_state.m_volumeSel ? (m_state.m_volumeSel.evaluate(time, *this, m_state) / 127.f) : 1.f;
|
||||
return ApplyVolume(clamp(0.f, evalVol, 1.f), samp);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T Voice::_procSampleAuxA(double time, T samp)
|
||||
{
|
||||
float evalVol = m_state.m_volumeSel ? (m_state.m_volumeSel.evaluate(time, *this, m_state) / 2.f) : 1.f;
|
||||
evalVol *= m_state.m_reverbSel ? (m_state.m_reverbSel.evaluate(time, *this, m_state) / 2.f) : m_curReverbVol;
|
||||
evalVol += m_state.m_preAuxASel ? (m_state.m_preAuxASel.evaluate(time, *this, m_state) / 2.f) : 0.f;
|
||||
float evalVol = m_state.m_volumeSel ? (m_state.m_volumeSel.evaluate(time, *this, m_state) / 127.f) : 1.f;
|
||||
evalVol *= m_state.m_reverbSel ? (m_state.m_reverbSel.evaluate(time, *this, m_state) / 127.f) : m_curReverbVol;
|
||||
evalVol += m_state.m_preAuxASel ? (m_state.m_preAuxASel.evaluate(time, *this, m_state) / 127.f) : 0.f;
|
||||
return ApplyVolume(clamp(0.f, evalVol, 1.f), samp);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T Voice::_procSampleAuxB(double time, T samp)
|
||||
{
|
||||
float evalVol = m_state.m_volumeSel ? (m_state.m_volumeSel.evaluate(time, *this, m_state) / 2.f) : 1.f;
|
||||
evalVol *= m_state.m_postAuxB ? (m_state.m_postAuxB.evaluate(time, *this, m_state) / 2.f) : m_curAuxBVol;
|
||||
evalVol += m_state.m_preAuxBSel ? (m_state.m_preAuxBSel.evaluate(time, *this, m_state) / 2.f) : 0.f;
|
||||
float evalVol = m_state.m_volumeSel ? (m_state.m_volumeSel.evaluate(time, *this, m_state) / 127.f) : 1.f;
|
||||
evalVol *= m_state.m_postAuxB ? (m_state.m_postAuxB.evaluate(time, *this, m_state) / 127.f) : m_curAuxBVol;
|
||||
evalVol += m_state.m_preAuxBSel ? (m_state.m_preAuxBSel.evaluate(time, *this, m_state) / 127.f) : 0.f;
|
||||
return ApplyVolume(clamp(0.f, evalVol, 1.f), samp);
|
||||
}
|
||||
|
||||
|
@ -376,7 +377,7 @@ void Voice::preSupplyAudio(double dt)
|
|||
/* Process per-block evaluators here */
|
||||
if (m_state.m_pedalSel)
|
||||
{
|
||||
bool pedal = m_state.m_pedalSel.evaluate(m_voiceTime, *this, m_state) >= 1.f;
|
||||
bool pedal = m_state.m_pedalSel.evaluate(m_voiceTime, *this, m_state) >= 64.f;
|
||||
if (pedal != m_sustained)
|
||||
setPedal(pedal);
|
||||
}
|
||||
|
@ -384,7 +385,8 @@ void Voice::preSupplyAudio(double dt)
|
|||
bool panDirty = false;
|
||||
if (m_state.m_panSel)
|
||||
{
|
||||
float evalPan = m_state.m_panSel.evaluate(m_voiceTime, *this, m_state);
|
||||
float evalPan = (m_state.m_panSel.evaluate(m_voiceTime, *this, m_state) - 64.f) / 63.f;
|
||||
evalPan = clamp(-1.f, evalPan, 1.f);
|
||||
if (evalPan != m_curPan)
|
||||
{
|
||||
m_curPan = evalPan;
|
||||
|
@ -393,7 +395,8 @@ void Voice::preSupplyAudio(double dt)
|
|||
}
|
||||
if (m_state.m_spanSel)
|
||||
{
|
||||
float evalSpan = m_state.m_spanSel.evaluate(m_voiceTime, *this, m_state);
|
||||
float evalSpan = (m_state.m_spanSel.evaluate(m_voiceTime, *this, m_state) - 64.f) / 63.f;
|
||||
evalSpan = clamp(-1.f, evalSpan, 1.f);
|
||||
if (evalSpan != m_curSpan)
|
||||
{
|
||||
m_curSpan = evalSpan;
|
||||
|
@ -404,7 +407,10 @@ void Voice::preSupplyAudio(double dt)
|
|||
_setPan(m_curPan);
|
||||
|
||||
if (m_state.m_pitchWheelSel)
|
||||
_setPitchWheel(m_state.m_pitchWheelSel.evaluate(m_voiceTime, *this, m_state));
|
||||
{
|
||||
float evalPWheel = (m_state.m_pitchWheelSel.evaluate(m_voiceTime, *this, m_state) - 64.f) / 63.f;
|
||||
_setPitchWheel(clamp(-1.f, evalPWheel, 1.f));
|
||||
}
|
||||
|
||||
/* Process active pan-sweep */
|
||||
bool refresh = false;
|
||||
|
@ -939,13 +945,13 @@ void Voice::startSample(SampleId sampId, int32_t offset)
|
|||
std::tie(m_curSample, m_curSampleData) = m_audioGroup.getSampleData(sampId, sample);
|
||||
|
||||
m_sampleRate = m_curSample->m_sampleRate;
|
||||
m_curPitch = m_curSample->m_pitch;
|
||||
m_curPitch = m_curSample->getPitch();
|
||||
m_pitchDirty = true;
|
||||
_setPitchWheel(m_curPitchWheel);
|
||||
m_backendVoice->resetSampleRate(m_curSample->m_sampleRate);
|
||||
m_needsSlew = false;
|
||||
|
||||
int32_t numSamples = m_curSample->m_numSamples & 0xffffff;
|
||||
int32_t numSamples = m_curSample->getNumSamples();
|
||||
if (offset)
|
||||
{
|
||||
if (m_curSample->m_loopLengthSamples)
|
||||
|
@ -962,7 +968,7 @@ void Voice::startSample(SampleId sampId, int32_t offset)
|
|||
m_prev1 = 0;
|
||||
m_prev2 = 0;
|
||||
|
||||
m_curFormat = SampleFormat(m_curSample->m_numSamples >> 24);
|
||||
m_curFormat = m_curSample->getSampleFormat();
|
||||
if (m_curFormat == SampleFormat::DSP_DRUM)
|
||||
m_curFormat = SampleFormat::DSP;
|
||||
|
||||
|
@ -1389,7 +1395,7 @@ bool Voice::doPortamento(uint8_t newNote)
|
|||
pState = true;
|
||||
break;
|
||||
case SoundMacro::CmdPortamento::PortState::MIDIControlled:
|
||||
pState = m_state.m_portamentoSel ? (m_state.m_portamentoSel.evaluate(m_voiceTime, *this, m_state) >= 1.f)
|
||||
pState = m_state.m_portamentoSel ? (m_state.m_portamentoSel.evaluate(m_voiceTime, *this, m_state) >= 64.f)
|
||||
: (getCtrlValue(65) >= 64);
|
||||
break;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue