mirror of https://github.com/AxioDL/amuse.git
More undo commands and pitch/mod coding fix
This commit is contained in:
parent
277e78c14b
commit
5e89954094
|
@ -37,7 +37,6 @@ QT5_WRAP_CPP(AMUSE_MOC
|
|||
KeyboardWidget.hpp
|
||||
StatusBarWidget.hpp
|
||||
ProjectModel.hpp
|
||||
ProjectStatistics.hpp
|
||||
EditorWidget.hpp
|
||||
SoundMacroEditor.hpp
|
||||
ADSREditor.hpp
|
||||
|
@ -55,7 +54,6 @@ add_executable(amuse-gui WIN32 MACOSX_BUNDLE
|
|||
KeyboardWidget.hpp KeyboardWidget.cpp
|
||||
StatusBarWidget.hpp
|
||||
ProjectModel.hpp ProjectModel.cpp
|
||||
ProjectStatistics.hpp ProjectStatistics.cpp
|
||||
EditorWidget.hpp EditorWidget.cpp
|
||||
SoundMacroEditor.hpp SoundMacroEditor.cpp
|
||||
ADSREditor.hpp ADSREditor.cpp
|
||||
|
|
|
@ -4,6 +4,99 @@
|
|||
#include <QScrollBar>
|
||||
#include <QMimeData>
|
||||
|
||||
class LayerDataChangeUndoCommand : public EditorUndoCommand
|
||||
{
|
||||
QModelIndex m_index;
|
||||
int m_undoVal, m_redoVal;
|
||||
bool m_undid = false;
|
||||
public:
|
||||
explicit LayerDataChangeUndoCommand(ProjectModel::LayersNode* node, const QString& text,
|
||||
QModelIndex index, int redoVal)
|
||||
: EditorUndoCommand(node, text), m_index(index), m_redoVal(redoVal) {}
|
||||
void undo()
|
||||
{
|
||||
m_undid = true;
|
||||
amuse::LayerMapping& layer = (*static_cast<ProjectModel::LayersNode*>(m_node.get())->m_obj)[m_index.row()];
|
||||
|
||||
switch (m_index.column())
|
||||
{
|
||||
case 0:
|
||||
layer.macro.id = m_undoVal;
|
||||
break;
|
||||
case 1:
|
||||
layer.keyLo = m_undoVal;
|
||||
break;
|
||||
case 2:
|
||||
layer.keyHi = m_undoVal;
|
||||
break;
|
||||
case 3:
|
||||
layer.transpose = m_undoVal;
|
||||
break;
|
||||
case 4:
|
||||
layer.volume = m_undoVal;
|
||||
break;
|
||||
case 5:
|
||||
layer.prioOffset = m_undoVal;
|
||||
break;
|
||||
case 6:
|
||||
layer.span = m_undoVal;
|
||||
break;
|
||||
case 7:
|
||||
layer.pan = m_undoVal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo()
|
||||
{
|
||||
amuse::LayerMapping& layer = (*static_cast<ProjectModel::LayersNode*>(m_node.get())->m_obj)[m_index.row()];
|
||||
|
||||
switch (m_index.column())
|
||||
{
|
||||
case 0:
|
||||
m_undoVal = layer.macro.id;
|
||||
layer.macro.id = m_redoVal;
|
||||
break;
|
||||
case 1:
|
||||
m_undoVal = layer.keyLo;
|
||||
layer.keyLo = m_redoVal;
|
||||
break;
|
||||
case 2:
|
||||
m_undoVal = layer.keyHi;
|
||||
layer.keyHi = m_redoVal;
|
||||
break;
|
||||
case 3:
|
||||
m_undoVal = layer.transpose;
|
||||
layer.transpose = m_redoVal;
|
||||
break;
|
||||
case 4:
|
||||
m_undoVal = layer.volume;
|
||||
layer.volume = m_redoVal;
|
||||
break;
|
||||
case 5:
|
||||
m_undoVal = layer.prioOffset;
|
||||
layer.prioOffset = m_redoVal;
|
||||
break;
|
||||
case 6:
|
||||
m_undoVal = layer.span;
|
||||
layer.span = m_redoVal;
|
||||
break;
|
||||
case 7:
|
||||
m_undoVal = layer.pan;
|
||||
layer.pan = m_redoVal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
};
|
||||
|
||||
SoundMacroDelegate::SoundMacroDelegate(QObject* parent)
|
||||
: QStyledItemDelegate(parent) {}
|
||||
|
||||
|
@ -35,10 +128,13 @@ void SoundMacroDelegate::setModelData(QWidget* editor, QAbstractItemModel* m, co
|
|||
ProjectModel::GroupNode* group = g_MainWindow->projectModel()->getGroupNode(model->m_node.get());
|
||||
ProjectModel::CollectionNode* smColl = group->getCollectionOfType(ProjectModel::INode::Type::SoundMacro);
|
||||
int idx = static_cast<EditorFieldProjectNode*>(editor)->currentIndex();
|
||||
if (idx == 0)
|
||||
layer.macro.id = amuse::SoundMacroId();
|
||||
else
|
||||
layer.macro.id = smColl->idOfIndex(idx - 1);
|
||||
amuse::SoundMacroId id;
|
||||
if (idx != 0)
|
||||
id = smColl->idOfIndex(idx - 1);
|
||||
if (layer.macro.id == id)
|
||||
return;
|
||||
g_MainWindow->pushUndoCommand(new LayerDataChangeUndoCommand(model->m_node.get(),
|
||||
tr("Change %1").arg(m->headerData(0, Qt::Horizontal).toString()), index, id.id));
|
||||
emit m->dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
}
|
||||
|
||||
|
@ -123,42 +219,50 @@ bool LayersModel::setData(const QModelIndex& index, const QVariant& value, int r
|
|||
{
|
||||
if (!m_node || role != Qt::EditRole)
|
||||
return false;
|
||||
amuse::LayerMapping& layer = (*m_node->m_obj)[index.row()];
|
||||
const amuse::LayerMapping& layer = (*m_node->m_obj)[index.row()];
|
||||
|
||||
switch (index.column())
|
||||
{
|
||||
case 1:
|
||||
layer.keyLo = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
case 2:
|
||||
layer.keyHi = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
case 3:
|
||||
layer.transpose = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
case 4:
|
||||
layer.volume = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
case 5:
|
||||
layer.prioOffset = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
case 6:
|
||||
layer.span = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
case 7:
|
||||
layer.pan = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
default:
|
||||
case 0:
|
||||
if (layer.macro.id == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 1:
|
||||
if (layer.keyLo == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 2:
|
||||
if (layer.keyHi == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 3:
|
||||
if (layer.transpose == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 4:
|
||||
if (layer.volume == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 5:
|
||||
if (layer.prioOffset == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 6:
|
||||
if (layer.span == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 7:
|
||||
if (layer.pan == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
g_MainWindow->pushUndoCommand(new LayerDataChangeUndoCommand(m_node.get(),
|
||||
tr("Change %1").arg(headerData(index.column(), Qt::Horizontal).toString()), index, value.toInt()));
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -210,6 +314,32 @@ Qt::DropActions LayersModel::supportedDragActions() const
|
|||
return Qt::MoveAction;
|
||||
}
|
||||
|
||||
class LayerRowMoveCommand : public EditorUndoCommand
|
||||
{
|
||||
LayersTableView* m_view;
|
||||
int m_undoPos, m_redoPos, m_count;
|
||||
bool m_undid = false;
|
||||
public:
|
||||
explicit LayerRowMoveCommand(ProjectModel::LayersNode* node, const QString& text, LayersTableView* view,
|
||||
int undoPos, int redoPos, int count)
|
||||
: EditorUndoCommand(node, text), m_view(view), m_undoPos(undoPos), m_redoPos(redoPos), m_count(count) {}
|
||||
void undo()
|
||||
{
|
||||
m_undid = true;
|
||||
EditorUndoCommand::undo();
|
||||
if (m_redoPos > m_undoPos)
|
||||
m_view->model()->moveRows(QModelIndex(), m_redoPos - 1, m_count, QModelIndex(), m_undoPos);
|
||||
else
|
||||
m_view->model()->moveRows(QModelIndex(), m_redoPos, m_count, QModelIndex(), m_undoPos + 1);
|
||||
}
|
||||
void redo()
|
||||
{
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
m_view->model()->moveRows(QModelIndex(), m_undoPos, m_count, QModelIndex(), m_redoPos);
|
||||
}
|
||||
};
|
||||
|
||||
bool LayersModel::dropMimeData(const QMimeData* data, Qt::DropAction action,
|
||||
int row, int column, const QModelIndex& parent)
|
||||
{
|
||||
|
@ -257,10 +387,75 @@ bool LayersModel::dropMimeData(const QMimeData* data, Qt::DropAction action,
|
|||
dest += 1;
|
||||
}
|
||||
|
||||
moveRows(QModelIndex(), start, count, QModelIndex(), dest);
|
||||
g_MainWindow->pushUndoCommand(new LayerRowMoveCommand(m_node.get(),
|
||||
count > 1 ? tr("Move Layers") : tr("Move Layer"),
|
||||
&static_cast<LayersEditor*>(QObject::parent())->m_tableView, start, dest, count));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
class LayerRowUndoCommand : public EditorUndoCommand
|
||||
{
|
||||
protected:
|
||||
LayersTableView* m_view;
|
||||
std::vector<std::pair<amuse::LayerMapping, int>> m_data;
|
||||
bool m_undid = false;
|
||||
void add()
|
||||
{
|
||||
m_view->selectionModel()->clearSelection();
|
||||
for (const auto& p : m_data)
|
||||
{
|
||||
static_cast<LayersModel*>(m_view->model())->_insertRow(p.second, p.first);
|
||||
m_view->selectionModel()->select(QItemSelection(
|
||||
m_view->model()->index(p.second, 0), m_view->model()->index(p.second, 7)),
|
||||
QItemSelectionModel::SelectCurrent);
|
||||
}
|
||||
}
|
||||
void del()
|
||||
{
|
||||
for (auto it = m_data.rbegin(); it != m_data.rend(); ++it)
|
||||
{
|
||||
it->first = static_cast<LayersModel*>(m_view->model())->_removeRow(it->second);
|
||||
}
|
||||
}
|
||||
void undo()
|
||||
{
|
||||
m_undid = true;
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo()
|
||||
{
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
public:
|
||||
explicit LayerRowUndoCommand(ProjectModel::LayersNode* node, const QString& text, LayersTableView* view,
|
||||
std::vector<std::pair<amuse::LayerMapping, int>>&& data)
|
||||
: EditorUndoCommand(node, text), m_view(view), m_data(std::move(data)) {}
|
||||
};
|
||||
|
||||
class LayerRowAddUndoCommand : public LayerRowUndoCommand
|
||||
{
|
||||
using base = LayerRowUndoCommand;
|
||||
public:
|
||||
explicit LayerRowAddUndoCommand(ProjectModel::LayersNode* node, const QString& text, LayersTableView* view,
|
||||
std::vector<std::pair<amuse::LayerMapping, int>>&& data)
|
||||
: LayerRowUndoCommand(node, text, view, std::move(data)) {}
|
||||
void undo() { base::undo(); base::del(); }
|
||||
void redo() { base::redo(); base::add(); }
|
||||
};
|
||||
|
||||
class LayerRowDelUndoCommand : public LayerRowUndoCommand
|
||||
{
|
||||
using base = LayerRowUndoCommand;
|
||||
public:
|
||||
explicit LayerRowDelUndoCommand(ProjectModel::LayersNode* node, const QString& text, LayersTableView* view,
|
||||
std::vector<std::pair<amuse::LayerMapping, int>>&& data)
|
||||
: LayerRowUndoCommand(node, text, view, std::move(data)) {}
|
||||
void undo() { base::undo(); base::add(); }
|
||||
void redo() { base::redo(); base::del(); }
|
||||
};
|
||||
|
||||
bool LayersModel::insertRows(int row, int count, const QModelIndex& parent)
|
||||
{
|
||||
if (!m_node)
|
||||
|
@ -316,15 +511,45 @@ bool LayersModel::removeRows(int row, int count, const QModelIndex& parent)
|
|||
return true;
|
||||
}
|
||||
|
||||
void LayersModel::_insertRow(int row, const amuse::LayerMapping& data)
|
||||
{
|
||||
if (!m_node)
|
||||
return;
|
||||
beginInsertRows(QModelIndex(), row, row);
|
||||
std::vector<amuse::LayerMapping>& layers = *m_node->m_obj;
|
||||
layers.insert(layers.begin() + row, data);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
amuse::LayerMapping LayersModel::_removeRow(int row)
|
||||
{
|
||||
if (!m_node)
|
||||
return {};
|
||||
beginRemoveRows(QModelIndex(), row, row);
|
||||
std::vector<amuse::LayerMapping>& layers = *m_node->m_obj;
|
||||
amuse::LayerMapping ret = layers[row];
|
||||
layers.erase(layers.begin() + row);
|
||||
endRemoveRows();
|
||||
return ret;
|
||||
}
|
||||
|
||||
LayersModel::LayersModel(QObject* parent)
|
||||
: QAbstractTableModel(parent)
|
||||
{}
|
||||
|
||||
void LayersTableView::deleteSelection()
|
||||
{
|
||||
QModelIndexList list;
|
||||
while (!(list = selectionModel()->selectedRows()).isEmpty())
|
||||
model()->removeRow(list.back().row());
|
||||
QModelIndexList list = selectionModel()->selectedRows();
|
||||
if (list.isEmpty())
|
||||
return;
|
||||
std::vector<std::pair<amuse::LayerMapping, int>> data;
|
||||
data.reserve(list.size());
|
||||
for (QModelIndex idx : list)
|
||||
data.push_back(std::make_pair(amuse::LayerMapping{}, idx.row()));
|
||||
std::sort(data.begin(), data.end(), [](const auto& a, const auto& b) { return a.second < b.second; });
|
||||
g_MainWindow->pushUndoCommand(
|
||||
new LayerRowDelUndoCommand(static_cast<LayersModel*>(model())->m_node.get(),
|
||||
data.size() > 1 ? tr("Delete Layers") : tr("Delete Layer"), this, std::move(data)));
|
||||
}
|
||||
|
||||
void LayersTableView::setModel(QAbstractItemModel* model)
|
||||
|
@ -396,10 +621,13 @@ void LayersEditor::resizeEvent(QResizeEvent* ev)
|
|||
void LayersEditor::doAdd()
|
||||
{
|
||||
QModelIndex idx = m_tableView.selectionModel()->currentIndex();
|
||||
std::vector<std::pair<amuse::LayerMapping, int>> data;
|
||||
if (!idx.isValid())
|
||||
m_model.insertRow(m_model.rowCount() - 1);
|
||||
data.push_back(std::make_pair(amuse::LayerMapping{}, m_model.rowCount() - 1));
|
||||
else
|
||||
m_model.insertRow(idx.row());
|
||||
data.push_back(std::make_pair(amuse::LayerMapping{}, idx.row()));
|
||||
g_MainWindow->pushUndoCommand(new LayerRowAddUndoCommand(m_model.m_node.get(),
|
||||
tr("Add Layer"), &m_tableView, std::move(data)));
|
||||
}
|
||||
|
||||
void LayersEditor::doSelectionChanged()
|
||||
|
|
|
@ -25,6 +25,7 @@ class LayersModel : public QAbstractTableModel
|
|||
Q_OBJECT
|
||||
friend class LayersEditor;
|
||||
friend class SoundMacroDelegate;
|
||||
friend class LayersTableView;
|
||||
amuse::ObjToken<ProjectModel::LayersNode> m_node;
|
||||
public:
|
||||
explicit LayersModel(QObject* parent = Q_NULLPTR);
|
||||
|
@ -46,6 +47,9 @@ public:
|
|||
bool moveRows(const QModelIndex& sourceParent, int sourceRow, int count,
|
||||
const QModelIndex& destinationParent, int destinationChild);
|
||||
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex());
|
||||
|
||||
void _insertRow(int row, const amuse::LayerMapping& data);
|
||||
amuse::LayerMapping _removeRow(int row);
|
||||
};
|
||||
|
||||
class LayersTableView : public QTableView
|
||||
|
@ -64,6 +68,7 @@ public:
|
|||
class LayersEditor : public EditorWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
friend class LayersModel;
|
||||
LayersModel m_model;
|
||||
LayersTableView m_tableView;
|
||||
AddRemoveButtons m_addRemoveButtons;
|
||||
|
|
|
@ -69,6 +69,9 @@
|
|||
<property name="placeholderText">
|
||||
<string>Filter</string>
|
||||
</property>
|
||||
<property name="clearButtonEnabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
|
@ -85,6 +88,9 @@
|
|||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="focusPolicy">
|
||||
<enum>Qt::WheelFocus</enum>
|
||||
</property>
|
||||
<property name="animated">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
|
|
|
@ -269,6 +269,33 @@ Qt::ItemFlags PageObjectProxyModel::flags(const QModelIndex& proxyIndex) const
|
|||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
}
|
||||
|
||||
void ProjectModel::NameUndoRegistry::registerSongName(amuse::SongId id) const
|
||||
{
|
||||
auto search = m_songIDs.find(id);
|
||||
if (search != m_songIDs.cend())
|
||||
g_MainWindow->projectModel()->_allocateSongId(id, search->second);
|
||||
}
|
||||
void ProjectModel::NameUndoRegistry::unregisterSongName(amuse::SongId id)
|
||||
{
|
||||
auto search = amuse::SongId::CurNameDB->m_idToString.find(id);
|
||||
if (search != amuse::SongId::CurNameDB->m_idToString.cend())
|
||||
m_songIDs[id] = search->second;
|
||||
g_MainWindow->projectModel()->deallocateSongId(id);
|
||||
}
|
||||
void ProjectModel::NameUndoRegistry::registerSFXName(amuse::SongId id) const
|
||||
{
|
||||
auto search = m_sfxIDs.find(id);
|
||||
if (search != m_sfxIDs.cend())
|
||||
amuse::SFXId::CurNameDB->registerPair(search->second, id);
|
||||
}
|
||||
void ProjectModel::NameUndoRegistry::unregisterSFXName(amuse::SongId id)
|
||||
{
|
||||
auto search = amuse::SFXId::CurNameDB->m_idToString.find(id);
|
||||
if (search != amuse::SFXId::CurNameDB->m_idToString.cend())
|
||||
m_sfxIDs[id] = search->second;
|
||||
amuse::SFXId::CurNameDB->remove(id);
|
||||
}
|
||||
|
||||
ProjectModel::INode::INode(const QString& name)
|
||||
: m_name(name)
|
||||
{
|
||||
|
@ -401,7 +428,27 @@ bool ProjectModel::openSongsData()
|
|||
amuse::SongId id = uint16_t(strtoul(p.first.c_str(), &endPtr, 0));
|
||||
if (endPtr == p.first.c_str() || id.id == 0xffff)
|
||||
continue;
|
||||
m_midiFiles[id] = QString::fromStdString(p.second->m_scalarString);
|
||||
|
||||
m_midiFiles.clear();
|
||||
QString path = QString::fromStdString(p.second->m_scalarString);
|
||||
m_root->oneLevelTraverse([this, id, path](INode* n)
|
||||
{
|
||||
GroupNode* gn = static_cast<GroupNode*>(n);
|
||||
amuse::AudioGroupDatabase* db = gn->getAudioGroup();
|
||||
for (const auto& p : db->getProj().songGroups())
|
||||
{
|
||||
for (const auto& m : p.second->m_midiSetups)
|
||||
{
|
||||
if (id == m.first)
|
||||
{
|
||||
Song& song = m_midiFiles[id];
|
||||
song.m_path = path;
|
||||
++song.m_refCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -489,7 +536,7 @@ bool ProjectModel::saveToFile(UIMessenger& messenger)
|
|||
{
|
||||
char id[16];
|
||||
snprintf(id, 16, "%04X", p.first.id);
|
||||
dw.writeString(id, p.second.toUtf8().data());
|
||||
dw.writeString(id, p.second.m_path.toUtf8().data());
|
||||
}
|
||||
athena::io::FileWriter w(QStringToSysString(songsFile.path()));
|
||||
if (!w.hasError())
|
||||
|
@ -1182,37 +1229,77 @@ ProjectModel::GroupNode* ProjectModel::getGroupOfSfx(amuse::SFXId id) const
|
|||
return ret;
|
||||
}
|
||||
|
||||
ProjectModel::GroupNode* ProjectModel::getGroupOfSong(amuse::SongId id) const
|
||||
{
|
||||
ProjectModel::GroupNode* ret = nullptr;
|
||||
m_root->oneLevelTraverse([id, &ret](INode* n)
|
||||
{
|
||||
GroupNode* gn = static_cast<GroupNode*>(n);
|
||||
amuse::AudioGroupDatabase* db = gn->getAudioGroup();
|
||||
for (const auto& p : db->getProj().songGroups())
|
||||
{
|
||||
if (p.second->m_midiSetups.find(id) != p.second->m_midiSetups.cend())
|
||||
{
|
||||
ret = gn;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString ProjectModel::getMIDIPathOfSong(amuse::SongId id) const
|
||||
{
|
||||
auto search = m_midiFiles.find(id);
|
||||
if (search == m_midiFiles.cend())
|
||||
return {};
|
||||
return search->second;
|
||||
return search->second.m_path;
|
||||
}
|
||||
|
||||
void ProjectModel::setMIDIPathOfSong(amuse::SongId id, const QString& path)
|
||||
{
|
||||
m_midiFiles[id] = path;
|
||||
m_midiFiles[id].m_path = path;
|
||||
}
|
||||
|
||||
void ProjectModel::_allocateSongId(amuse::SongId id, std::string_view name)
|
||||
{
|
||||
m_projectDatabase.setIdDatabases();
|
||||
amuse::SongId::CurNameDB->registerPair(name, id);
|
||||
Song& song = m_midiFiles[id];
|
||||
++song.m_refCount;
|
||||
}
|
||||
|
||||
std::pair<amuse::SongId, std::string> ProjectModel::allocateSongId()
|
||||
{
|
||||
m_projectDatabase.setIdDatabases();
|
||||
std::pair<amuse::SongId, std::string> ret;
|
||||
ret.first = amuse::SongId::CurNameDB->generateId(amuse::NameDB::Type::Song);
|
||||
ret.second = amuse::SongId::CurNameDB->generateName(ret.first, amuse::NameDB::Type::Song);
|
||||
amuse::SongId::CurNameDB->registerPair(ret.second, ret.first);
|
||||
m_midiFiles[ret.first] = {{}, 1};
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ProjectModel::deallocateSongId(amuse::SongId oldId)
|
||||
{
|
||||
Song& oldSong = m_midiFiles[oldId];
|
||||
--oldSong.m_refCount;
|
||||
if (oldSong.m_refCount <= 0)
|
||||
{
|
||||
oldSong.m_refCount = 0;
|
||||
amuse::SongId::CurNameDB->remove(oldId);
|
||||
m_midiFiles.erase(oldId);
|
||||
}
|
||||
}
|
||||
|
||||
amuse::SongId ProjectModel::exchangeSongId(amuse::SongId oldId, std::string_view newName)
|
||||
{
|
||||
m_projectDatabase.setIdDatabases();
|
||||
amuse::SongId newId;
|
||||
auto search = amuse::SongId::CurNameDB->m_stringToId.find(newName.data());
|
||||
if (search == amuse::SongId::CurNameDB->m_stringToId.cend())
|
||||
{
|
||||
newId = amuse::SongId::CurNameDB->generateId(amuse::NameDB::Type::Song);
|
||||
amuse::SongId::CurNameDB->registerPair(newName, newId);
|
||||
}
|
||||
else
|
||||
newId = search->second;
|
||||
if (oldId == newId)
|
||||
return newId;
|
||||
Song& oldSong = m_midiFiles[oldId];
|
||||
Song& newSong = m_midiFiles[newId];
|
||||
++newSong.m_refCount;
|
||||
if (newSong.m_path.isEmpty())
|
||||
newSong.m_path = oldSong.m_path;
|
||||
--oldSong.m_refCount;
|
||||
if (oldSong.m_refCount <= 0)
|
||||
{
|
||||
oldSong.m_refCount = 0;
|
||||
amuse::SongId::CurNameDB->remove(oldId);
|
||||
m_midiFiles.erase(oldId);
|
||||
}
|
||||
return newId;
|
||||
}
|
||||
|
||||
void ProjectModel::setIdDatabases(INode* context) const
|
||||
|
|
|
@ -57,32 +57,10 @@ public:
|
|||
{
|
||||
std::unordered_map<amuse::SongId, std::string> m_songIDs;
|
||||
std::unordered_map<amuse::SFXId, std::string> m_sfxIDs;
|
||||
void registerSongName(amuse::SongId id) const
|
||||
{
|
||||
auto search = m_songIDs.find(id);
|
||||
if (search != m_songIDs.cend())
|
||||
amuse::SongId::CurNameDB->registerPair(search->second, id);
|
||||
}
|
||||
void unregisterSongName(amuse::SongId id)
|
||||
{
|
||||
auto search = amuse::SongId::CurNameDB->m_idToString.find(id);
|
||||
if (search != amuse::SongId::CurNameDB->m_idToString.cend())
|
||||
m_songIDs[id] = search->second;
|
||||
amuse::SongId::CurNameDB->remove(id);
|
||||
}
|
||||
void registerSFXName(amuse::SongId id) const
|
||||
{
|
||||
auto search = m_sfxIDs.find(id);
|
||||
if (search != m_sfxIDs.cend())
|
||||
amuse::SFXId::CurNameDB->registerPair(search->second, id);
|
||||
}
|
||||
void unregisterSFXName(amuse::SongId id)
|
||||
{
|
||||
auto search = amuse::SFXId::CurNameDB->m_idToString.find(id);
|
||||
if (search != amuse::SFXId::CurNameDB->m_idToString.cend())
|
||||
m_sfxIDs[id] = search->second;
|
||||
amuse::SFXId::CurNameDB->remove(id);
|
||||
}
|
||||
void registerSongName(amuse::SongId id) const;
|
||||
void unregisterSongName(amuse::SongId id);
|
||||
void registerSFXName(amuse::SongId id) const;
|
||||
void unregisterSFXName(amuse::SongId id);
|
||||
void clear()
|
||||
{
|
||||
m_songIDs.clear();
|
||||
|
@ -98,7 +76,12 @@ private:
|
|||
amuse::ProjectDatabase m_projectDatabase;
|
||||
std::unordered_map<QString, amuse::AudioGroupDatabase> m_groups;
|
||||
|
||||
std::unordered_map<amuse::SongId, QString> m_midiFiles;
|
||||
struct Song
|
||||
{
|
||||
QString m_path;
|
||||
int m_refCount = 0;
|
||||
};
|
||||
std::unordered_map<amuse::SongId, Song> m_midiFiles;
|
||||
|
||||
public:
|
||||
class INode : public amuse::IObj
|
||||
|
@ -436,9 +419,12 @@ public:
|
|||
PageObjectProxyModel* getPageObjectProxy() { return &m_pageObjectProxy; }
|
||||
|
||||
GroupNode* getGroupOfSfx(amuse::SFXId id) const;
|
||||
GroupNode* getGroupOfSong(amuse::SongId id) const;
|
||||
QString getMIDIPathOfSong(amuse::SongId id) const;
|
||||
void setMIDIPathOfSong(amuse::SongId id, const QString& path);
|
||||
void _allocateSongId(amuse::SongId id, std::string_view name);
|
||||
std::pair<amuse::SongId, std::string> allocateSongId();
|
||||
void deallocateSongId(amuse::SongId oldId);
|
||||
amuse::SongId exchangeSongId(amuse::SongId oldId, std::string_view newName);
|
||||
|
||||
void setIdDatabases(INode* context) const;
|
||||
};
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
#include "ProjectStatistics.hpp"
|
||||
|
||||
ProjectStatistics::ProjectStatistics(QObject* parent)
|
||||
: QAbstractTableModel(parent)
|
||||
{
|
||||
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
#ifndef AMUSE_PROJECT_STATISTICS_HPP
|
||||
#define AMUSE_PROJECT_STATISTICS_HPP
|
||||
|
||||
#include <QAbstractTableModel>
|
||||
|
||||
class ProjectStatistics : public QAbstractTableModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ProjectStatistics(QObject* parent = Q_NULLPTR);
|
||||
};
|
||||
|
||||
|
||||
#endif //AMUSE_PROJECT_STATISTICS_HPP
|
|
@ -172,7 +172,17 @@ void SampleView::paintEvent(QPaintEvent* ev)
|
|||
qreal scale = -sampleHeight / 2.0;
|
||||
qreal trans = sampleHeight / 2.0;
|
||||
|
||||
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> lastAvgPeak;
|
||||
if (startSample >= deviceSamplesPerPx)
|
||||
{
|
||||
seekToSample(startSample - deviceSamplesPerPx);
|
||||
lastAvgPeak = iterateSampleInterval(deviceSamplesPerPx);
|
||||
}
|
||||
else
|
||||
{
|
||||
seekToSample(startSample);
|
||||
lastAvgPeak = std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>>{};
|
||||
}
|
||||
|
||||
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> avgPeak;
|
||||
for (qreal i = 0.0; i < deviceWidth; i += increment)
|
||||
|
@ -183,12 +193,18 @@ void SampleView::paintEvent(QPaintEvent* ev)
|
|||
avgPeak = iterateSampleInterval(deviceSamplesPerPx);
|
||||
else
|
||||
m_curSamplePos += deviceSamplesPerPx;
|
||||
std::pair<std::pair<qreal, qreal>, std::pair<qreal, qreal>> drawAvgPeak = avgPeak;
|
||||
if (lastAvgPeak.first.first > avgPeak.second.first)
|
||||
drawAvgPeak.second.first = lastAvgPeak.first.first;
|
||||
else if (lastAvgPeak.second.first < avgPeak.first.first)
|
||||
drawAvgPeak.first.first = lastAvgPeak.second.first;
|
||||
painter.setPen(peakPen);
|
||||
painter.drawLine(QPointF(rectStart + i, avgPeak.first.second * scale + trans),
|
||||
QPointF(rectStart + i, avgPeak.second.second * scale + trans));
|
||||
painter.drawLine(QPointF(rectStart + i, drawAvgPeak.first.second * scale + trans),
|
||||
QPointF(rectStart + i, drawAvgPeak.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));
|
||||
painter.drawLine(QPointF(rectStart + i, drawAvgPeak.first.first * scale + trans),
|
||||
QPointF(rectStart + i, drawAvgPeak.second.first * scale + trans));
|
||||
lastAvgPeak = avgPeak;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,232 @@
|
|||
#include "MainWindow.hpp"
|
||||
#include "amuse/SongConverter.hpp"
|
||||
|
||||
class PageDataChangeUndoCommand : public EditorUndoCommand
|
||||
{
|
||||
bool m_drum;
|
||||
uint8_t m_prog;
|
||||
int m_column;
|
||||
int m_undoVal, m_redoVal;
|
||||
bool m_undid = false;
|
||||
public:
|
||||
explicit PageDataChangeUndoCommand(ProjectModel::SongGroupNode* node, const QString& text,
|
||||
bool drum, uint8_t prog, int column, int redoVal)
|
||||
: EditorUndoCommand(node, text), m_drum(drum), m_prog(prog), m_column(column), m_redoVal(redoVal) {}
|
||||
void undo()
|
||||
{
|
||||
m_undid = true;
|
||||
amuse::SongGroupIndex& index = *static_cast<ProjectModel::SongGroupNode*>(m_node.get())->m_index;
|
||||
auto& map = m_drum ? index.m_drumPages : index.m_normPages;
|
||||
amuse::SongGroupIndex::PageEntry& entry = map[m_prog];
|
||||
|
||||
switch (m_column)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
auto nh = map.extract(m_prog);
|
||||
nh.key() = m_undoVal;
|
||||
m_prog = m_undoVal;
|
||||
map.insert(std::move(nh));
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
entry.objId.id = m_undoVal;
|
||||
break;
|
||||
case 2:
|
||||
entry.priority = m_undoVal;
|
||||
break;
|
||||
case 3:
|
||||
entry.maxVoices = m_undoVal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo()
|
||||
{
|
||||
amuse::SongGroupIndex& index = *static_cast<ProjectModel::SongGroupNode*>(m_node.get())->m_index;
|
||||
auto& map = m_drum ? index.m_drumPages : index.m_normPages;
|
||||
amuse::SongGroupIndex::PageEntry& entry = map[m_prog];
|
||||
|
||||
switch (m_column)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
auto nh = map.extract(m_prog);
|
||||
m_undoVal = m_prog;
|
||||
nh.key() = m_redoVal;
|
||||
m_prog = m_redoVal;
|
||||
map.insert(std::move(nh));
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
m_undoVal = entry.objId.id;
|
||||
entry.objId.id = m_redoVal;
|
||||
break;
|
||||
case 2:
|
||||
m_undoVal = entry.priority;
|
||||
entry.priority = m_redoVal;
|
||||
break;
|
||||
case 3:
|
||||
m_undoVal = entry.maxVoices;
|
||||
entry.maxVoices = m_redoVal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
};
|
||||
|
||||
class SetupDataChangeUndoCommand : public EditorUndoCommand
|
||||
{
|
||||
amuse::SongId m_song;
|
||||
int m_row, m_column;
|
||||
int m_undoVal, m_redoVal;
|
||||
bool m_undid = false;
|
||||
public:
|
||||
explicit SetupDataChangeUndoCommand(ProjectModel::SongGroupNode* node, const QString& text,
|
||||
amuse::SongId song, int row, int column, int redoVal)
|
||||
: EditorUndoCommand(node, text), m_song(song), m_row(row), m_column(column), m_redoVal(redoVal) {}
|
||||
void undo()
|
||||
{
|
||||
m_undid = true;
|
||||
amuse::SongGroupIndex& index = *static_cast<ProjectModel::SongGroupNode*>(m_node.get())->m_index;
|
||||
auto& map = index.m_midiSetups;
|
||||
std::array<amuse::SongGroupIndex::MIDISetup, 16>& entry = map[m_song];
|
||||
|
||||
switch (m_column)
|
||||
{
|
||||
case 0:
|
||||
entry[m_row].programNo = m_undoVal;
|
||||
break;
|
||||
case 2:
|
||||
entry[m_row].volume = m_undoVal;
|
||||
break;
|
||||
case 3:
|
||||
entry[m_row].panning = m_undoVal;
|
||||
break;
|
||||
case 4:
|
||||
entry[m_row].reverb = m_undoVal;
|
||||
break;
|
||||
case 5:
|
||||
entry[m_row].chorus = m_undoVal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo()
|
||||
{
|
||||
amuse::SongGroupIndex& index = *static_cast<ProjectModel::SongGroupNode*>(m_node.get())->m_index;
|
||||
auto& map = index.m_midiSetups;
|
||||
std::array<amuse::SongGroupIndex::MIDISetup, 16>& entry = map[m_song];
|
||||
|
||||
switch (m_column)
|
||||
{
|
||||
case 0:
|
||||
m_undoVal = entry[m_row].programNo;
|
||||
entry[m_row].programNo = m_redoVal;
|
||||
break;
|
||||
case 2:
|
||||
m_undoVal = entry[m_row].volume;
|
||||
entry[m_row].volume = m_redoVal;
|
||||
break;
|
||||
case 3:
|
||||
m_undoVal = entry[m_row].panning;
|
||||
entry[m_row].panning = m_redoVal;
|
||||
break;
|
||||
case 4:
|
||||
m_undoVal = entry[m_row].reverb;
|
||||
entry[m_row].reverb = m_redoVal;
|
||||
break;
|
||||
case 5:
|
||||
m_undoVal = entry[m_row].chorus;
|
||||
entry[m_row].chorus = m_redoVal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
};
|
||||
|
||||
class SongNameChangeUndoCommand : public EditorUndoCommand
|
||||
{
|
||||
amuse::SongId m_song;
|
||||
std::string m_undoVal, m_redoVal;
|
||||
bool m_undid = false;
|
||||
public:
|
||||
explicit SongNameChangeUndoCommand(ProjectModel::SongGroupNode* node, const QString& text,
|
||||
amuse::SongId song, std::string_view redoVal)
|
||||
: EditorUndoCommand(node, text), m_song(song), m_redoVal(redoVal) {}
|
||||
void undo()
|
||||
{
|
||||
m_undid = true;
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
amuse::SongGroupIndex& index = *static_cast<ProjectModel::SongGroupNode*>(m_node.get())->m_index;
|
||||
auto& map = index.m_midiSetups;
|
||||
|
||||
amuse::SongId newId = g_MainWindow->projectModel()->exchangeSongId(m_song, m_undoVal);
|
||||
|
||||
auto nh = map.extract(m_song);
|
||||
nh.key() = newId;
|
||||
m_song = newId;
|
||||
map.insert(std::move(nh));
|
||||
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo()
|
||||
{
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
amuse::SongGroupIndex& index = *static_cast<ProjectModel::SongGroupNode*>(m_node.get())->m_index;
|
||||
auto& map = index.m_midiSetups;
|
||||
|
||||
m_undoVal = amuse::SongId::CurNameDB->resolveNameFromId(m_song);
|
||||
amuse::SongId newId = g_MainWindow->projectModel()->exchangeSongId(m_song, m_redoVal);
|
||||
|
||||
auto nh = map.extract(m_song);
|
||||
nh.key() = newId;
|
||||
m_song = newId;
|
||||
map.insert(std::move(nh));
|
||||
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
};
|
||||
|
||||
class SongMIDIPathChangeUndoCommand : public EditorUndoCommand
|
||||
{
|
||||
amuse::SongId m_song;
|
||||
QString m_undoVal, m_redoVal;
|
||||
bool m_undid = false;
|
||||
public:
|
||||
explicit SongMIDIPathChangeUndoCommand(ProjectModel::SongGroupNode* node, const QString& text,
|
||||
amuse::SongId song, QString redoVal)
|
||||
: EditorUndoCommand(node, text), m_song(song), m_redoVal(redoVal) {}
|
||||
void undo()
|
||||
{
|
||||
m_undid = true;
|
||||
g_MainWindow->projectModel()->setMIDIPathOfSong(m_song, m_undoVal);
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo()
|
||||
{
|
||||
m_undoVal = g_MainWindow->projectModel()->getMIDIPathOfSong(m_song);
|
||||
g_MainWindow->projectModel()->setMIDIPathOfSong(m_song, m_redoVal);
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
};
|
||||
|
||||
PageObjectDelegate::PageObjectDelegate(QObject* parent)
|
||||
: QStyledItemDelegate(parent) {}
|
||||
|
||||
|
@ -33,19 +259,23 @@ void PageObjectDelegate::setModelData(QWidget* editor, QAbstractItemModel* m, co
|
|||
const PageModel* model = static_cast<const PageModel*>(m);
|
||||
auto entry = model->m_sorted[index.row()];
|
||||
int idx = static_cast<EditorFieldPageObjectNode*>(editor)->currentIndex();
|
||||
if (idx == 0)
|
||||
{
|
||||
entry->second.objId.id = amuse::ObjectId();
|
||||
}
|
||||
else
|
||||
amuse::ObjectId id;
|
||||
if (idx != 0)
|
||||
{
|
||||
ProjectModel::BasePoolObjectNode* node = static_cast<ProjectModel::BasePoolObjectNode*>(
|
||||
g_MainWindow->projectModel()->node(
|
||||
g_MainWindow->projectModel()->getPageObjectProxy()->mapToSource(
|
||||
g_MainWindow->projectModel()->getPageObjectProxy()->index(idx, 0,
|
||||
static_cast<EditorFieldPageObjectNode*>(editor)->rootModelIndex()))));
|
||||
entry->second.objId.id = node->id();
|
||||
id = node->id();
|
||||
}
|
||||
if (id == entry->second.objId.id)
|
||||
{
|
||||
emit m->dataChanged(index, index);
|
||||
return;
|
||||
}
|
||||
g_MainWindow->pushUndoCommand(new PageDataChangeUndoCommand(model->m_node.get(),
|
||||
tr("Change %1").arg(m->headerData(1, Qt::Horizontal).toString()), model->m_drum, entry->first, 1, id.id));
|
||||
emit m->dataChanged(index, index);
|
||||
}
|
||||
|
||||
|
@ -106,7 +336,10 @@ void MIDIFileDelegate::setModelData(QWidget* editor, QAbstractItemModel* m, cons
|
|||
MIDIFileFieldWidget* widget = static_cast<MIDIFileFieldWidget*>(editor);
|
||||
const SetupListModel* model = static_cast<const SetupListModel*>(index.model());
|
||||
auto entry = model->m_sorted[index.row()];
|
||||
g_MainWindow->projectModel()->setMIDIPathOfSong(entry->first, widget->path());
|
||||
if (g_MainWindow->projectModel()->getMIDIPathOfSong(entry->first) == widget->path())
|
||||
return;
|
||||
g_MainWindow->pushUndoCommand(new SongMIDIPathChangeUndoCommand(model->m_node.get(),
|
||||
tr("Change MIDI Path"), entry->first, widget->path()));
|
||||
emit m->dataChanged(index, index);
|
||||
}
|
||||
|
||||
|
@ -237,9 +470,10 @@ bool PageModel::setData(const QModelIndex& index, const QVariant& value, int rol
|
|||
return false;
|
||||
}
|
||||
emit layoutAboutToBeChanged();
|
||||
auto nh = map.extract(entry->first);
|
||||
nh.key() = value.toInt();
|
||||
map.insert(std::move(nh));
|
||||
|
||||
g_MainWindow->pushUndoCommand(new PageDataChangeUndoCommand(m_node.get(),
|
||||
tr("Change %1").arg(headerData(0, Qt::Horizontal).toString()), m_drum, entry->first, 0, value.toInt()));
|
||||
|
||||
_buildSortedList();
|
||||
QModelIndex newIndex = _indexOfProgram(value.toInt());
|
||||
changePersistentIndex(index, newIndex);
|
||||
|
@ -247,19 +481,28 @@ bool PageModel::setData(const QModelIndex& index, const QVariant& value, int rol
|
|||
emit dataChanged(newIndex, newIndex);
|
||||
return true;
|
||||
}
|
||||
case 2:
|
||||
entry->second.priority = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
case 3:
|
||||
entry->second.maxVoices = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
default:
|
||||
case 1:
|
||||
if (entry->second.objId.id == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 2:
|
||||
if (entry->second.priority == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 3:
|
||||
if (entry->second.maxVoices == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
g_MainWindow->pushUndoCommand(new PageDataChangeUndoCommand(m_node.get(),
|
||||
tr("Change %1").arg(headerData(index.column(), Qt::Horizontal).toString()), m_drum,
|
||||
entry->first, index.column(), value.toInt()));
|
||||
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariant PageModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
|
@ -292,64 +535,99 @@ Qt::ItemFlags PageModel::flags(const QModelIndex& index) const
|
|||
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
|
||||
}
|
||||
|
||||
bool PageModel::insertRows(int row, int count, const QModelIndex& parent)
|
||||
class PageRowUndoCommand : public EditorUndoCommand
|
||||
{
|
||||
protected:
|
||||
PageTableView* m_view;
|
||||
std::vector<std::pair<uint8_t, amuse::SongGroupIndex::PageEntry>> m_data;
|
||||
bool m_undid = false;
|
||||
void add()
|
||||
{
|
||||
m_view->selectionModel()->clearSelection();
|
||||
for (const auto& p : m_data)
|
||||
{
|
||||
int row = static_cast<PageModel*>(m_view->model())->_insertRow(p);
|
||||
m_view->selectionModel()->select(QItemSelection(
|
||||
m_view->model()->index(row, 0), m_view->model()->index(row, 3)),
|
||||
QItemSelectionModel::SelectCurrent);
|
||||
}
|
||||
}
|
||||
void del()
|
||||
{
|
||||
for (auto it = m_data.rbegin(); it != m_data.rend(); ++it)
|
||||
{
|
||||
*it = static_cast<PageModel*>(m_view->model())->_removeRow(it->first);
|
||||
}
|
||||
}
|
||||
void undo()
|
||||
{
|
||||
m_undid = true;
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo()
|
||||
{
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
public:
|
||||
explicit PageRowUndoCommand(ProjectModel::SongGroupNode* node, const QString& text, PageTableView* view,
|
||||
std::vector<std::pair<uint8_t, amuse::SongGroupIndex::PageEntry>>&& data)
|
||||
: EditorUndoCommand(node, text), m_view(view), m_data(std::move(data)) {}
|
||||
};
|
||||
|
||||
class PageRowAddUndoCommand : public PageRowUndoCommand
|
||||
{
|
||||
using base = PageRowUndoCommand;
|
||||
public:
|
||||
explicit PageRowAddUndoCommand(ProjectModel::SongGroupNode* node, const QString& text, PageTableView* view,
|
||||
std::vector<std::pair<uint8_t, amuse::SongGroupIndex::PageEntry>>&& data)
|
||||
: PageRowUndoCommand(node, text, view, std::move(data)) {}
|
||||
void undo() { base::undo(); base::del(); }
|
||||
void redo() { base::redo(); base::add(); }
|
||||
};
|
||||
|
||||
class PageRowDelUndoCommand : public PageRowUndoCommand
|
||||
{
|
||||
using base = PageRowUndoCommand;
|
||||
public:
|
||||
explicit PageRowDelUndoCommand(ProjectModel::SongGroupNode* node, const QString& text, PageTableView* view,
|
||||
std::vector<std::pair<uint8_t, amuse::SongGroupIndex::PageEntry>>&& data)
|
||||
: PageRowUndoCommand(node, text, view, std::move(data)) {}
|
||||
void undo() { base::undo(); base::add(); }
|
||||
void redo() { base::redo(); base::del(); }
|
||||
};
|
||||
|
||||
int PageModel::_insertRow(const std::pair<uint8_t, amuse::SongGroupIndex::PageEntry>& data)
|
||||
{
|
||||
if (!m_node)
|
||||
return false;
|
||||
if (m_sorted.size() >= 128)
|
||||
return false;
|
||||
return 0;
|
||||
auto& map = _getMap();
|
||||
if (m_sorted.empty())
|
||||
{
|
||||
beginInsertRows(parent, 0, count - 1);
|
||||
for (int i = 0; i < count; ++i)
|
||||
map.emplace(std::make_pair(i, amuse::SongGroupIndex::PageEntry{}));
|
||||
int idx = _hypotheticalIndexOfProgram(data.first);
|
||||
beginInsertRows(QModelIndex(), idx, idx);
|
||||
map.emplace(data);
|
||||
_buildSortedList();
|
||||
endInsertRows();
|
||||
return true;
|
||||
}
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
int prog = -1;
|
||||
if (row < m_sorted.size())
|
||||
{
|
||||
prog = m_sorted[row].m_it->first;
|
||||
while (prog >= 0 && _indexOfProgram(prog).isValid())
|
||||
--prog;
|
||||
}
|
||||
if (prog == -1)
|
||||
{
|
||||
prog = 0;
|
||||
while (prog < 128 && _indexOfProgram(prog).isValid())
|
||||
++prog;
|
||||
}
|
||||
if (prog == 128)
|
||||
return true;
|
||||
int insertIdx = _hypotheticalIndexOfProgram(prog);
|
||||
beginInsertRows(parent, insertIdx, insertIdx);
|
||||
map.emplace(std::make_pair(prog, amuse::SongGroupIndex::PageEntry{}));
|
||||
_buildSortedList();
|
||||
endInsertRows();
|
||||
++row;
|
||||
}
|
||||
return true;
|
||||
return idx;
|
||||
}
|
||||
|
||||
bool PageModel::removeRows(int row, int count, const QModelIndex& parent)
|
||||
std::pair<uint8_t, amuse::SongGroupIndex::PageEntry> PageModel::_removeRow(uint8_t prog)
|
||||
{
|
||||
std::pair<uint8_t, amuse::SongGroupIndex::PageEntry> ret;
|
||||
if (!m_node)
|
||||
return false;
|
||||
return ret;
|
||||
auto& map = _getMap();
|
||||
beginRemoveRows(parent, row, row + count - 1);
|
||||
std::vector<uint8_t> removeProgs;
|
||||
removeProgs.reserve(count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
removeProgs.push_back(m_sorted[row+i].m_it->first);
|
||||
for (uint8_t prog : removeProgs)
|
||||
map.erase(prog);
|
||||
int idx = _hypotheticalIndexOfProgram(prog);
|
||||
beginRemoveRows(QModelIndex(), idx, idx);
|
||||
ret.first = prog;
|
||||
auto search = map.find(prog);
|
||||
if (search != map.cend())
|
||||
{
|
||||
ret.second = search->second;
|
||||
map.erase(search);
|
||||
}
|
||||
_buildSortedList();
|
||||
endRemoveRows();
|
||||
return true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
PageModel::PageModel(bool drum, QObject* parent)
|
||||
|
@ -392,7 +670,7 @@ void SetupListModel::loadData(ProjectModel::SongGroupNode* node)
|
|||
{
|
||||
beginResetModel();
|
||||
m_node = node;
|
||||
g_MainWindow->projectModel()->getGroupNode(m_node.get())->getAudioGroup()->setIdDatabases();
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
_buildSortedList();
|
||||
endResetModel();
|
||||
}
|
||||
|
@ -434,7 +712,7 @@ QVariant SetupListModel::data(const QModelIndex& index, int role) const
|
|||
{
|
||||
if (index.column() == 0)
|
||||
{
|
||||
g_MainWindow->projectModel()->getGroupNode(m_node.get())->getAudioGroup()->setIdDatabases();
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
return amuse::SongId::CurNameDB->resolveNameFromId(entry->first.id).data();
|
||||
}
|
||||
else if (index.column() == 1)
|
||||
|
@ -451,10 +729,9 @@ bool SetupListModel::setData(const QModelIndex& index, const QVariant& value, in
|
|||
if (!m_node || role != Qt::EditRole || index.column() != 0)
|
||||
return false;
|
||||
|
||||
auto& map = _getMap();
|
||||
auto entry = m_sorted[index.row()];
|
||||
|
||||
g_MainWindow->projectModel()->getGroupNode(m_node.get())->getAudioGroup()->setIdDatabases();
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
auto utf8key = value.toString().toUtf8();
|
||||
std::unordered_map<std::string, amuse::ObjectId>::iterator idIt;
|
||||
if ((idIt = amuse::SongId::CurNameDB->m_stringToId.find(utf8key.data())) != amuse::SongId::CurNameDB->m_stringToId.cend())
|
||||
|
@ -466,7 +743,8 @@ bool SetupListModel::setData(const QModelIndex& index, const QVariant& value, in
|
|||
return false;
|
||||
}
|
||||
emit layoutAboutToBeChanged();
|
||||
amuse::SongId::CurNameDB->rename(entry.m_it->first, utf8key.data());
|
||||
g_MainWindow->pushUndoCommand(new SongNameChangeUndoCommand(m_node.get(),
|
||||
tr("Change Song Name"), entry.m_it->first, utf8key.data()));
|
||||
_buildSortedList();
|
||||
QModelIndex newIndex = _indexOfSong(entry.m_it->first);
|
||||
changePersistentIndex(index, newIndex);
|
||||
|
@ -500,46 +778,112 @@ Qt::ItemFlags SetupListModel::flags(const QModelIndex& index) const
|
|||
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
|
||||
}
|
||||
|
||||
bool SetupListModel::insertRows(int row, int count, const QModelIndex& parent)
|
||||
class SetupRowUndoCommand : public EditorUndoCommand
|
||||
{
|
||||
protected:
|
||||
SetupTableView* m_view;
|
||||
std::vector<std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>> m_data;
|
||||
bool m_undid = false;
|
||||
void add()
|
||||
{
|
||||
QTableView* listView = m_view->m_listView;
|
||||
listView->selectionModel()->clearSelection();
|
||||
for (auto& p : m_data)
|
||||
{
|
||||
int row = static_cast<SetupListModel*>(m_view->m_listView->model())->_insertRow(p);
|
||||
listView->selectionModel()->select(QItemSelection(
|
||||
listView->model()->index(row, 0), listView->model()->index(row, 1)),
|
||||
QItemSelectionModel::SelectCurrent);
|
||||
}
|
||||
}
|
||||
void del()
|
||||
{
|
||||
QTableView* listView = m_view->m_listView;
|
||||
for (auto it = m_data.rbegin(); it != m_data.rend(); ++it)
|
||||
{
|
||||
*it = static_cast<SetupListModel*>(listView->model())->_removeRow(std::get<0>(*it));
|
||||
}
|
||||
}
|
||||
void undo()
|
||||
{
|
||||
m_undid = true;
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo()
|
||||
{
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
public:
|
||||
explicit SetupRowUndoCommand(ProjectModel::SongGroupNode* node, const QString& text, SetupTableView* view,
|
||||
std::vector<std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>>&& data)
|
||||
: EditorUndoCommand(node, text), m_view(view), m_data(std::move(data)) {}
|
||||
};
|
||||
|
||||
class SetupRowAddUndoCommand : public SetupRowUndoCommand
|
||||
{
|
||||
using base = SetupRowUndoCommand;
|
||||
public:
|
||||
explicit SetupRowAddUndoCommand(ProjectModel::SongGroupNode* node, const QString& text, SetupTableView* view,
|
||||
std::vector<std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>>&& data)
|
||||
: SetupRowUndoCommand(node, text, view, std::move(data)) {}
|
||||
void undo() { base::undo(); base::del(); }
|
||||
void redo() { base::redo(); base::add(); }
|
||||
};
|
||||
|
||||
class SetupRowDelUndoCommand : public SetupRowUndoCommand
|
||||
{
|
||||
using base = SetupRowUndoCommand;
|
||||
public:
|
||||
explicit SetupRowDelUndoCommand(ProjectModel::SongGroupNode* node, const QString& text, SetupTableView* view,
|
||||
std::vector<std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>>&& data)
|
||||
: SetupRowUndoCommand(node, text, view, std::move(data)) {}
|
||||
void undo() { base::undo(); base::add(); }
|
||||
void redo() { base::redo(); base::del(); }
|
||||
};
|
||||
|
||||
int SetupListModel::_insertRow(
|
||||
std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>& data)
|
||||
{
|
||||
if (!m_node)
|
||||
return false;
|
||||
return 0;
|
||||
auto& map = _getMap();
|
||||
g_MainWindow->projectModel()->getGroupNode(m_node.get())->getAudioGroup()->setIdDatabases();
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
amuse::ObjectId songId = amuse::SongId::CurNameDB->generateId(amuse::NameDB::Type::Song);
|
||||
std::string songName = amuse::SongId::CurNameDB->generateName(songId, amuse::NameDB::Type::Song);
|
||||
int insertIdx = _hypotheticalIndexOfSong(songName);
|
||||
beginInsertRows(parent, insertIdx, insertIdx);
|
||||
amuse::SongId::CurNameDB->registerPair(songName, songId);
|
||||
map.emplace(std::make_pair(songId, std::array<amuse::SongGroupIndex::MIDISetup, 16>{}));
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
if (std::get<0>(data).id == 0xffff)
|
||||
std::get<0>(data) = amuse::SongId::CurNameDB->generateId(amuse::NameDB::Type::Song);
|
||||
|
||||
g_MainWindow->projectModel()->_allocateSongId(std::get<0>(data), std::get<1>(data));
|
||||
int idx = _hypotheticalIndexOfSong(std::get<1>(data));
|
||||
beginInsertRows(QModelIndex(), idx, idx);
|
||||
map[std::get<0>(data)] = std::get<2>(data);
|
||||
_buildSortedList();
|
||||
endInsertRows();
|
||||
++row;
|
||||
}
|
||||
return true;
|
||||
return idx;
|
||||
}
|
||||
|
||||
bool SetupListModel::removeRows(int row, int count, const QModelIndex& parent)
|
||||
std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>
|
||||
SetupListModel::_removeRow(amuse::SongId id)
|
||||
{
|
||||
std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>> ret = {};
|
||||
if (!m_node)
|
||||
return false;
|
||||
return ret;
|
||||
|
||||
auto& map = _getMap();
|
||||
g_MainWindow->projectModel()->getGroupNode(m_node.get())->getAudioGroup()->setIdDatabases();
|
||||
beginRemoveRows(parent, row, row + count - 1);
|
||||
std::vector<amuse::SongId> removeSongs;
|
||||
removeSongs.reserve(count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
removeSongs.push_back(m_sorted[row+i].m_it->first);
|
||||
for (amuse::SongId song : removeSongs)
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
std::get<0>(ret) = id;
|
||||
std::get<1>(ret) = amuse::SongId::CurNameDB->resolveNameFromId(id);
|
||||
int idx = _indexOfSong(id).row();
|
||||
beginRemoveRows(QModelIndex(), idx, idx);
|
||||
auto search = map.find(id);
|
||||
if (search != map.end())
|
||||
{
|
||||
amuse::SongId::CurNameDB->remove(song);
|
||||
map.erase(song);
|
||||
std::get<2>(ret) = search->second;
|
||||
g_MainWindow->projectModel()->deallocateSongId(id);
|
||||
map.erase(search);
|
||||
}
|
||||
_buildSortedList();
|
||||
endRemoveRows();
|
||||
return true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
SetupListModel::SetupListModel(QObject* parent)
|
||||
|
@ -615,30 +959,37 @@ bool SetupModel::setData(const QModelIndex& index, const QVariant& value, int ro
|
|||
switch (index.column())
|
||||
{
|
||||
case 0:
|
||||
entry.programNo = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
case 1:
|
||||
entry.volume = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
case 2:
|
||||
entry.panning = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
case 3:
|
||||
entry.reverb = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
case 4:
|
||||
entry.chorus = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
default:
|
||||
if (entry.programNo == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 1:
|
||||
if (entry.volume == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 2:
|
||||
if (entry.panning == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 3:
|
||||
if (entry.reverb == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 4:
|
||||
if (entry.chorus == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
g_MainWindow->pushUndoCommand(new SetupDataChangeUndoCommand(
|
||||
static_cast<SongGroupEditor*>(parent())->m_setupList.m_node.get(),
|
||||
tr("Change %1").arg(headerData(index.column(), Qt::Horizontal).toString()), m_data->first,
|
||||
index.row(), index.column(), value.toInt()));
|
||||
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariant SetupModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
|
@ -688,9 +1039,17 @@ SetupModel::SetupModel(QObject* parent)
|
|||
|
||||
void PageTableView::deleteSelection()
|
||||
{
|
||||
QModelIndexList list;
|
||||
while (!(list = selectionModel()->selectedRows()).isEmpty())
|
||||
model()->removeRow(list.back().row());
|
||||
QModelIndexList list = selectionModel()->selectedRows();
|
||||
if (list.isEmpty())
|
||||
return;
|
||||
std::vector<std::pair<uint8_t, amuse::SongGroupIndex::PageEntry>> data;
|
||||
data.reserve(list.size());
|
||||
for (QModelIndex idx : list)
|
||||
data.push_back(std::make_pair(model()->data(idx).toInt(), amuse::SongGroupIndex::PageEntry{}));
|
||||
std::sort(data.begin(), data.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
|
||||
g_MainWindow->pushUndoCommand(
|
||||
new PageRowDelUndoCommand(static_cast<PageModel*>(model())->m_node.get(),
|
||||
data.size() > 1 ? tr("Delete Page Entries") : tr("Delete Page Entry"), this, std::move(data)));
|
||||
}
|
||||
|
||||
void PageTableView::setModel(QAbstractItemModel* model)
|
||||
|
@ -740,9 +1099,20 @@ void SetupTableView::setModel(QAbstractItemModel* list, QAbstractItemModel* tabl
|
|||
|
||||
void SetupTableView::deleteSelection()
|
||||
{
|
||||
QModelIndexList list;
|
||||
while (!(list = m_listView->selectionModel()->selectedRows()).isEmpty())
|
||||
m_listView->model()->removeRow(list.back().row());
|
||||
QModelIndexList list = m_listView->selectionModel()->selectedRows();
|
||||
if (list.isEmpty())
|
||||
return;
|
||||
std::sort(list.begin(), list.end(), [](const auto& a, const auto& b) { return a.row() < b.row(); });
|
||||
std::vector<std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>> data;
|
||||
data.reserve(list.size());
|
||||
for (QModelIndex idx : list)
|
||||
{
|
||||
auto& entry = *static_cast<SetupListModel*>(m_listView->model())->m_sorted[idx.row()].m_it;
|
||||
data.push_back({entry.first, {}, {}});
|
||||
}
|
||||
g_MainWindow->pushUndoCommand(
|
||||
new SetupRowDelUndoCommand(static_cast<SetupListModel*>(m_listView->model())->m_node.get(),
|
||||
data.size() > 1 ? tr("Delete Setup Entries") : tr("Delete Setup Entry"), this, std::move(data)));
|
||||
}
|
||||
|
||||
void SetupTableView::showEvent(QShowEvent* event)
|
||||
|
@ -898,20 +1268,49 @@ void SongGroupEditor::doAdd()
|
|||
if (PageTableView* table = qobject_cast<PageTableView*>(m_tabs.currentWidget()))
|
||||
{
|
||||
QModelIndex idx = table->selectionModel()->currentIndex();
|
||||
int row;
|
||||
if (!idx.isValid())
|
||||
table->model()->insertRow(table->model()->rowCount() - 1);
|
||||
row = table->model()->rowCount() - 1;
|
||||
else
|
||||
table->model()->insertRow(idx.row());
|
||||
if (PageTableView* ctable = qobject_cast<PageTableView*>(table))
|
||||
m_addRemoveButtons.addAction()->setDisabled(ctable->model()->rowCount() >= 128);
|
||||
row = idx.row();
|
||||
|
||||
PageModel* model = static_cast<PageModel*>(table->model());
|
||||
int prog = 0;
|
||||
if (!model->m_sorted.empty())
|
||||
{
|
||||
prog = -1;
|
||||
if (row < model->m_sorted.size())
|
||||
{
|
||||
prog = model->m_sorted[row].m_it->first;
|
||||
while (prog >= 0 && model->_indexOfProgram(prog).isValid())
|
||||
--prog;
|
||||
}
|
||||
if (prog == -1)
|
||||
{
|
||||
prog = 0;
|
||||
while (prog < 128 && model->_indexOfProgram(prog).isValid())
|
||||
++prog;
|
||||
}
|
||||
if (prog == 128)
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::pair<uint8_t, amuse::SongGroupIndex::PageEntry>> data;
|
||||
data.push_back(std::make_pair(prog, amuse::SongGroupIndex::PageEntry{}));
|
||||
g_MainWindow->pushUndoCommand(
|
||||
new PageRowAddUndoCommand(model->m_node.get(), tr("Add Page Entry"), table, std::move(data)));
|
||||
|
||||
m_addRemoveButtons.addAction()->setDisabled(table->model()->rowCount() >= 128);
|
||||
}
|
||||
else if (SetupTableView* table = qobject_cast<SetupTableView*>(m_tabs.currentWidget()))
|
||||
{
|
||||
QModelIndex idx = table->m_listView->selectionModel()->currentIndex();
|
||||
if (!idx.isValid())
|
||||
table->m_listView->model()->insertRow(table->m_listView->model()->rowCount() - 1);
|
||||
else
|
||||
table->m_listView->model()->insertRow(idx.row());
|
||||
SetupListModel* model = static_cast<SetupListModel*>(table->m_listView->model());
|
||||
g_MainWindow->projectModel()->setIdDatabases(model->m_node.get());
|
||||
std::vector<std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>> data;
|
||||
auto songId = g_MainWindow->projectModel()->allocateSongId();
|
||||
data.push_back(std::make_tuple(songId.first, songId.second, std::array<amuse::SongGroupIndex::MIDISetup, 16>{}));
|
||||
g_MainWindow->pushUndoCommand(
|
||||
new SetupRowAddUndoCommand(model->m_node.get(), tr("Add Setup Entry"), table, std::move(data)));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -62,6 +62,7 @@ class PageModel : public QAbstractTableModel
|
|||
Q_OBJECT
|
||||
friend class SongGroupEditor;
|
||||
friend class PageObjectDelegate;
|
||||
friend class PageTableView;
|
||||
amuse::ObjToken<ProjectModel::SongGroupNode> m_node;
|
||||
struct Iterator
|
||||
{
|
||||
|
@ -90,8 +91,8 @@ public:
|
|||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||
|
||||
bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex());
|
||||
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex());
|
||||
int _insertRow(const std::pair<uint8_t, amuse::SongGroupIndex::PageEntry>& data);
|
||||
std::pair<uint8_t, amuse::SongGroupIndex::PageEntry> _removeRow(uint8_t prog);
|
||||
};
|
||||
|
||||
class SetupListModel : public QAbstractTableModel
|
||||
|
@ -99,6 +100,8 @@ class SetupListModel : public QAbstractTableModel
|
|||
Q_OBJECT
|
||||
friend class SongGroupEditor;
|
||||
friend class MIDIFileDelegate;
|
||||
friend class SetupTableView;
|
||||
friend class SetupModel;
|
||||
amuse::ObjToken<ProjectModel::SongGroupNode> m_node;
|
||||
struct Iterator
|
||||
{
|
||||
|
@ -138,8 +141,10 @@ public:
|
|||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||
|
||||
bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex());
|
||||
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex());
|
||||
int _insertRow(
|
||||
std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>& data);
|
||||
std::tuple<amuse::SongId, std::string, std::array<amuse::SongGroupIndex::MIDISetup, 16>>
|
||||
_removeRow(amuse::SongId id);
|
||||
};
|
||||
|
||||
class SetupModel : public QAbstractTableModel
|
||||
|
@ -177,6 +182,7 @@ class SetupTableView : public QSplitter
|
|||
{
|
||||
Q_OBJECT
|
||||
friend class SongGroupEditor;
|
||||
friend class SetupRowUndoCommand;
|
||||
QTableView* m_listView;
|
||||
QTableView* m_tableView;
|
||||
MIDIFileDelegate m_midiDelegate;
|
||||
|
@ -239,6 +245,7 @@ public slots:
|
|||
class SongGroupEditor : public EditorWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
friend class SetupModel;
|
||||
PageModel m_normPages;
|
||||
PageModel m_drumPages;
|
||||
SetupListModel m_setupList;
|
||||
|
|
|
@ -1,6 +1,124 @@
|
|||
#include "SoundGroupEditor.hpp"
|
||||
#include "MainWindow.hpp"
|
||||
|
||||
class SFXDataChangeUndoCommand : public EditorUndoCommand
|
||||
{
|
||||
amuse::SFXId m_sfx;
|
||||
int m_column;
|
||||
int m_undoVal, m_redoVal;
|
||||
bool m_undid = false;
|
||||
public:
|
||||
explicit SFXDataChangeUndoCommand(ProjectModel::SoundGroupNode* node, const QString& text,
|
||||
amuse::SFXId sfx, int column, int redoVal)
|
||||
: EditorUndoCommand(node, text), m_sfx(sfx), m_column(column), m_redoVal(redoVal) {}
|
||||
void undo()
|
||||
{
|
||||
m_undid = true;
|
||||
amuse::SFXGroupIndex& index = *static_cast<ProjectModel::SoundGroupNode*>(m_node.get())->m_index;
|
||||
auto& map = index.m_sfxEntries;
|
||||
amuse::SFXGroupIndex::SFXEntry& entry = map[m_sfx];
|
||||
|
||||
switch (m_column)
|
||||
{
|
||||
case 1:
|
||||
entry.objId.id = m_undoVal;
|
||||
break;
|
||||
case 2:
|
||||
entry.priority = m_undoVal;
|
||||
break;
|
||||
case 3:
|
||||
entry.maxVoices = m_undoVal;
|
||||
break;
|
||||
case 4:
|
||||
entry.defVel = m_undoVal;
|
||||
break;
|
||||
case 5:
|
||||
entry.panning = m_undoVal;
|
||||
break;
|
||||
case 6:
|
||||
entry.defKey = m_undoVal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo()
|
||||
{
|
||||
amuse::SFXGroupIndex& index = *static_cast<ProjectModel::SoundGroupNode*>(m_node.get())->m_index;
|
||||
auto& map = index.m_sfxEntries;
|
||||
amuse::SFXGroupIndex::SFXEntry& entry = map[m_sfx];
|
||||
|
||||
switch (m_column)
|
||||
{
|
||||
case 1:
|
||||
m_undoVal = entry.objId.id;
|
||||
entry.objId.id = m_redoVal;
|
||||
break;
|
||||
case 2:
|
||||
m_undoVal = entry.priority;
|
||||
entry.priority = m_redoVal;
|
||||
break;
|
||||
case 3:
|
||||
m_undoVal = entry.maxVoices;
|
||||
entry.maxVoices = m_redoVal;
|
||||
break;
|
||||
case 4:
|
||||
m_undoVal = entry.defVel;
|
||||
entry.defVel = m_redoVal;
|
||||
break;
|
||||
case 5:
|
||||
m_undoVal = entry.panning;
|
||||
entry.panning = m_redoVal;
|
||||
break;
|
||||
case 6:
|
||||
m_undoVal = entry.defKey;
|
||||
entry.defKey = m_redoVal;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
};
|
||||
|
||||
class SFXNameChangeUndoCommand : public EditorUndoCommand
|
||||
{
|
||||
amuse::SFXId m_sfx;
|
||||
std::string m_undoVal, m_redoVal;
|
||||
bool m_undid = false;
|
||||
public:
|
||||
explicit SFXNameChangeUndoCommand(ProjectModel::SoundGroupNode* node, const QString& text,
|
||||
amuse::SFXId sfx, std::string_view redoVal)
|
||||
: EditorUndoCommand(node, text), m_sfx(sfx), m_redoVal(redoVal) {}
|
||||
void undo()
|
||||
{
|
||||
m_undid = true;
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
amuse::SFXGroupIndex& index = *static_cast<ProjectModel::SoundGroupNode*>(m_node.get())->m_index;
|
||||
auto& map = index.m_sfxEntries;
|
||||
|
||||
amuse::SFXId::CurNameDB->rename(m_sfx, m_undoVal);
|
||||
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo()
|
||||
{
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
amuse::SFXGroupIndex& index = *static_cast<ProjectModel::SoundGroupNode*>(m_node.get())->m_index;
|
||||
auto& map = index.m_sfxEntries;
|
||||
|
||||
m_undoVal = amuse::SFXId::CurNameDB->resolveNameFromId(m_sfx);
|
||||
amuse::SFXId::CurNameDB->rename(m_sfx, m_redoVal);
|
||||
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
};
|
||||
|
||||
SFXObjectDelegate::SFXObjectDelegate(QObject* parent)
|
||||
: QStyledItemDelegate(parent) {}
|
||||
|
||||
|
@ -32,19 +150,23 @@ void SFXObjectDelegate::setModelData(QWidget* editor, QAbstractItemModel* m, con
|
|||
const SFXModel* model = static_cast<const SFXModel*>(m);
|
||||
auto entry = model->m_sorted[index.row()];
|
||||
int idx = static_cast<EditorFieldPageObjectNode*>(editor)->currentIndex();
|
||||
if (idx == 0)
|
||||
{
|
||||
entry->second.objId.id = amuse::ObjectId();
|
||||
}
|
||||
else
|
||||
amuse::ObjectId id;
|
||||
if (idx != 0)
|
||||
{
|
||||
ProjectModel::BasePoolObjectNode* node = static_cast<ProjectModel::BasePoolObjectNode*>(
|
||||
g_MainWindow->projectModel()->node(
|
||||
g_MainWindow->projectModel()->getPageObjectProxy()->mapToSource(
|
||||
g_MainWindow->projectModel()->getPageObjectProxy()->index(idx, 0,
|
||||
static_cast<EditorFieldPageObjectNode*>(editor)->rootModelIndex()))));
|
||||
entry->second.objId.id = node->id();
|
||||
id = node->id();
|
||||
}
|
||||
if (id == entry->second.objId.id)
|
||||
{
|
||||
emit m->dataChanged(index, index);
|
||||
return;
|
||||
}
|
||||
g_MainWindow->pushUndoCommand(new SFXDataChangeUndoCommand(model->m_node.get(),
|
||||
tr("Change %1").arg(m->headerData(1, Qt::Horizontal).toString()), entry->first, 1, id.id));
|
||||
emit m->dataChanged(index, index);
|
||||
}
|
||||
|
||||
|
@ -132,7 +254,7 @@ QVariant SFXModel::data(const QModelIndex& index, int role) const
|
|||
{
|
||||
case 0:
|
||||
{
|
||||
g_MainWindow->projectModel()->getGroupNode(m_node.get())->getAudioGroup()->setIdDatabases();
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
return amuse::SFXId::CurNameDB->resolveNameFromId(entry->first.id).data();
|
||||
}
|
||||
case 1:
|
||||
|
@ -172,7 +294,7 @@ bool SFXModel::setData(const QModelIndex& index, const QVariant& value, int role
|
|||
{
|
||||
case 0:
|
||||
{
|
||||
g_MainWindow->projectModel()->getGroupNode(m_node.get())->getAudioGroup()->setIdDatabases();
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
auto utf8key = value.toString().toUtf8();
|
||||
std::unordered_map<std::string, amuse::ObjectId>::iterator idIt;
|
||||
if ((idIt = amuse::SFXId::CurNameDB->m_stringToId.find(utf8key.data())) != amuse::SFXId::CurNameDB->m_stringToId.cend())
|
||||
|
@ -184,39 +306,45 @@ bool SFXModel::setData(const QModelIndex& index, const QVariant& value, int role
|
|||
return false;
|
||||
}
|
||||
emit layoutAboutToBeChanged();
|
||||
amuse::SFXId::CurNameDB->rename(entry.m_it->first, utf8key.data());
|
||||
g_MainWindow->pushUndoCommand(new SFXNameChangeUndoCommand(m_node.get(),
|
||||
tr("Change SFX Name"), entry->first, utf8key.data()));
|
||||
_buildSortedList();
|
||||
QModelIndex newIndex = _indexOfSFX(entry.m_it->first);
|
||||
QModelIndex newIndex = _indexOfSFX(entry->first);
|
||||
changePersistentIndex(index, newIndex);
|
||||
emit layoutChanged();
|
||||
emit dataChanged(newIndex, newIndex, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
}
|
||||
case 2:
|
||||
entry->second.priority = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
case 3:
|
||||
entry->second.maxVoices = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
case 4:
|
||||
entry->second.defVel = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
case 5:
|
||||
entry->second.panning = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
case 6:
|
||||
entry->second.defKey = value.toInt();
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
return true;
|
||||
default:
|
||||
if (entry->second.priority == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 3:
|
||||
if (entry->second.maxVoices == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 4:
|
||||
if (entry->second.defVel == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 5:
|
||||
if (entry->second.panning == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
case 6:
|
||||
if (entry->second.defKey == value.toInt())
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
g_MainWindow->pushUndoCommand(new SFXDataChangeUndoCommand(m_node.get(),
|
||||
tr("Change %1").arg(headerData(index.column(), Qt::Horizontal).toString()),
|
||||
entry->first, index.column(), value.toInt()));
|
||||
emit dataChanged(index, index, {Qt::DisplayRole, Qt::EditRole});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QVariant SFXModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
|
@ -255,42 +383,104 @@ Qt::ItemFlags SFXModel::flags(const QModelIndex& index) const
|
|||
return Qt::ItemIsSelectable | Qt::ItemIsEnabled | Qt::ItemIsEditable;
|
||||
}
|
||||
|
||||
bool SFXModel::insertRows(int row, int count, const QModelIndex& parent)
|
||||
class SFXRowUndoCommand : public EditorUndoCommand
|
||||
{
|
||||
protected:
|
||||
SFXTableView* m_view;
|
||||
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>> m_data;
|
||||
bool m_undid = false;
|
||||
void add()
|
||||
{
|
||||
m_view->selectionModel()->clearSelection();
|
||||
for (auto& p : m_data)
|
||||
{
|
||||
int row = static_cast<SFXModel*>(m_view->model())->_insertRow(p);
|
||||
m_view->selectionModel()->select(QItemSelection(
|
||||
m_view->model()->index(row, 0), m_view->model()->index(row, 6)),
|
||||
QItemSelectionModel::SelectCurrent);
|
||||
}
|
||||
}
|
||||
void del()
|
||||
{
|
||||
for (auto it = m_data.rbegin(); it != m_data.rend(); ++it)
|
||||
{
|
||||
*it = static_cast<SFXModel*>(m_view->model())->_removeRow(std::get<0>(*it));
|
||||
}
|
||||
}
|
||||
void undo()
|
||||
{
|
||||
m_undid = true;
|
||||
EditorUndoCommand::undo();
|
||||
}
|
||||
void redo()
|
||||
{
|
||||
if (m_undid)
|
||||
EditorUndoCommand::redo();
|
||||
}
|
||||
public:
|
||||
explicit SFXRowUndoCommand(ProjectModel::SoundGroupNode* node, const QString& text, SFXTableView* view,
|
||||
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>>&& data)
|
||||
: EditorUndoCommand(node, text), m_view(view), m_data(std::move(data)) {}
|
||||
};
|
||||
|
||||
class SFXRowAddUndoCommand : public SFXRowUndoCommand
|
||||
{
|
||||
using base = SFXRowUndoCommand;
|
||||
public:
|
||||
explicit SFXRowAddUndoCommand(ProjectModel::SoundGroupNode* node, const QString& text, SFXTableView* view,
|
||||
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>>&& data)
|
||||
: SFXRowUndoCommand(node, text, view, std::move(data)) {}
|
||||
void undo() { base::undo(); base::del(); }
|
||||
void redo() { base::redo(); base::add(); }
|
||||
};
|
||||
|
||||
class SFXRowDelUndoCommand : public SFXRowUndoCommand
|
||||
{
|
||||
using base = SFXRowUndoCommand;
|
||||
public:
|
||||
explicit SFXRowDelUndoCommand(ProjectModel::SoundGroupNode* node, const QString& text, SFXTableView* view,
|
||||
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>>&& data)
|
||||
: SFXRowUndoCommand(node, text, view, std::move(data)) {}
|
||||
void undo() { base::undo(); base::add(); }
|
||||
void redo() { base::redo(); base::del(); }
|
||||
};
|
||||
|
||||
int SFXModel::_insertRow(const std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>& data)
|
||||
{
|
||||
if (!m_node)
|
||||
return false;
|
||||
return 0;
|
||||
auto& map = _getMap();
|
||||
g_MainWindow->projectModel()->getGroupNode(m_node.get())->getAudioGroup()->setIdDatabases();
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
amuse::ObjectId sfxId = amuse::SFXId::CurNameDB->generateId(amuse::NameDB::Type::SFX);
|
||||
std::string sfxName = amuse::SFXId::CurNameDB->generateName(sfxId, amuse::NameDB::Type::SFX);
|
||||
int insertIdx = _hypotheticalIndexOfSFX(sfxName);
|
||||
beginInsertRows(parent, insertIdx, insertIdx);
|
||||
amuse::SFXId::CurNameDB->registerPair(sfxName, sfxId);
|
||||
map.emplace(std::make_pair(sfxId, amuse::SFXGroupIndex::SFXEntry{}));
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
amuse::SFXId::CurNameDB->registerPair(std::get<1>(data), std::get<0>(data));
|
||||
int idx = _hypotheticalIndexOfSFX(std::get<1>(data));
|
||||
beginInsertRows(QModelIndex(), idx, idx);
|
||||
map.emplace(std::make_pair(std::get<0>(data), std::get<2>(data)));
|
||||
_buildSortedList();
|
||||
endInsertRows();
|
||||
++row;
|
||||
}
|
||||
return true;
|
||||
return idx;
|
||||
}
|
||||
|
||||
bool SFXModel::removeRows(int row, int count, const QModelIndex& parent)
|
||||
std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry> SFXModel::_removeRow(amuse::SFXId sfx)
|
||||
{
|
||||
std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry> ret;
|
||||
if (!m_node)
|
||||
return false;
|
||||
return ret;
|
||||
auto& map = _getMap();
|
||||
beginRemoveRows(parent, row, row + count - 1);
|
||||
std::vector<amuse::SFXId> removeSFXs;
|
||||
removeSFXs.reserve(count);
|
||||
for (int i = 0; i < count; ++i)
|
||||
removeSFXs.push_back(m_sorted[row+i].m_it->first);
|
||||
for (amuse::SFXId sfx : removeSFXs)
|
||||
map.erase(sfx);
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_node.get());
|
||||
int idx = _indexOfSFX(sfx).row();
|
||||
beginRemoveRows(QModelIndex(), idx, idx);
|
||||
std::get<0>(ret) = sfx;
|
||||
std::get<1>(ret) = amuse::SFXId::CurNameDB->resolveNameFromId(sfx);
|
||||
auto search = map.find(sfx);
|
||||
if (search != map.cend())
|
||||
{
|
||||
std::get<2>(ret) = search->second;
|
||||
amuse::SFXId::CurNameDB->remove(sfx);
|
||||
map.erase(search);
|
||||
}
|
||||
_buildSortedList();
|
||||
endRemoveRows();
|
||||
return true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
SFXModel::SFXModel(QObject* parent)
|
||||
|
@ -299,9 +489,20 @@ SFXModel::SFXModel(QObject* parent)
|
|||
|
||||
void SFXTableView::deleteSelection()
|
||||
{
|
||||
QModelIndexList list;
|
||||
while (!(list = selectionModel()->selectedRows()).isEmpty())
|
||||
model()->removeRow(list.back().row());
|
||||
QModelIndexList list = selectionModel()->selectedRows();
|
||||
if (list.isEmpty())
|
||||
return;
|
||||
std::sort(list.begin(), list.end(), [](const auto& a, const auto& b) { return a.row() < b.row(); });
|
||||
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>> data;
|
||||
data.reserve(list.size());
|
||||
for (QModelIndex idx : list)
|
||||
{
|
||||
auto& entry = *static_cast<SFXModel*>(model())->m_sorted[idx.row()].m_it;
|
||||
data.push_back({entry.first, {}, {}});
|
||||
}
|
||||
g_MainWindow->pushUndoCommand(
|
||||
new SFXRowDelUndoCommand(static_cast<SFXModel*>(model())->m_node.get(),
|
||||
data.size() > 1 ? tr("Delete SFX Entries") : tr("Delete SFX Entry"), this, std::move(data)));
|
||||
}
|
||||
|
||||
void SFXTableView::setModel(QAbstractItemModel* model)
|
||||
|
@ -420,11 +621,13 @@ bool SoundGroupEditor::isItemEditEnabled() const
|
|||
|
||||
void SoundGroupEditor::doAdd()
|
||||
{
|
||||
QModelIndex idx = m_sfxTable->selectionModel()->currentIndex();
|
||||
if (!idx.isValid())
|
||||
m_sfxTable->model()->insertRow(m_sfxTable->model()->rowCount() - 1);
|
||||
else
|
||||
m_sfxTable->model()->insertRow(idx.row());
|
||||
g_MainWindow->projectModel()->setIdDatabases(m_sfxs.m_node.get());
|
||||
std::vector<std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>> data;
|
||||
amuse::SFXId sfxId = amuse::SFXId::CurNameDB->generateId(amuse::NameDB::Type::SFX);
|
||||
std::string sfxName = amuse::SFXId::CurNameDB->generateName(sfxId, amuse::NameDB::Type::SFX);
|
||||
data.push_back(std::make_tuple(sfxId, sfxName, amuse::SFXGroupIndex::SFXEntry{}));
|
||||
g_MainWindow->pushUndoCommand(
|
||||
new SFXRowAddUndoCommand(m_sfxs.m_node.get(), tr("Add SFX Entry"), m_sfxTable, std::move(data)));
|
||||
}
|
||||
|
||||
void SoundGroupEditor::doSelectionChanged()
|
||||
|
@ -440,9 +643,9 @@ void SoundGroupEditor::sfxDataChanged()
|
|||
{
|
||||
QModelIndex index = m_sfxs.index(idx, 1);
|
||||
SFXPlayerWidget* w = qobject_cast<SFXPlayerWidget*>(m_sfxTable->indexWidget(index));
|
||||
if (!w || w->sfxId() != p.m_it->first)
|
||||
if (!w || w->sfxId() != p->first)
|
||||
{
|
||||
SFXPlayerWidget* newW = new SFXPlayerWidget(index, m_sfxs.m_node->m_id, p.m_it->first);
|
||||
SFXPlayerWidget* newW = new SFXPlayerWidget(index, m_sfxs.m_node->m_id, p->first);
|
||||
m_sfxTable->setIndexWidget(index, newW);
|
||||
}
|
||||
++idx;
|
||||
|
|
|
@ -23,13 +23,14 @@ class SFXModel : public QAbstractTableModel
|
|||
Q_OBJECT
|
||||
friend class SoundGroupEditor;
|
||||
friend class SFXObjectDelegate;
|
||||
friend class SFXTableView;
|
||||
amuse::ObjToken<ProjectModel::SoundGroupNode> m_node;
|
||||
struct Iterator
|
||||
{
|
||||
using ItTp = std::unordered_map<amuse::SFXId, amuse::SFXGroupIndex::SFXEntry>::iterator;
|
||||
ItTp m_it;
|
||||
Iterator(ItTp it) : m_it(it) {}
|
||||
ItTp::pointer operator->() { return m_it.operator->(); }
|
||||
ItTp::pointer operator->() const { return m_it.operator->(); }
|
||||
bool operator<(const Iterator& other) const
|
||||
{
|
||||
return amuse::SFXId::CurNameDB->resolveNameFromId(m_it->first) <
|
||||
|
@ -62,8 +63,8 @@ public:
|
|||
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||
Qt::ItemFlags flags(const QModelIndex& index) const;
|
||||
|
||||
bool insertRows(int row, int count, const QModelIndex& parent = QModelIndex());
|
||||
bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex());
|
||||
int _insertRow(const std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry>& data);
|
||||
std::tuple<amuse::SFXId, std::string, amuse::SFXGroupIndex::SFXEntry> _removeRow(amuse::SFXId sfx);
|
||||
};
|
||||
|
||||
class SFXTableView : public QTableView
|
||||
|
|
|
@ -197,12 +197,17 @@
|
|||
<context>
|
||||
<name>LayersEditor</name>
|
||||
<message>
|
||||
<location filename="../LayersEditor.cpp" line="443"/>
|
||||
<location filename="../LayersEditor.cpp" line="630"/>
|
||||
<source>Add Layer</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../LayersEditor.cpp" line="671"/>
|
||||
<source>Add new layer mapping</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../LayersEditor.cpp" line="445"/>
|
||||
<location filename="../LayersEditor.cpp" line="673"/>
|
||||
<source>Remove selected layer mappings</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -210,55 +215,91 @@
|
|||
<context>
|
||||
<name>LayersModel</name>
|
||||
<message>
|
||||
<location filename="../LayersEditor.cpp" line="172"/>
|
||||
<location filename="../LayersEditor.cpp" line="263"/>
|
||||
<source>Change %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../LayersEditor.cpp" line="276"/>
|
||||
<source>SoundMacro</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../LayersEditor.cpp" line="174"/>
|
||||
<location filename="../LayersEditor.cpp" line="278"/>
|
||||
<source>Key Lo</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../LayersEditor.cpp" line="176"/>
|
||||
<location filename="../LayersEditor.cpp" line="280"/>
|
||||
<source>Key Hi</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../LayersEditor.cpp" line="178"/>
|
||||
<location filename="../LayersEditor.cpp" line="282"/>
|
||||
<source>Transpose</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../LayersEditor.cpp" line="180"/>
|
||||
<location filename="../LayersEditor.cpp" line="284"/>
|
||||
<source>Volume</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../LayersEditor.cpp" line="182"/>
|
||||
<location filename="../LayersEditor.cpp" line="286"/>
|
||||
<source>Prio Off</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../LayersEditor.cpp" line="184"/>
|
||||
<location filename="../LayersEditor.cpp" line="288"/>
|
||||
<source>Span</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../LayersEditor.cpp" line="186"/>
|
||||
<location filename="../LayersEditor.cpp" line="290"/>
|
||||
<source>Pan</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../LayersEditor.cpp" line="391"/>
|
||||
<source>Move Layers</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../LayersEditor.cpp" line="391"/>
|
||||
<source>Move Layer</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>LayersTableView</name>
|
||||
<message>
|
||||
<location filename="../LayersEditor.cpp" line="552"/>
|
||||
<source>Delete Layers</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../LayersEditor.cpp" line="552"/>
|
||||
<source>Delete Layer</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MIDIFileDelegate</name>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="342"/>
|
||||
<source>Change MIDI Path</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>MIDIFileFieldWidget</name>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="73"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="303"/>
|
||||
<source>Browse</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="74"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="304"/>
|
||||
<source>Open Song File</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -266,13 +307,13 @@
|
|||
<context>
|
||||
<name>MIDIPlayerWidget</name>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="823"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="1193"/>
|
||||
<source>Stop</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="837"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="860"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="1207"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="1230"/>
|
||||
<source>Play</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -291,152 +332,152 @@
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="296"/>
|
||||
<location filename="../MainWindow.ui" line="302"/>
|
||||
<source>&File</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="303"/>
|
||||
<location filename="../MainWindow.ui" line="309"/>
|
||||
<source>Recent &Projects</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="319"/>
|
||||
<location filename="../MainWindow.ui" line="325"/>
|
||||
<source>Pro&ject</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="334"/>
|
||||
<location filename="../MainWindow.ui" line="340"/>
|
||||
<source>&Audio</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="341"/>
|
||||
<location filename="../MainWindow.ui" line="347"/>
|
||||
<source>&MIDI</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="348"/>
|
||||
<location filename="../MainWindow.ui" line="354"/>
|
||||
<source>&Edit</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="364"/>
|
||||
<location filename="../MainWindow.ui" line="370"/>
|
||||
<source>&New Project</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="369"/>
|
||||
<location filename="../MainWindow.ui" line="375"/>
|
||||
<source>&Open Project</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="377"/>
|
||||
<location filename="../MainWindow.ui" line="383"/>
|
||||
<source>&Cut</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="385"/>
|
||||
<location filename="../MainWindow.ui" line="391"/>
|
||||
<source>C&opy</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="393"/>
|
||||
<location filename="../MainWindow.ui" line="399"/>
|
||||
<source>&Paste</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="401"/>
|
||||
<location filename="../MainWindow.ui" line="407"/>
|
||||
<source>&Delete</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="406"/>
|
||||
<location filename="../MainWindow.ui" line="412"/>
|
||||
<source>&Import Groups</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="409"/>
|
||||
<location filename="../MainWindow.ui" line="415"/>
|
||||
<source>Ctrl+I</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="421"/>
|
||||
<location filename="../MainWindow.ui" line="427"/>
|
||||
<source>New SF&X Group</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="433"/>
|
||||
<location filename="../MainWindow.ui" line="439"/>
|
||||
<source>New Son&g Group</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="445"/>
|
||||
<location filename="../MainWindow.ui" line="451"/>
|
||||
<source>New Sound &Macro</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="457"/>
|
||||
<location filename="../MainWindow.ui" line="463"/>
|
||||
<source>New &Keymap</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="469"/>
|
||||
<location filename="../MainWindow.ui" line="475"/>
|
||||
<source>New &Layers</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="477"/>
|
||||
<location filename="../MainWindow.ui" line="483"/>
|
||||
<source>&Output Device:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="485"/>
|
||||
<location filename="../MainWindow.ui" line="491"/>
|
||||
<source>&Input Device:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="493"/>
|
||||
<location filename="../MainWindow.ui" line="499"/>
|
||||
<source>&Export GameCube Groups</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="496"/>
|
||||
<location filename="../MainWindow.ui" line="502"/>
|
||||
<source>Ctrl+E</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="508"/>
|
||||
<location filename="../MainWindow.ui" line="514"/>
|
||||
<source>&New Subproject</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="520"/>
|
||||
<location filename="../MainWindow.ui" line="526"/>
|
||||
<source>New &ADSR</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="532"/>
|
||||
<location filename="../MainWindow.ui" line="538"/>
|
||||
<source>New &Curve</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="540"/>
|
||||
<location filename="../MainWindow.ui" line="546"/>
|
||||
<source>&Save Project</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="548"/>
|
||||
<location filename="../MainWindow.ui" line="554"/>
|
||||
<source>&Revert Project</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="556"/>
|
||||
<location filename="../MainWindow.ui" line="562"/>
|
||||
<source>Reload Sample &Data</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../MainWindow.ui" line="564"/>
|
||||
<location filename="../MainWindow.ui" line="570"/>
|
||||
<source>I&mport Songs</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -862,36 +903,50 @@
|
|||
<context>
|
||||
<name>PageModel</name>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="235"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="468"/>
|
||||
<source>Program Conflict</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="236"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="469"/>
|
||||
<source>Program %1 is already defined in table</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="272"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="475"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="501"/>
|
||||
<source>Change %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="515"/>
|
||||
<source>Program</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="274"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="517"/>
|
||||
<source>Object</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="276"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="519"/>
|
||||
<source>Priority</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="278"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="521"/>
|
||||
<source>Max Voices</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PageObjectDelegate</name>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="278"/>
|
||||
<source>Change %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PageObjectProxyModel</name>
|
||||
<message>
|
||||
|
@ -910,6 +965,19 @@
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PageTableView</name>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="1052"/>
|
||||
<source>Delete Page Entries</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="1052"/>
|
||||
<source>Delete Page Entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>PaintButton</name>
|
||||
<message>
|
||||
|
@ -929,188 +997,188 @@
|
|||
<context>
|
||||
<name>ProjectModel</name>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="519"/>
|
||||
<location filename="../ProjectModel.cpp" line="566"/>
|
||||
<source>Sound Macros</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="538"/>
|
||||
<location filename="../ProjectModel.cpp" line="585"/>
|
||||
<source>ADSRs</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="549"/>
|
||||
<location filename="../ProjectModel.cpp" line="596"/>
|
||||
<source>Curves</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="561"/>
|
||||
<location filename="../ProjectModel.cpp" line="608"/>
|
||||
<source>Keymaps</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="568"/>
|
||||
<location filename="../ProjectModel.cpp" line="615"/>
|
||||
<source>Layers</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="575"/>
|
||||
<location filename="../ProjectModel.cpp" line="622"/>
|
||||
<source>Samples</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="840"/>
|
||||
<location filename="../ProjectModel.cpp" line="887"/>
|
||||
<source>Subproject Conflict</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="840"/>
|
||||
<location filename="../ProjectModel.cpp" line="887"/>
|
||||
<source>The subproject %1 is already defined</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="845"/>
|
||||
<location filename="../ProjectModel.cpp" line="892"/>
|
||||
<source>Add Subproject %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="895"/>
|
||||
<location filename="../ProjectModel.cpp" line="942"/>
|
||||
<source>Sound Group Conflict</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="895"/>
|
||||
<location filename="../ProjectModel.cpp" line="920"/>
|
||||
<location filename="../ProjectModel.cpp" line="942"/>
|
||||
<location filename="../ProjectModel.cpp" line="967"/>
|
||||
<source>The group %1 is already defined</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="899"/>
|
||||
<location filename="../ProjectModel.cpp" line="946"/>
|
||||
<source>Add Sound Group %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="920"/>
|
||||
<location filename="../ProjectModel.cpp" line="967"/>
|
||||
<source>Song Group Conflict</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="924"/>
|
||||
<location filename="../ProjectModel.cpp" line="971"/>
|
||||
<source>Add Song Group %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1005"/>
|
||||
<location filename="../ProjectModel.cpp" line="1052"/>
|
||||
<source>Sound Macro Conflict</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1005"/>
|
||||
<location filename="../ProjectModel.cpp" line="1052"/>
|
||||
<source>The macro %1 is already defined</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1015"/>
|
||||
<location filename="../ProjectModel.cpp" line="1062"/>
|
||||
<source>Add Sound Macro %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1036"/>
|
||||
<location filename="../ProjectModel.cpp" line="1083"/>
|
||||
<source>ADSR Conflict</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1036"/>
|
||||
<location filename="../ProjectModel.cpp" line="1083"/>
|
||||
<source>The ADSR %1 is already defined</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1042"/>
|
||||
<location filename="../ProjectModel.cpp" line="1089"/>
|
||||
<source>Add ADSR %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1063"/>
|
||||
<location filename="../ProjectModel.cpp" line="1110"/>
|
||||
<source>Curve Conflict</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1063"/>
|
||||
<location filename="../ProjectModel.cpp" line="1110"/>
|
||||
<source>The Curve %1 is already defined</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1069"/>
|
||||
<location filename="../ProjectModel.cpp" line="1116"/>
|
||||
<source>Add Curve %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1090"/>
|
||||
<location filename="../ProjectModel.cpp" line="1137"/>
|
||||
<source>Keymap Conflict</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1090"/>
|
||||
<location filename="../ProjectModel.cpp" line="1137"/>
|
||||
<source>The Keymap %1 is already defined</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1095"/>
|
||||
<location filename="../ProjectModel.cpp" line="1142"/>
|
||||
<source>Add Keymap %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1116"/>
|
||||
<location filename="../ProjectModel.cpp" line="1163"/>
|
||||
<source>Layers Conflict</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1116"/>
|
||||
<location filename="../ProjectModel.cpp" line="1163"/>
|
||||
<source>Layers %1 is already defined</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1121"/>
|
||||
<location filename="../ProjectModel.cpp" line="1168"/>
|
||||
<source>Add Layers %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1135"/>
|
||||
<location filename="../ProjectModel.cpp" line="1182"/>
|
||||
<source>Delete Subproject %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1138"/>
|
||||
<location filename="../ProjectModel.cpp" line="1185"/>
|
||||
<source>Delete SongGroup %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1141"/>
|
||||
<location filename="../ProjectModel.cpp" line="1188"/>
|
||||
<source>Delete SFXGroup %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1144"/>
|
||||
<location filename="../ProjectModel.cpp" line="1191"/>
|
||||
<source>Delete SoundMacro %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1147"/>
|
||||
<location filename="../ProjectModel.cpp" line="1194"/>
|
||||
<source>Delete ADSR %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1150"/>
|
||||
<location filename="../ProjectModel.cpp" line="1197"/>
|
||||
<source>Delete Curve %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1153"/>
|
||||
<location filename="../ProjectModel.cpp" line="1200"/>
|
||||
<source>Delete Keymap %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../ProjectModel.cpp" line="1156"/>
|
||||
<location filename="../ProjectModel.cpp" line="1203"/>
|
||||
<source>Delete Layers %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -1118,132 +1186,163 @@
|
|||
<context>
|
||||
<name>SFXModel</name>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="182"/>
|
||||
<location filename="../SoundGroupEditor.cpp" line="304"/>
|
||||
<source>SFX Conflict</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="183"/>
|
||||
<location filename="../SoundGroupEditor.cpp" line="305"/>
|
||||
<source>SFX %1 is already defined in project</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="229"/>
|
||||
<location filename="../SoundGroupEditor.cpp" line="310"/>
|
||||
<source>Change SFX Name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="343"/>
|
||||
<source>Change %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="357"/>
|
||||
<source>SFX</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="231"/>
|
||||
<location filename="../SoundGroupEditor.cpp" line="359"/>
|
||||
<source>Object</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="233"/>
|
||||
<location filename="../SoundGroupEditor.cpp" line="361"/>
|
||||
<source>Priority</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="235"/>
|
||||
<location filename="../SoundGroupEditor.cpp" line="363"/>
|
||||
<source>Max Voices</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="237"/>
|
||||
<location filename="../SoundGroupEditor.cpp" line="365"/>
|
||||
<source>Velocity</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="239"/>
|
||||
<location filename="../SoundGroupEditor.cpp" line="367"/>
|
||||
<source>Panning</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="241"/>
|
||||
<location filename="../SoundGroupEditor.cpp" line="369"/>
|
||||
<source>Key</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SFXObjectDelegate</name>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="169"/>
|
||||
<source>Change %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SFXPlayerWidget</name>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="350"/>
|
||||
<location filename="../SoundGroupEditor.cpp" line="551"/>
|
||||
<source>Stop</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="364"/>
|
||||
<location filename="../SoundGroupEditor.cpp" line="386"/>
|
||||
<location filename="../SoundGroupEditor.cpp" line="565"/>
|
||||
<location filename="../SoundGroupEditor.cpp" line="587"/>
|
||||
<source>Play</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SFXTableView</name>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="505"/>
|
||||
<source>Delete SFX Entries</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="505"/>
|
||||
<source>Delete SFX Entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SampleControls</name>
|
||||
<message>
|
||||
<location filename="../SampleEditor.cpp" line="459"/>
|
||||
<location filename="../SampleEditor.cpp" line="475"/>
|
||||
<source>Change %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SampleEditor.cpp" line="507"/>
|
||||
<location filename="../SampleEditor.cpp" line="522"/>
|
||||
<location filename="../SampleEditor.cpp" line="781"/>
|
||||
<location filename="../SampleEditor.cpp" line="523"/>
|
||||
<location filename="../SampleEditor.cpp" line="538"/>
|
||||
<location filename="../SampleEditor.cpp" line="797"/>
|
||||
<source>Loop</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SampleEditor.cpp" line="541"/>
|
||||
<location filename="../SampleEditor.cpp" line="557"/>
|
||||
<source>Loop Start</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SampleEditor.cpp" line="560"/>
|
||||
<location filename="../SampleEditor.cpp" line="576"/>
|
||||
<source>Loop End</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SampleEditor.cpp" line="577"/>
|
||||
<location filename="../SampleEditor.cpp" line="593"/>
|
||||
<source>Change Base Pitch</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SampleEditor.cpp" line="666"/>
|
||||
<location filename="../SampleEditor.cpp" line="682"/>
|
||||
<source>Make WAV Version</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SampleEditor.cpp" line="673"/>
|
||||
<location filename="../SampleEditor.cpp" line="689"/>
|
||||
<source>Make Compressed Version</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SampleEditor.cpp" line="678"/>
|
||||
<location filename="../SampleEditor.cpp" line="694"/>
|
||||
<source>Up To Date</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SampleEditor.cpp" line="752"/>
|
||||
<location filename="../SampleEditor.cpp" line="822"/>
|
||||
<location filename="../SampleEditor.cpp" line="768"/>
|
||||
<location filename="../SampleEditor.cpp" line="838"/>
|
||||
<source>Nothing Loaded</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SampleEditor.cpp" line="774"/>
|
||||
<location filename="../SampleEditor.cpp" line="790"/>
|
||||
<source>Zoom</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SampleEditor.cpp" line="788"/>
|
||||
<location filename="../SampleEditor.cpp" line="804"/>
|
||||
<source>Start</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SampleEditor.cpp" line="795"/>
|
||||
<location filename="../SampleEditor.cpp" line="811"/>
|
||||
<source>End</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SampleEditor.cpp" line="802"/>
|
||||
<location filename="../SampleEditor.cpp" line="818"/>
|
||||
<source>Base Pitch</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -1251,22 +1350,27 @@
|
|||
<context>
|
||||
<name>SetupListModel</name>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="464"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="741"/>
|
||||
<source>Song Conflict</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="465"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="742"/>
|
||||
<source>Song %1 is already defined in project</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="486"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="747"/>
|
||||
<source>Change Song Name</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="764"/>
|
||||
<source>Song</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="488"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="766"/>
|
||||
<source>MIDI File</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -1274,55 +1378,83 @@
|
|||
<context>
|
||||
<name>SetupModel</name>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="653"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="987"/>
|
||||
<source>Change %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="1004"/>
|
||||
<source>Program</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="655"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="1006"/>
|
||||
<source>Volume</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="657"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="1008"/>
|
||||
<source>Panning</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="659"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="1010"/>
|
||||
<source>Reverb</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="661"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="1012"/>
|
||||
<source>Chorus</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SetupTableView</name>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="1115"/>
|
||||
<source>Delete Setup Entries</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="1115"/>
|
||||
<source>Delete Setup Entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SongGroupEditor</name>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="1103"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="1502"/>
|
||||
<source>Add new page entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="1105"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="1504"/>
|
||||
<source>Remove selected page entries</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="1072"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="1471"/>
|
||||
<source>Normal Pages</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="1073"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="1301"/>
|
||||
<source>Add Page Entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="1313"/>
|
||||
<source>Add Setup Entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="1472"/>
|
||||
<source>Drum Pages</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SongGroupEditor.cpp" line="1074"/>
|
||||
<location filename="../SongGroupEditor.cpp" line="1473"/>
|
||||
<source>MIDI Setups</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -1330,12 +1462,17 @@
|
|||
<context>
|
||||
<name>SoundGroupEditor</name>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="490"/>
|
||||
<location filename="../SoundGroupEditor.cpp" line="630"/>
|
||||
<source>Add SFX Entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="693"/>
|
||||
<source>Add new SFX entry</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../SoundGroupEditor.cpp" line="492"/>
|
||||
<location filename="../SoundGroupEditor.cpp" line="695"/>
|
||||
<source>Remove selected SFX entries</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -1413,6 +1550,14 @@
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SoundMacroDelegate</name>
|
||||
<message>
|
||||
<location filename="../LayersEditor.cpp" line="137"/>
|
||||
<source>Change %1</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>SoundMacroDeleteButton</name>
|
||||
<message>
|
||||
|
|
|
@ -63,9 +63,11 @@ class Sequencer : public Entity
|
|||
ObjToken<Voice> m_lastVoice;
|
||||
int8_t m_ctrlVals[128] = {}; /**< MIDI controller values */
|
||||
float m_curPitchWheel = 0.f; /**< MIDI pitch-wheel */
|
||||
int8_t m_pitchWheelRange = -1; /**< Pitch wheel range settable by RPN 0 */
|
||||
int8_t m_curProgram = 0; /**< MIDI program number */
|
||||
float m_curVol = 1.f; /**< Current volume of channel */
|
||||
float m_curPan = 0.f; /**< Current panning of channel */
|
||||
uint16_t m_rpn = 0; /**< Current RPN (only pitch-range 0x0000 supported) */
|
||||
|
||||
void _bringOutYourDead();
|
||||
size_t getVoiceCount() const;
|
||||
|
|
|
@ -78,10 +78,12 @@ class SongState
|
|||
const unsigned char* m_data = nullptr; /**< Pointer to upcoming command data */
|
||||
const unsigned char* m_pitchWheelData = nullptr; /**< Pointer to upcoming pitch data */
|
||||
const unsigned char* m_modWheelData = nullptr; /**< Pointer to upcoming modulation data */
|
||||
uint32_t m_lastPitchTick = 0; /**< Last position of pitch wheel change */
|
||||
int32_t m_lastPitchVal = 0; /**< Last value of pitch */
|
||||
uint32_t m_lastModTick = 0; /**< Last position of mod wheel change */
|
||||
int32_t m_lastModVal = 0; /**< Last value of mod */
|
||||
int32_t m_pitchVal = 0; /**< Accumulated value of pitch */
|
||||
uint32_t m_nextPitchTick = 0; /**< Upcoming position of pitch wheel change */
|
||||
int32_t m_nextPitchDelta = 0; /**< Upcoming delta value of pitch */
|
||||
int32_t m_modVal = 0; /**< Accumulated value of mod */
|
||||
uint32_t m_nextModTick = 0; /**< Upcoming position of mod wheel change */
|
||||
int32_t m_nextModDelta = 0; /**< Upcoming delta value of mod */
|
||||
std::array<int, 128> m_remNoteLengths; /**< Remaining ticks per note */
|
||||
|
||||
int32_t m_eventWaitCountdown = 0; /**< Current wait in ticks */
|
||||
|
|
|
@ -86,8 +86,8 @@ class Voice : public Entity
|
|||
float m_curPan = 0.f; /**< Current pan of voice */
|
||||
float m_curSpan = -1.f; /**< Current surround pan of voice */
|
||||
float m_curPitchWheel = 0.f; /**< Current normalized wheel value for control */
|
||||
int32_t m_pitchWheelUp = 600; /**< Up range for pitchwheel control in cents */
|
||||
int32_t m_pitchWheelDown = 600; /**< Down range for pitchwheel control in cents */
|
||||
int32_t m_pitchWheelUp = 200; /**< Up range for pitchwheel control in cents */
|
||||
int32_t m_pitchWheelDown = 200; /**< Down range for pitchwheel control in cents */
|
||||
int32_t m_pitchWheelVal = 0; /**< Current resolved pitchwheel delta for control */
|
||||
int32_t m_curPitch; /**< Current base pitch in cents */
|
||||
bool m_pitchDirty = true; /**< m_curPitch has been updated and needs sending to voice */
|
||||
|
@ -139,6 +139,8 @@ class Voice : public Entity
|
|||
std::unique_ptr<int8_t[]> m_ctrlValsSelf; /**< Self-owned MIDI Controller values */
|
||||
int8_t* m_extCtrlVals = nullptr; /**< MIDI Controller values (external storage) */
|
||||
|
||||
uint16_t m_rpn = 0; /**< Current RPN (only pitch-range 0x0000 supported) */
|
||||
|
||||
void _destroy();
|
||||
bool _checkSamplePos(bool& looped);
|
||||
void _doKeyOff();
|
||||
|
|
|
@ -278,6 +278,8 @@ ObjToken<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velocity)
|
|||
(*ret)->setAuxBVol(m_ctrlVals[0x5d] / 127.f);
|
||||
(*ret)->setPan(m_curPan);
|
||||
(*ret)->setPitchWheel(m_curPitchWheel);
|
||||
if (m_pitchWheelRange != -1)
|
||||
(*ret)->setPitchWheelRange(m_pitchWheelRange, m_pitchWheelRange);
|
||||
|
||||
if (m_ctrlVals[64] > 64)
|
||||
(*ret)->setPedal(true);
|
||||
|
@ -336,6 +338,23 @@ void Sequencer::ChannelState::setCtrlValue(uint8_t ctrl, int8_t val)
|
|||
case 10:
|
||||
setPan(val / 64.f - 1.f);
|
||||
break;
|
||||
case 98:
|
||||
// RPN LSB
|
||||
m_rpn &= ~0x7f;
|
||||
m_rpn |= val;
|
||||
case 99:
|
||||
// RPN MSB
|
||||
m_rpn &= ~0x3f80;
|
||||
m_rpn |= val << 7;
|
||||
case 6:
|
||||
if (m_rpn == 0)
|
||||
m_pitchWheelRange = val;
|
||||
case 96:
|
||||
if (m_rpn == 0)
|
||||
m_pitchWheelRange += 1;
|
||||
case 97:
|
||||
if (m_rpn == 0)
|
||||
m_pitchWheelRange -= 1;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -513,68 +513,63 @@ public:
|
|||
std::vector<uint8_t>& getResult() { return m_result; }
|
||||
};
|
||||
|
||||
static uint32_t DecodeRLE(const unsigned char*& data)
|
||||
static uint16_t DecodeUnsignedValue(const unsigned char*& data)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
|
||||
while (true)
|
||||
uint16_t ret;
|
||||
if (data[0] & 0x80)
|
||||
{
|
||||
uint32_t thisPart = *data & 0x7f;
|
||||
if (*data & 0x80)
|
||||
{
|
||||
++data;
|
||||
thisPart = thisPart * 256 + *data;
|
||||
if (thisPart == 0)
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (thisPart == 32767)
|
||||
{
|
||||
ret += 32767;
|
||||
ret = data[1] | ((data[0] & 0x7f) << 8);
|
||||
data += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
ret += thisPart;
|
||||
data += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void EncodeRLE(std::vector<uint8_t>& vecOut, uint32_t val)
|
||||
{
|
||||
while (val >= 32767)
|
||||
{
|
||||
vecOut.push_back(0xff);
|
||||
vecOut.push_back(0xff);
|
||||
vecOut.push_back(0);
|
||||
val -= 32767;
|
||||
}
|
||||
|
||||
if (val >= 128)
|
||||
{
|
||||
vecOut.push_back(uint8_t(val / 256) | 0x80);
|
||||
vecOut.push_back(uint8_t(val % 256));
|
||||
}
|
||||
else
|
||||
vecOut.push_back(uint8_t(val));
|
||||
}
|
||||
|
||||
static int32_t DecodeContinuousRLE(const unsigned char*& data)
|
||||
{
|
||||
int32_t ret = int32_t(DecodeRLE(data));
|
||||
if (ret >= 16384)
|
||||
return ret - 32767;
|
||||
ret = data[0];
|
||||
data += 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void EncodeContinuousRLE(std::vector<uint8_t>& vecOut, int32_t val)
|
||||
static void EncodeUnsignedValue(std::vector<uint8_t>& vecOut, uint16_t val)
|
||||
{
|
||||
if (val < 0)
|
||||
val += 32767;
|
||||
EncodeRLE(vecOut, uint32_t(val));
|
||||
if (val >= 128)
|
||||
{
|
||||
vecOut.push_back(0x80 | ((val >> 8) & 0x7f));
|
||||
vecOut.push_back(val & 0xff);
|
||||
}
|
||||
else
|
||||
{
|
||||
vecOut.push_back(val & 0x7f);
|
||||
}
|
||||
}
|
||||
|
||||
static int16_t DecodeSignedValue(const unsigned char*& data)
|
||||
{
|
||||
int16_t ret;
|
||||
if (data[0] & 0x80)
|
||||
{
|
||||
ret = data[1] | ((data[0] & 0x7f) << 8);
|
||||
ret |= ((ret << 1) & 0x8000);
|
||||
data += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = int8_t(data[0] | ((data[0] << 1) & 0x80));
|
||||
data += 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void EncodeSignedValue(std::vector<uint8_t>& vecOut, int16_t val)
|
||||
{
|
||||
if (val >= 64 || val < -64)
|
||||
{
|
||||
vecOut.push_back(0x80 | ((val >> 8) & 0x7f));
|
||||
vecOut.push_back(val & 0xff);
|
||||
}
|
||||
else
|
||||
{
|
||||
vecOut.push_back(val & 0x7f);
|
||||
}
|
||||
}
|
||||
|
||||
static uint32_t DecodeTimeRLE(const unsigned char*& data)
|
||||
|
@ -719,48 +714,44 @@ std::vector<uint8_t> SongConverter::SongToMIDI(const unsigned char* data, int& v
|
|||
{
|
||||
while (true)
|
||||
{
|
||||
/* See if there's an upcoming pitch change in this interval */
|
||||
const unsigned char* ptr = trk.m_pitchWheelData;
|
||||
uint32_t deltaTicks = DecodeRLE(ptr);
|
||||
if (deltaTicks != 0xffffffff)
|
||||
{
|
||||
int32_t nextTick = trk.m_lastPitchTick + deltaTicks;
|
||||
int32_t pitchDelta = DecodeContinuousRLE(ptr);
|
||||
trk.m_lastPitchVal += pitchDelta;
|
||||
trk.m_pitchWheelData = ptr;
|
||||
trk.m_lastPitchTick = nextTick;
|
||||
events.emplace(regStart + nextTick,
|
||||
/* Update pitch */
|
||||
trk.m_pitchVal += trk.m_nextPitchDelta;
|
||||
events.emplace(regStart + trk.m_nextPitchTick,
|
||||
Event{PitchEvent{}, trk.m_midiChan,
|
||||
clamp(0, trk.m_lastPitchVal / 2 + 0x2000, 0x4000)});
|
||||
clamp(0, trk.m_pitchVal + 0x2000, 0x4000)});
|
||||
if (trk.m_pitchWheelData[0] != 0x80 || trk.m_pitchWheelData[1] != 0x00)
|
||||
{
|
||||
trk.m_nextPitchTick += DecodeUnsignedValue(trk.m_pitchWheelData);
|
||||
trk.m_nextPitchDelta = DecodeSignedValue(trk.m_pitchWheelData);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Update continuous modulation data */
|
||||
if (trk.m_modWheelData)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
/* See if there's an upcoming modulation change in this interval */
|
||||
const unsigned char* ptr = trk.m_modWheelData;
|
||||
uint32_t deltaTicks = DecodeRLE(ptr);
|
||||
if (deltaTicks != 0xffffffff)
|
||||
{
|
||||
int32_t nextTick = trk.m_lastModTick + deltaTicks;
|
||||
int32_t modDelta = DecodeContinuousRLE(ptr);
|
||||
trk.m_lastModVal += modDelta;
|
||||
trk.m_modWheelData = ptr;
|
||||
trk.m_lastModTick = nextTick;
|
||||
events.emplace(regStart + nextTick,
|
||||
/* Update modulation */
|
||||
trk.m_modVal += trk.m_nextModDelta;
|
||||
events.emplace(regStart + trk.m_nextModTick,
|
||||
Event{CtrlEvent{}, trk.m_midiChan, 1,
|
||||
uint8_t(clamp(0, trk.m_lastModVal * 128 / 16384, 127)), 0});
|
||||
uint8_t(clamp(0, trk.m_modVal / 128, 127)), 0});
|
||||
if (trk.m_modWheelData[0] != 0x80 || trk.m_modWheelData[1] != 0x00)
|
||||
{
|
||||
trk.m_nextModTick += DecodeUnsignedValue(trk.m_modWheelData);
|
||||
trk.m_nextModDelta = DecodeSignedValue(trk.m_modWheelData);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop through as many commands as we can for this time period */
|
||||
if (song.m_sngVersion == 1)
|
||||
|
@ -1088,10 +1079,10 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
|||
{
|
||||
if (event.second.noteOrCtrl == 1)
|
||||
{
|
||||
EncodeRLE(region.modBuf, uint32_t(eventTick - lastModTick));
|
||||
EncodeUnsignedValue(region.modBuf, uint32_t(eventTick - lastModTick));
|
||||
lastModTick = eventTick;
|
||||
int newMod = event.second.velOrVal * 16384 / 128;
|
||||
EncodeContinuousRLE(region.modBuf, newMod - lastModVal);
|
||||
int newMod = event.second.velOrVal * 128;
|
||||
EncodeSignedValue(region.modBuf, newMod - lastModVal);
|
||||
lastModVal = newMod;
|
||||
}
|
||||
else
|
||||
|
@ -1157,10 +1148,10 @@ std::vector<uint8_t> SongConverter::MIDIToSong(const std::vector<uint8_t>& data,
|
|||
}
|
||||
case Event::Type::Pitch:
|
||||
{
|
||||
EncodeRLE(region.pitchBuf, uint32_t(eventTick - lastPitchTick));
|
||||
EncodeUnsignedValue(region.pitchBuf, uint32_t(eventTick - lastPitchTick));
|
||||
lastPitchTick = eventTick;
|
||||
int newPitch = (event.second.pitchBend - 0x2000) * 2;
|
||||
EncodeContinuousRLE(region.pitchBuf, newPitch - lastPitchVal);
|
||||
int newPitch = event.second.pitchBend - 0x2000;
|
||||
EncodeSignedValue(region.pitchBuf, newPitch - lastPitchVal);
|
||||
lastPitchVal = newPitch;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -6,44 +6,36 @@
|
|||
namespace amuse
|
||||
{
|
||||
|
||||
static uint32_t DecodeRLE(const unsigned char*& data)
|
||||
static uint16_t DecodeUnsignedValue(const unsigned char*& data)
|
||||
{
|
||||
uint32_t ret = 0;
|
||||
|
||||
while (true)
|
||||
uint16_t ret;
|
||||
if (data[0] & 0x80)
|
||||
{
|
||||
uint32_t thisPart = *data & 0x7f;
|
||||
if (*data & 0x80)
|
||||
{
|
||||
++data;
|
||||
thisPart = thisPart * 256 + *data;
|
||||
if (thisPart == 0)
|
||||
{
|
||||
++data;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (thisPart == 32767)
|
||||
{
|
||||
ret += 32767;
|
||||
ret = data[1] | ((data[0] & 0x7f) << 8);
|
||||
data += 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
ret += thisPart;
|
||||
else
|
||||
{
|
||||
ret = data[0];
|
||||
data += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int32_t DecodeContinuousRLE(const unsigned char*& data)
|
||||
static int16_t DecodeSignedValue(const unsigned char*& data)
|
||||
{
|
||||
int32_t ret = int32_t(DecodeRLE(data));
|
||||
if (ret >= 16384)
|
||||
return ret - 32767;
|
||||
int16_t ret;
|
||||
if (data[0] & 0x80)
|
||||
{
|
||||
ret = data[1] | ((data[0] & 0x7f) << 8);
|
||||
ret |= ((ret << 1) & 0x8000);
|
||||
data += 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = int8_t(data[0] | ((data[0] << 1) & 0x80));
|
||||
data += 1;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -118,20 +110,39 @@ void SongState::Track::setRegion(Sequencer* seq, const TrackRegion* region)
|
|||
header.swapBig();
|
||||
m_data += 12;
|
||||
|
||||
m_pitchWheelData = nullptr;
|
||||
m_nextPitchTick = 0x7fffffff;
|
||||
m_nextPitchDelta = 0;
|
||||
if (header.m_pitchOff)
|
||||
{
|
||||
m_pitchWheelData = m_parent->m_songData + header.m_pitchOff;
|
||||
if (m_pitchWheelData[0] != 0x80 || m_pitchWheelData[1] != 0x00)
|
||||
{
|
||||
m_nextPitchTick = m_parent->m_curTick + DecodeUnsignedValue(m_pitchWheelData);
|
||||
m_nextPitchDelta = DecodeSignedValue(m_pitchWheelData);
|
||||
}
|
||||
}
|
||||
|
||||
m_modWheelData = nullptr;
|
||||
m_nextModTick = 0x7fffffff;
|
||||
m_nextModDelta = 0;
|
||||
if (header.m_modOff)
|
||||
{
|
||||
m_modWheelData = m_parent->m_songData + header.m_modOff;
|
||||
if (m_modWheelData[0] != 0x80 || m_modWheelData[1] != 0x00)
|
||||
{
|
||||
m_nextModTick = m_parent->m_curTick + DecodeUnsignedValue(m_modWheelData);
|
||||
m_nextModDelta = DecodeSignedValue(m_modWheelData);
|
||||
}
|
||||
}
|
||||
|
||||
m_eventWaitCountdown = 0;
|
||||
m_lastPitchTick = m_parent->m_curTick;
|
||||
m_lastPitchVal = 0;
|
||||
m_lastModTick = m_parent->m_curTick;
|
||||
m_lastModVal = 0;
|
||||
m_pitchVal = 0;
|
||||
m_modVal = 0;
|
||||
if (seq)
|
||||
{
|
||||
seq->setPitchWheel(m_midiChan, clamp(-1.f, m_lastPitchVal / 32768.f, 1.f));
|
||||
seq->setCtrlValue(m_midiChan, 1, clamp(0, m_lastModVal * 128 / 16384, 127));
|
||||
seq->setPitchWheel(m_midiChan, clamp(-1.f, m_pitchVal / 32768.f, 1.f));
|
||||
seq->setCtrlValue(m_midiChan, 1, clamp(0, m_modVal * 128 / 16384, 127));
|
||||
}
|
||||
if (m_parent->m_sngVersion == 1)
|
||||
m_eventWaitCountdown = int32_t(DecodeTimeRLE(m_data));
|
||||
|
@ -219,10 +230,12 @@ int SongState::DetectVersion(const unsigned char* ptr, bool& isBig)
|
|||
if (header.m_pitchOff)
|
||||
{
|
||||
const unsigned char* dptr = ptr + header.m_pitchOff;
|
||||
while (DecodeRLE(dptr) != 0xffffffff)
|
||||
while (dptr[0] != 0x80 || dptr[1] != 0x00)
|
||||
{
|
||||
DecodeContinuousRLE(dptr);
|
||||
DecodeUnsignedValue(dptr);
|
||||
DecodeSignedValue(dptr);
|
||||
}
|
||||
dptr += 2;
|
||||
if (dptr >= (expectedEnd - 4) && (dptr <= expectedEnd))
|
||||
continue;
|
||||
}
|
||||
|
@ -231,10 +244,12 @@ int SongState::DetectVersion(const unsigned char* ptr, bool& isBig)
|
|||
if (header.m_modOff)
|
||||
{
|
||||
const unsigned char* dptr = ptr + header.m_modOff;
|
||||
while (DecodeRLE(dptr) != 0xffffffff)
|
||||
while (dptr[0] != 0x80 || dptr[1] != 0x00)
|
||||
{
|
||||
DecodeContinuousRLE(dptr);
|
||||
DecodeUnsignedValue(dptr);
|
||||
DecodeSignedValue(dptr);
|
||||
}
|
||||
dptr += 2;
|
||||
if (dptr >= (expectedEnd - 4) && (dptr <= expectedEnd))
|
||||
continue;
|
||||
}
|
||||
|
@ -404,28 +419,24 @@ bool SongState::Track::advance(Sequencer& seq, int32_t ticks)
|
|||
while (pitchTick < endTick)
|
||||
{
|
||||
/* See if there's an upcoming pitch change in this interval */
|
||||
const unsigned char* ptr = m_pitchWheelData;
|
||||
uint32_t deltaTicks = DecodeRLE(ptr);
|
||||
if (deltaTicks != 0xffffffff)
|
||||
{
|
||||
int32_t nextTick = m_lastPitchTick + deltaTicks;
|
||||
int32_t nextTick = m_nextPitchTick;
|
||||
if (pitchTick + remPitchTicks > nextTick)
|
||||
{
|
||||
/* Update pitch */
|
||||
int32_t pitchDelta = DecodeContinuousRLE(ptr);
|
||||
m_lastPitchVal += pitchDelta;
|
||||
m_pitchWheelData = ptr;
|
||||
m_lastPitchTick = nextTick;
|
||||
remPitchTicks -= (nextTick - pitchTick);
|
||||
pitchTick = nextTick;
|
||||
seq.setPitchWheel(m_midiChan, clamp(-1.f, m_lastPitchVal / 32768.f, 1.f));
|
||||
continue;
|
||||
}
|
||||
remPitchTicks -= (nextTick - pitchTick);
|
||||
pitchTick = nextTick;
|
||||
m_pitchVal += m_nextPitchDelta;
|
||||
seq.setPitchWheel(m_midiChan, clamp(-1.f, m_pitchVal / 8191.f, 1.f));
|
||||
if (m_pitchWheelData[0] != 0x80 || m_pitchWheelData[1] != 0x00)
|
||||
{
|
||||
m_nextPitchTick += DecodeUnsignedValue(m_pitchWheelData);
|
||||
m_nextPitchDelta = DecodeSignedValue(m_pitchWheelData);
|
||||
}
|
||||
else
|
||||
break;
|
||||
{
|
||||
m_nextPitchTick = 0x7fffffff;
|
||||
}
|
||||
}
|
||||
remPitchTicks -= (nextTick - pitchTick);
|
||||
pitchTick = nextTick;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -437,28 +448,24 @@ bool SongState::Track::advance(Sequencer& seq, int32_t ticks)
|
|||
while (modTick < endTick)
|
||||
{
|
||||
/* See if there's an upcoming modulation change in this interval */
|
||||
const unsigned char* ptr = m_modWheelData;
|
||||
uint32_t deltaTicks = DecodeRLE(ptr);
|
||||
if (deltaTicks != 0xffffffff)
|
||||
{
|
||||
int32_t nextTick = m_lastModTick + deltaTicks;
|
||||
int32_t nextTick = m_nextModTick;
|
||||
if (modTick + remModTicks > nextTick)
|
||||
{
|
||||
/* Update modulation */
|
||||
int32_t modDelta = DecodeContinuousRLE(ptr);
|
||||
m_lastModVal += modDelta;
|
||||
m_modWheelData = ptr;
|
||||
m_lastModTick = nextTick;
|
||||
remModTicks -= (nextTick - modTick);
|
||||
modTick = nextTick;
|
||||
seq.setCtrlValue(m_midiChan, 1, clamp(0, m_lastModVal * 128 / 16384, 127));
|
||||
continue;
|
||||
}
|
||||
remModTicks -= (nextTick - modTick);
|
||||
modTick = nextTick;
|
||||
m_modVal += m_nextModDelta;
|
||||
seq.setCtrlValue(m_midiChan, 1, clamp(0, m_modVal / 128, 127));
|
||||
if (m_modWheelData[0] != 0x80 || m_modWheelData[1] != 0x00)
|
||||
{
|
||||
m_nextModTick += DecodeUnsignedValue(m_modWheelData);
|
||||
m_nextModDelta = DecodeSignedValue(m_modWheelData);
|
||||
}
|
||||
else
|
||||
break;
|
||||
{
|
||||
m_nextModTick = 0x7fffffff;
|
||||
}
|
||||
}
|
||||
remModTicks -= (nextTick - modTick);
|
||||
modTick = nextTick;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1458,6 +1458,39 @@ void Voice::_notifyCtrlChange(uint8_t ctrl, int8_t val)
|
|||
{
|
||||
m_state.m_curMod = uint8_t(val);
|
||||
}
|
||||
else if (ctrl == 0x64)
|
||||
{
|
||||
// RPN LSB
|
||||
m_rpn &= ~0x7f;
|
||||
m_rpn |= val;
|
||||
}
|
||||
else if (ctrl == 0x65)
|
||||
{
|
||||
// RPN MSB
|
||||
m_rpn &= ~0x3f80;
|
||||
m_rpn |= val << 7;
|
||||
}
|
||||
else if (ctrl == 0x6)
|
||||
{
|
||||
if (m_rpn == 0)
|
||||
m_pitchWheelUp = m_pitchWheelDown = val * 100;
|
||||
}
|
||||
else if (ctrl == 0x60)
|
||||
{
|
||||
if (m_rpn == 0)
|
||||
{
|
||||
m_pitchWheelUp += 100;
|
||||
m_pitchWheelDown += 100;
|
||||
}
|
||||
}
|
||||
else if (ctrl == 0x61)
|
||||
{
|
||||
if (m_rpn == 0)
|
||||
{
|
||||
m_pitchWheelUp -= 100;
|
||||
m_pitchWheelDown -= 100;
|
||||
}
|
||||
}
|
||||
|
||||
for (ObjToken<Voice>& vox : m_childVoices)
|
||||
vox->_notifyCtrlChange(ctrl, val);
|
||||
|
|
Loading…
Reference in New Issue