Initial SampleEditor

This commit is contained in:
Jack Andersen 2018-07-29 20:20:03 -10:00
parent 16745c9bf8
commit 8d24e19989
21 changed files with 469 additions and 144 deletions

View File

@ -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();

View File

@ -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) {}

View File

@ -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>

View File

@ -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;

View File

@ -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;
} }

View File

@ -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(); }

View File

@ -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);
} }

View File

@ -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;
}; };

View File

@ -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)

View File

@ -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;

View File

@ -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 &apos;%1&apos; does not exist.</source> <source>The directory at &apos;%1&apos; does not exist.</source>
<translation>Das Verzeichnis &apos;% 1&apos; existiert nicht.</translation> <translation>Das Verzeichnis &apos;% 1&apos; 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 &apos;%1&apos; must not be empty.</source> <source>The directory at &apos;%1&apos; 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>

View File

@ -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>

View File

@ -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)...);
} }

View File

@ -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

View File

@ -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__

View File

@ -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)
{ {

View File

@ -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;
}

View File

@ -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;
} }

View File

@ -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();)

View File

@ -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;
} }
} }

View File

@ -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;
} }