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