Initial ProjectModel implementation

This commit is contained in:
Jack Andersen 2018-07-16 18:48:38 -10:00
parent 7a38fd0676
commit 3f265cdb46
29 changed files with 1337 additions and 224 deletions

View File

@ -54,4 +54,4 @@ target_link_libraries(amuse-gui ${PLAT_LIBS}
${Qt5Network_LIBRARIES}
${Qt5Xml_LIBRARIES}
${Qt5Svg_LIBRARIES}
amuse boo ${BOO_SYS_LIBS} logvisor zeus athena-core athena-libyaml xxhash ${ZLIB_LIBRARIES} ${LZO_LIB})
amuse boo ${BOO_SYS_LIBS} logvisor athena-core athena-libyaml xxhash ${ZLIB_LIBRARIES} ${LZO_LIB})

View File

@ -20,18 +20,18 @@ QString SysStringToQString(const boo::SystemString& str)
#endif
}
bool MkPath(const QString& path, QWidget* parent)
bool MkPath(const QString& path, UIMessenger& messenger)
{
QFileInfo fInfo(path);
return MkPath(fInfo.dir(), fInfo.fileName(), parent);
return MkPath(fInfo.dir(), fInfo.fileName(), messenger);
}
bool MkPath(const QDir& dir, const QString& file, QWidget* parent)
bool MkPath(const QDir& dir, const QString& file, UIMessenger& messenger)
{
if (!dir.mkpath(file))
{
QString msg = QString(parent->tr("A directory at '%1/%2' could not be created.")).arg(dir.path()).arg(file);
QMessageBox::critical(parent, parent->tr("Unable to create directory"), msg);
QString msg = QString(QObject::tr("A directory at '%1/%2' could not be created.")).arg(dir.path()).arg(file);
messenger.critical(QObject::tr("Unable to create directory"), msg);
return false;
}
return true;

View File

@ -4,11 +4,33 @@
#include "boo/System.hpp"
#include <QString>
#include <QDir>
#include <QMessageBox>
class UIMessenger : public QObject
{
Q_OBJECT
public:
using QObject::QObject;
signals:
QMessageBox::StandardButton information(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton question(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons =
QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton warning(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton critical(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
};
boo::SystemString QStringToSysString(const QString& str);
QString SysStringToQString(const boo::SystemString& str);
bool MkPath(const QString& path, QWidget* parent);
bool MkPath(const QDir& dir, const QString& file, QWidget* parent);
bool MkPath(const QString& path, UIMessenger& messenger);
bool MkPath(const QDir& dir, const QString& file, UIMessenger& messenger);
#endif //AMUSE_COMMON_HPP

View File

@ -3,15 +3,27 @@
#include <QMessageBox>
#include <QLineEdit>
#include <QInputDialog>
#include <QProgressDialog>
#include <QtSvg/QtSvg>
#include "amuse/ContainerRegistry.hpp"
#include "Common.hpp"
static void connectMessenger(UIMessenger* messenger, Qt::ConnectionType type)
{
}
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent),
m_undoStack(new QUndoStack(this))
m_mainMessenger(this),
m_undoStack(new QUndoStack(this)),
m_backgroundThread(this)
{
m_backgroundThread.start();
m_ui.setupUi(this);
connectMessenger(&m_mainMessenger, Qt::DirectConnection);
m_ui.actionNew_Project->setShortcut(QKeySequence::New);
connect(m_ui.actionNew_Project, SIGNAL(triggered()), this, SLOT(newAction()));
m_ui.actionOpen_Project->setShortcut(QKeySequence::Open);
@ -55,9 +67,39 @@ MainWindow::MainWindow(QWidget* parent)
MainWindow::~MainWindow()
{
m_backgroundThread.quit();
m_backgroundThread.wait();
printf("IM DYING\n");
}
void MainWindow::connectMessenger(UIMessenger* messenger, Qt::ConnectionType type)
{
connect(messenger, SIGNAL(information(const QString&,
const QString&, QMessageBox::StandardButtons,
QMessageBox::StandardButton)),
this, SLOT(msgInformation(const QString&,
const QString&, QMessageBox::StandardButtons,
QMessageBox::StandardButton)), type);
connect(messenger, SIGNAL(question(const QString&,
const QString&, QMessageBox::StandardButtons,
QMessageBox::StandardButton)),
this, SLOT(msgQuestion(const QString&,
const QString&, QMessageBox::StandardButtons,
QMessageBox::StandardButton)), type);
connect(messenger, SIGNAL(warning(const QString&,
const QString&, QMessageBox::StandardButtons,
QMessageBox::StandardButton)),
this, SLOT(msgWarning(const QString&,
const QString&, QMessageBox::StandardButtons,
QMessageBox::StandardButton)), type);
connect(messenger, SIGNAL(critical(const QString&,
const QString&, QMessageBox::StandardButtons,
QMessageBox::StandardButton)),
this, SLOT(msgCritical(const QString&,
const QString&, QMessageBox::StandardButtons,
QMessageBox::StandardButton)), type);
}
bool MainWindow::setProjectPath(const QString& path)
{
if (m_projectModel && m_projectModel->path() == path)
@ -138,14 +180,52 @@ void MainWindow::refreshMIDIIO()
m_ui.menuMIDI->addAction(tr("No MIDI Devices Found"))->setEnabled(false);
}
void MainWindow::startBackgroundTask(const QString& windowTitle, const QString& label,
std::function<void(BackgroundTask&)>&& task)
{
assert(m_backgroundTask == nullptr && "existing background process");
setEnabled(false);
m_backgroundTask = new BackgroundTask(std::move(task));
m_backgroundTask->moveToThread(&m_backgroundThread);
m_backgroundDialog = new QProgressDialog(this);
m_backgroundDialog->setWindowTitle(windowTitle);
m_backgroundDialog->setLabelText(label);
m_backgroundDialog->setMinimumWidth(300);
m_backgroundDialog->setAutoClose(false);
m_backgroundDialog->setAutoReset(false);
m_backgroundDialog->setMinimumDuration(0);
m_backgroundDialog->setMinimum(0);
m_backgroundDialog->setMaximum(0);
m_backgroundDialog->setValue(0);
connect(m_backgroundTask, SIGNAL(setMinimum(int)),
m_backgroundDialog, SLOT(setMinimum(int)), Qt::QueuedConnection);
connect(m_backgroundTask, SIGNAL(setMaximum(int)),
m_backgroundDialog, SLOT(setMaximum(int)), Qt::QueuedConnection);
connect(m_backgroundTask, SIGNAL(setValue(int)),
m_backgroundDialog, SLOT(setValue(int)), Qt::QueuedConnection);
connect(m_backgroundTask, SIGNAL(setLabelText(const QString&)),
m_backgroundDialog, SLOT(setLabelText(const QString&)), Qt::QueuedConnection);
connect(m_backgroundTask, SIGNAL(finished()),
this, SLOT(onBackgroundTaskFinished()), Qt::QueuedConnection);
m_backgroundDialog->open(m_backgroundTask, SLOT(cancel()));
connectMessenger(&m_backgroundTask->uiMessenger(), Qt::BlockingQueuedConnection);
QMetaObject::invokeMethod(m_backgroundTask, "run", Qt::QueuedConnection);
}
void MainWindow::newAction()
{
QString path = QFileDialog::getSaveFileName(this, tr("New Project"));
if (path.isEmpty())
return;
if (!MkPath(path, this))
if (!MkPath(path, m_mainMessenger))
return;
if (!setProjectPath(path))
return;
setProjectPath(path);
}
void MainWindow::openAction()
@ -153,7 +233,10 @@ void MainWindow::openAction()
QString path = QFileDialog::getExistingDirectory(this, tr("Open Project"));
if (path.isEmpty())
return;
setProjectPath(path);
if (!setProjectPath(path))
return;
}
void MainWindow::importAction()
@ -176,8 +259,8 @@ void MainWindow::importAction()
int impMode = QMessageBox::question(this, tr("Sample Import Mode"),
tr("Amuse can import samples as WAV files for ease of editing, "
"import original compressed data for lossless repacking, or both. "
"Exporting the project will prefer compressed files over WAVs, so "
"be sure to delete the compressed version if you edit the WAV."),
"Exporting the project will prefer whichever version was modified "
"most recently."),
tr("Import Compressed"), tr("Import WAVs"), tr("Import Both"));
switch (impMode)
@ -189,6 +272,7 @@ void MainWindow::importAction()
default:
return;
}
ProjectModel::ImportMode importMode = ProjectModel::ImportMode(impMode);
/* Special handling for raw groups - gather sibling groups in filesystem */
if (tp == amuse::ContainerRegistry::Type::Raw4)
@ -211,12 +295,16 @@ void MainWindow::importAction()
QFileInfo fInfo(path);
QString newPath = QFileInfo(fInfo.dir(), newName).filePath();
if (!MkPath(fInfo.dir(), newName, this))
if (!MkPath(fInfo.dir(), newName, m_mainMessenger))
return;
if (!setProjectPath(newPath))
return;
}
ProjectModel* model = m_projectModel;
startBackgroundTask(tr("Importing"), tr("Scanning Project"),
[model, path, importMode](BackgroundTask& task)
{
QDir dir = QFileInfo(path).dir();
QStringList filters;
filters << "*.proj" << "*.pro";
@ -225,11 +313,18 @@ void MainWindow::importAction()
{
auto data = amuse::ContainerRegistry::LoadContainer(QStringToSysString(dir.filePath(fPath)).c_str());
for (auto& p : data)
if (!m_projectModel->importGroupData(SysStringToQString(p.first), std::move(p.second)))
{
task.setLabelText(tr("Importing %1").arg(SysStringToQString(p.first)));
if (task.isCanceled())
return;
if (!model->importGroupData(SysStringToQString(p.first), p.second,
importMode, task.uiMessenger()))
return;
}
m_projectModel->extractSamples(ProjectModel::ImportMode(impMode), this);
m_projectModel->saveToFile(this);
}
});
return;
}
else if (scanMode == QMessageBox::No)
@ -245,20 +340,31 @@ void MainWindow::importAction()
{
QFileInfo fInfo(path);
QString newPath = QFileInfo(fInfo.dir(), fInfo.completeBaseName()).filePath();
if (!MkPath(fInfo.dir(), fInfo.completeBaseName(), this))
if (!MkPath(fInfo.dir(), fInfo.completeBaseName(), m_mainMessenger))
return;
if (!setProjectPath(newPath))
return;
}
ProjectModel* model = m_projectModel;
startBackgroundTask(tr("Importing"), tr("Scanning Project"),
[model, path, importMode](BackgroundTask& task)
{
/* Handle single container */
auto data = amuse::ContainerRegistry::LoadContainer(QStringToSysString(path).c_str());
task.setMaximum(int(data.size()));
int curVal = 0;
for (auto& p : data)
if (!m_projectModel->importGroupData(SysStringToQString(p.first), std::move(p.second)))
{
task.setLabelText(tr("Importing %1").arg(SysStringToQString(p.first)));
if (task.isCanceled())
return;
m_projectModel->extractSamples(ProjectModel::ImportMode(impMode), this);
m_projectModel->saveToFile(this);
if (!model->importGroupData(SysStringToQString(p.first), p.second,
importMode, task.uiMessenger()))
return;
task.setValue(++curVal);
}
});
}
void MainWindow::exportAction()
@ -395,3 +501,42 @@ void MainWindow::onTextDelete()
le->del();
}
}
void MainWindow::onBackgroundTaskFinished()
{
m_backgroundDialog->reset();
m_backgroundDialog->deleteLater();
m_backgroundDialog = nullptr;
m_backgroundTask->deleteLater();
m_backgroundTask = nullptr;
m_projectModel->ensureModelData();
setEnabled(true);
}
QMessageBox::StandardButton MainWindow::msgInformation(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons,
QMessageBox::StandardButton defaultButton)
{
return QMessageBox::information(this, title, text, buttons, defaultButton);
}
QMessageBox::StandardButton MainWindow::msgQuestion(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons,
QMessageBox::StandardButton defaultButton)
{
return QMessageBox::question(this, title, text, buttons, defaultButton);
}
QMessageBox::StandardButton MainWindow::msgWarning(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons,
QMessageBox::StandardButton defaultButton)
{
return QMessageBox::warning(this, title, text, buttons, defaultButton);
}
QMessageBox::StandardButton MainWindow::msgCritical(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons,
QMessageBox::StandardButton defaultButton)
{
return QMessageBox::critical(this, title, text, buttons, defaultButton);
}

View File

@ -3,6 +3,8 @@
#include <QMainWindow>
#include <QUndoStack>
#include <QProgressDialog>
#include <QThread>
#include "ui_MainWindow.h"
#include "amuse/Engine.hpp"
#include "amuse/BooBackend.hpp"
@ -15,10 +17,35 @@ class MainWindow;
class AudioGroupModel;
class BackgroundTask : public QObject
{
Q_OBJECT
std::function<void(BackgroundTask&)> m_task;
UIMessenger m_threadMessenger;
bool m_cancelled = false;
public:
explicit BackgroundTask(std::function<void(BackgroundTask&)>&& task)
: m_task(std::move(task)), m_threadMessenger(this) {}
bool isCanceled() const { QCoreApplication::processEvents(); return m_cancelled; }
UIMessenger& uiMessenger() { return m_threadMessenger; }
signals:
void setMinimum(int minimum);
void setMaximum(int maximum);
void setValue(int value);
void setLabelText(const QString& text);
void finished();
public slots:
void run() { m_task(*this); emit finished(); }
void cancel() { m_cancelled = true; }
};
class MainWindow : public QMainWindow
{
Q_OBJECT
Ui::MainWindow m_ui;
UIMessenger m_mainMessenger;
ProjectModel* m_projectModel = nullptr;
AudioGroupModel* m_focusAudioGroup = nullptr;
@ -38,11 +65,20 @@ class MainWindow : public QMainWindow
QMetaObject::Connection m_deleteConn;
QMetaObject::Connection m_canEditConn;
BackgroundTask* m_backgroundTask = nullptr;
QProgressDialog* m_backgroundDialog = nullptr;
QThread m_backgroundThread;
void connectMessenger(UIMessenger* messenger, Qt::ConnectionType type);
bool setProjectPath(const QString& path);
void setFocusAudioGroup(AudioGroupModel* group);
void refreshAudioIO();
void refreshMIDIIO();
void startBackgroundTask(const QString& windowTitle, const QString& label,
std::function<void(BackgroundTask&)>&& task);
public:
explicit MainWindow(QWidget* parent = Q_NULLPTR);
~MainWindow();
@ -70,6 +106,22 @@ public slots:
void onTextSelect();
void onTextDelete();
void onBackgroundTaskFinished();
QMessageBox::StandardButton msgInformation(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton msgQuestion(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons =
QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton msgWarning(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
QMessageBox::StandardButton msgCritical(const QString &title,
const QString &text, QMessageBox::StandardButtons buttons = QMessageBox::Ok,
QMessageBox::StandardButton defaultButton = QMessageBox::NoButton);
};

View File

@ -52,6 +52,9 @@
<height>0</height>
</size>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
<widget class="QTableView" name="propertyEditor">
<property name="sizePolicy">

View File

@ -4,111 +4,250 @@
#include "Common.hpp"
#include "athena/YAMLDocWriter.hpp"
QIcon ProjectModel::GroupNode::Icon;
QIcon ProjectModel::SongGroupNode::Icon;
QIcon ProjectModel::SoundGroupNode::Icon;
ProjectModel::ProjectModel(const QString& path, QObject* parent)
: QAbstractItemModel(parent), m_dir(path)
{
m_root = std::make_unique<RootNode>();
GroupNode::Icon = QIcon(":/icons/IconGroup.svg");
SongGroupNode::Icon = QIcon(":/icons/IconSongGroup.svg");
SoundGroupNode::Icon = QIcon(":/icons/IconSoundGroup.svg");
}
ProjectModel::ProjectGroup::ProjectGroup(amuse::IntrusiveAudioGroupData&& data)
: m_data(std::move(data)),
m_proj(amuse::AudioGroupProject::CreateAudioGroupProject(m_data)),
m_pool(amuse::AudioGroupPool::CreateAudioGroupPool(m_data)),
m_sdir(amuse::AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(m_data))
{}
bool ProjectModel::importGroupData(const QString& groupName, amuse::IntrusiveAudioGroupData&& data)
bool ProjectModel::importGroupData(const QString& groupName, const amuse::AudioGroupData& data,
ImportMode mode, UIMessenger& messenger)
{
setIdDatabases();
m_projectDatabase.setIdDatabases();
ProjectGroup& grp = m_groups.insert(std::make_pair(groupName, std::move(data))).first->second;
amuse::AudioGroupDatabase& grp = m_groups.insert(std::make_pair(groupName, data)).first->second;
grp.setIdDatabases();
amuse::AudioGroupProject::BootstrapObjectIDs(grp.m_data);
amuse::AudioGroupProject::BootstrapObjectIDs(data);
return true;
}
bool ProjectModel::extractSamples(ImportMode mode, QWidget* parent)
{
setIdDatabases();
if (!MkPath(m_dir.path(), parent))
if (!MkPath(m_dir.path(), messenger))
return false;
for (auto& g : m_groups)
{
g.second.setIdDatabases();
QDir dir(QFileInfo(m_dir, g.first).filePath());
if (!MkPath(dir.path(), parent))
QDir dir(QFileInfo(m_dir, groupName).filePath());
if (!MkPath(dir.path(), messenger))
return false;
amuse::SystemString sysDir = QStringToSysString(dir.path());
switch (mode)
{
case ImportMode::Original:
g.second.m_sdir.extractAllCompressed(sysDir, g.second.m_data.getSamp());
grp.getSdir().extractAllCompressed(sysDir, data.getSamp());
break;
case ImportMode::WAVs:
g.second.m_sdir.extractAllWAV(sysDir, g.second.m_data.getSamp());
grp.getSdir().extractAllWAV(sysDir, data.getSamp());
break;
case ImportMode::Both:
g.second.m_sdir.extractAllCompressed(sysDir, g.second.m_data.getSamp());
g.second.m_sdir.extractAllWAV(sysDir, g.second.m_data.getSamp());
grp.getSdir().extractAllWAV(sysDir, data.getSamp());
grp.getSdir().extractAllCompressed(sysDir, data.getSamp());
break;
default:
break;
}
}
grp.getProj().toYAML(sysDir);
grp.getPool().toYAML(sysDir);
m_needsReset = true;
return true;
}
bool ProjectModel::saveToFile(QWidget* parent)
bool ProjectModel::saveToFile(UIMessenger& messenger)
{
setIdDatabases();
m_projectDatabase.setIdDatabases();
if (!MkPath(m_dir.path(), parent))
if (!MkPath(m_dir.path(), messenger))
return false;
for (auto& g : m_groups)
{
QDir dir(QFileInfo(m_dir, g.first).filePath());
if (!MkPath(dir.path(), parent))
if (!MkPath(dir.path(), messenger))
return false;
g.second.setIdDatabases();
amuse::SystemString groupPath = QStringToSysString(dir.path());
g.second.m_proj.toYAML(groupPath);
g.second.m_pool.toYAML(groupPath);
g.second.getProj().toYAML(groupPath);
g.second.getPool().toYAML(groupPath);
}
return true;
}
QModelIndex ProjectModel::index(int row, int column, const QModelIndex& parent) const
void ProjectModel::_resetModelData()
{
return createIndex(row, column, nullptr);
beginResetModel();
m_projectDatabase.setIdDatabases();
m_root = std::make_unique<RootNode>();
m_root->reserve(m_groups.size());
for (auto it = m_groups.begin() ; it != m_groups.end() ; ++it)
{
it->second.setIdDatabases();
GroupNode& gn = m_root->makeChild<GroupNode>(it);
auto& songGroups = it->second.getProj().songGroups();
auto& sfxGroups = it->second.getProj().sfxGroups();
auto& soundMacros = it->second.getPool().soundMacros();
auto& tables = it->second.getPool().tables();
auto& keymaps = it->second.getPool().keymaps();
auto& layers = it->second.getPool().layers();
gn.reserve(songGroups.size() + sfxGroups.size() + 4);
for (const auto& grp : SortUnorderedMap(songGroups))
gn.makeChild<SongGroupNode>(grp.first, grp.second.get());
for (const auto& grp : SortUnorderedMap(sfxGroups))
gn.makeChild<SoundGroupNode>(grp.first, grp.second.get());
if (soundMacros.size())
{
CollectionNode& col =
gn.makeChild<CollectionNode>(tr("Sound Macros"), QIcon(":/icons/IconSoundMacro.svg"));
col.reserve(soundMacros.size());
for (const auto& macro : SortUnorderedMap(soundMacros))
col.makeChild<PoolObjectNode<amuse::SoundMacroId, amuse::SoundMacro>>(macro.first, macro.second.get());
}
if (tables.size())
{
auto tablesSort = SortUnorderedMap(tables);
size_t ADSRCount = 0;
size_t curveCount = 0;
for (auto& t : tablesSort)
{
amuse::ITable::Type tp = t.second.get()->Isa();
if (tp == amuse::ITable::Type::ADSR || tp == amuse::ITable::Type::ADSRDLS)
ADSRCount += 1;
else if (tp == amuse::ITable::Type::Curve)
curveCount += 1;
}
if (ADSRCount)
{
CollectionNode& col =
gn.makeChild<CollectionNode>(tr("ADSRs"), QIcon(":/icons/IconADSR.svg"));
col.reserve(ADSRCount);
for (auto& t : tablesSort)
{
amuse::ITable::Type tp = t.second.get()->Isa();
if (tp == amuse::ITable::Type::ADSR || tp == amuse::ITable::Type::ADSRDLS)
col.makeChild<PoolObjectNode<amuse::TableId, amuse::ITable>>(t.first, *t.second.get());
}
}
if (curveCount)
{
CollectionNode& col =
gn.makeChild<CollectionNode>(tr("Curves"), QIcon(":/icons/IconCurve.svg"));
col.reserve(curveCount);
for (auto& t : tablesSort)
{
amuse::ITable::Type tp = t.second.get()->Isa();
if (tp == amuse::ITable::Type::Curve)
col.makeChild<PoolObjectNode<amuse::TableId, amuse::Curve>>(t.first, static_cast<amuse::Curve&>(*t.second.get()));
}
}
}
if (keymaps.size())
{
CollectionNode& col =
gn.makeChild<CollectionNode>(tr("Keymaps"), QIcon(":/icons/IconKeymap.svg"));
col.reserve(keymaps.size());
for (auto& keymap : SortUnorderedMap(keymaps))
col.makeChild<PoolObjectNode<amuse::KeymapId, amuse::Keymap>>(keymap.first, keymap.second.get());
}
if (layers.size())
{
CollectionNode& col =
gn.makeChild<CollectionNode>(tr("Layers"), QIcon(":/icons/IconLayers.svg"));
col.reserve(layers.size());
for (auto& keymap : SortUnorderedMap(layers))
col.makeChild<PoolObjectNode<amuse::LayersId, std::vector<amuse::LayerMapping>>>(keymap.first, keymap.second.get());
}
}
endResetModel();
}
QModelIndex ProjectModel::parent(const QModelIndex& child) const
void ProjectModel::ensureModelData()
{
return {};
if (m_needsReset)
{
_resetModelData();
m_needsReset = false;
}
}
QModelIndex ProjectModel::index(int row, int column, const QModelIndex& parent) const
{
if (!hasIndex(row, column, parent))
return QModelIndex();
INode* parentItem;
if (!parent.isValid())
parentItem = m_root.get();
else
parentItem = static_cast<INode*>(parent.internalPointer());
INode* childItem = parentItem->child(row);
if (childItem)
return createIndex(row, column, childItem);
else
return QModelIndex();
}
QModelIndex ProjectModel::parent(const QModelIndex& index) const
{
if (!index.isValid())
return QModelIndex();
INode* childItem = static_cast<INode*>(index.internalPointer());
INode* parentItem = childItem->parent();
if (parentItem == m_root.get())
return QModelIndex();
return createIndex(parentItem->row(), 0, parentItem);
}
int ProjectModel::rowCount(const QModelIndex& parent) const
{
return 0;
INode* parentItem;
if (!parent.isValid())
parentItem = m_root.get();
else
parentItem = static_cast<INode*>(parent.internalPointer());
return parentItem->childCount();
}
int ProjectModel::columnCount(const QModelIndex& parent) const
{
return 0;
return 1;
}
QVariant ProjectModel::data(const QModelIndex& index, int role) const
{
if (!index.isValid())
return QVariant();
INode* item = static_cast<INode*>(index.internalPointer());
switch (role)
{
case Qt::DisplayRole:
return item->text();
case Qt::DecorationRole:
return item->icon();
default:
return {};
}
}
Qt::ItemFlags ProjectModel::flags(const QModelIndex& index) const
{
if (!index.isValid())
return 0;
return QAbstractItemModel::flags(index);
}
bool ProjectModel::canDelete() const

View File

@ -3,7 +3,10 @@
#include <QAbstractItemModel>
#include <QDir>
#include <QIcon>
#include <map>
#include "Common.hpp"
#include "amuse/AudioGroup.hpp"
#include "amuse/AudioGroupData.hpp"
#include "amuse/AudioGroupProject.hpp"
#include "amuse/AudioGroupPool.hpp"
@ -19,54 +22,134 @@ public:
WAVs,
Both
};
struct ProjectGroup
{
amuse::IntrusiveAudioGroupData m_data;
amuse::AudioGroupProject m_proj;
amuse::AudioGroupPool m_pool;
amuse::AudioGroupSampleDirectory m_sdir;
amuse::NameDB m_soundMacroDb;
amuse::NameDB m_sampleDb;
amuse::NameDB m_tableDb;
amuse::NameDB m_keymapDb;
amuse::NameDB m_layersDb;
explicit ProjectGroup(amuse::IntrusiveAudioGroupData&& data);
void setIdDatabases()
{
amuse::SoundMacroId::CurNameDB = &m_soundMacroDb;
amuse::SampleId::CurNameDB = &m_sampleDb;
amuse::TableId::CurNameDB = &m_tableDb;
amuse::KeymapId::CurNameDB = &m_keymapDb;
amuse::LayersId::CurNameDB = &m_layersDb;
}
};
private:
QDir m_dir;
amuse::NameDB m_songDb;
amuse::NameDB m_sfxDb;
std::map<QString, ProjectGroup> m_groups;
amuse::ProjectDatabase m_projectDatabase;
std::map<QString, amuse::AudioGroupDatabase> m_groups;
void setIdDatabases()
class INode
{
amuse::SongId::CurNameDB = &m_songDb;
amuse::SFXId::CurNameDB = &m_sfxDb;
enum class Type
{
Group, // Top-level group
SongGroup,
SfxGroup,
Collection, // Classified object collection, one of the following:
SoundMacro,
ADSR,
Curve,
Keymap,
Layer
};
INode* m_parent;
std::vector<std::unique_ptr<INode>> m_children;
int m_row;
public:
virtual ~INode() = default;
INode(INode* parent, int row) : m_parent(parent), m_row(row) {}
int childCount() const { return int(m_children.size()); }
INode* child(int row) const { return m_children[row].get(); }
INode* parent() const { return m_parent; }
int row() const { return m_row; }
void reserve(size_t sz) { m_children.reserve(sz); }
template<class T, class... _Args>
T& makeChild(_Args&&... args)
{
m_children.push_back(std::make_unique<T>(this, m_children.size(), std::forward<_Args>(args)...));
return static_cast<T&>(*m_children.back());
}
virtual QString text() const = 0;
virtual QIcon icon() const = 0;
};
struct RootNode : INode
{
RootNode() : INode(nullptr, 0) {}
QString text() const { return {}; }
QIcon icon() const { return {}; }
};
struct GroupNode : INode
{
std::map<QString, amuse::AudioGroupDatabase>::iterator m_it;
GroupNode(INode* parent, int row, std::map<QString, amuse::AudioGroupDatabase>::iterator it)
: INode(parent, row), m_it(it) {}
static QIcon Icon;
QString text() const { return m_it->first; }
QIcon icon() const { return Icon; }
};
struct SongGroupNode : INode
{
amuse::GroupId m_id;
QString m_name;
amuse::SongGroupIndex& m_index;
SongGroupNode(INode* parent, int row, amuse::GroupId id, amuse::SongGroupIndex& index)
: INode(parent, row), m_id(id), m_name(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_index(index) {}
static QIcon Icon;
QString text() const { return m_name; }
QIcon icon() const { return Icon; }
};
struct SoundGroupNode : INode
{
amuse::GroupId m_id;
QString m_name;
amuse::SFXGroupIndex& m_index;
SoundGroupNode(INode* parent, int row, amuse::GroupId id, amuse::SFXGroupIndex& index)
: INode(parent, row), m_id(id), m_name(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_index(index) {}
static QIcon Icon;
QString text() const { return m_name; }
QIcon icon() const { return Icon; }
};
struct CollectionNode : INode
{
QString m_name;
QIcon m_icon;
CollectionNode(INode* parent, int row, const QString& name, const QIcon& icon)
: INode(parent, row), m_name(name), m_icon(icon) {}
QString text() const { return m_name; }
QIcon icon() const { return m_icon; }
};
template <class ID, class T>
struct PoolObjectNode : INode
{
ID m_id;
QString m_name;
T& m_obj;
PoolObjectNode(INode* parent, int row, ID id, T& obj)
: INode(parent, row), m_id(id), m_name(ID::CurNameDB->resolveNameFromId(id).data()), m_obj(obj) {}
QString text() const { return m_name; }
QIcon icon() const { return {}; }
};
std::unique_ptr<RootNode> m_root;
bool m_needsReset = false;
void _resetModelData();
public:
explicit ProjectModel(const QString& path, QObject* parent = Q_NULLPTR);
bool importGroupData(const QString& groupName, amuse::IntrusiveAudioGroupData&& data);
bool extractSamples(ImportMode mode, QWidget* parent);
bool saveToFile(QWidget* parent);
bool importGroupData(const QString& groupName, const amuse::AudioGroupData& data,
ImportMode mode, UIMessenger& messenger);
bool saveToFile(UIMessenger& messenger);
void ensureModelData();
QModelIndex index(int row, int column, const QModelIndex& parent = QModelIndex()) const;
QModelIndex parent(const QModelIndex& child) const;
int rowCount(const QModelIndex& parent = QModelIndex()) const;
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
QString path() const { return m_dir.path(); }
bool canDelete() const;

View File

@ -0,0 +1,81 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconADSR.svg">
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="30.086227"
inkscape:cx="7.2902772"
inkscape:cy="8.0995259"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="267"
inkscape:window-y="1022"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
visible="true"
empspacing="1"
id="grid4173"
type="xygrid" />
</sodipodi:namedview>
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-292.76665)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<rect
style="fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.39687499;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect1235"
width="3.7041667"
height="3.7041805"
x="0.26458335"
y="293.03122" />
<path
style="fill:none;stroke:#000000;stroke-width:0.5291667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.26458333,296.7354 1.0583334,293.47246 1.5875,294.35415 H 2.9104167 L 3.96875,296.7354"
id="path1834"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,87 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconCurve.svg">
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="30.086227"
inkscape:cx="-2.7199818"
inkscape:cy="9.4290379"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="267"
inkscape:window-y="1022"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
visible="true"
empspacing="1"
id="grid4173"
type="xygrid" />
</sodipodi:namedview>
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-292.76665)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<rect
style="fill:#00ffcc;fill-opacity:1;stroke:none;stroke-width:0.39687499;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect1235"
width="3.7041667"
height="3.7041805"
x="0.26458335"
y="293.03122" />
<path
style="fill:none;stroke:#808080;stroke-width:0.52916667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:0.52916667, 0.52916666999999995;stroke-opacity:1;stroke-dashoffset:0"
d="m 0.26458333,292.76665 0,3.96875 H 4.2333333"
id="path1231"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#000000;stroke-width:0.5291667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0,296.7354 c 2.0787582,0 3.96875,-2.11667 3.96875,-3.96875"
id="path1233"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.91+devel+osxmenu r12922"
sodipodi:docname="IconSoundGroup.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="19.66"
inkscape:cx="6.046968"
inkscape:cy="6.9530957"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="231"
inkscape:window-y="82"
inkscape:window-maximized="0"
showborder="true"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="opacity:1;fill:#fbd365;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.67643285;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.3229167,293.04538 c 0.2645833,0 0.5291666,0.25044 0.5291666,0.51502 l 0,0.26458 1.8488138,0.0102 c 0.3165926,0.002 0.5239173,0.20975 0.5245413,0.52471 l 0.00313,1.57936 c 6.239e-4,0.31495 -0.2079439,0.53411 -0.5245413,0.53411 l -3.17666623,0 C 0.21076252,296.47339 0,296.2566 0,295.94165 c 4.054e-5,-0.82258 0,-2.38125 0,-2.38125 0,-0.26458 0.26458333,-0.51612 0.52916667,-0.51612 z"
id="rect4141"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccssssscccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -14,7 +14,7 @@
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.91+devel+osxmenu r12922"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconLayers.svg">
<defs
id="defs4" />
@ -26,7 +26,7 @@
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="4.92"
inkscape:cx="19.961362"
inkscape:cx="-13.575223"
inkscape:cy="7.1823475"
inkscape:document-units="px"
inkscape:current-layer="layer1"
@ -34,8 +34,8 @@
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="131"
inkscape:window-y="148"
inkscape:window-x="1749"
inkscape:window-y="677"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
@ -55,7 +55,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@ -0,0 +1,86 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconNewADSR.svg">
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="30.086227"
inkscape:cx="7.2902772"
inkscape:cy="8.0995259"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="341"
inkscape:window-y="1113"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
visible="true"
empspacing="1"
id="grid4173"
type="xygrid" />
</sodipodi:namedview>
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-292.76665)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<rect
style="fill:#ff6600;fill-opacity:1;stroke:none;stroke-width:0.39687499;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect1235"
width="3.7041667"
height="3.7041805"
x="0.26458335"
y="293.03122" />
<path
style="fill:none;stroke:#000000;stroke-width:0.5291667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 0.26458333,296.7354 1.0583334,293.47246 1.5875,294.35415 H 2.9104167 L 3.96875,296.7354"
id="path1834"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<path
inkscape:connector-curvature="0"
id="path5323"
style="fill:#ff0000;fill-rule:evenodd;stroke:#df0000;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.8386428,295.34071 0.9362114,0.93622 m -0.9362104,-10e-6 0.9362094,-0.9362 m -1.1301062,0.4681 H 3.96875 m -0.6620015,0.662 v -1.324" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconNewCurve.svg">
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="30.086227"
inkscape:cx="8.2484922"
inkscape:cy="9.4290379"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="459"
inkscape:window-y="172"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
visible="true"
empspacing="1"
id="grid4173"
type="xygrid" />
</sodipodi:namedview>
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-292.76665)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<rect
style="fill:#00ffcc;fill-opacity:1;stroke:none;stroke-width:0.39687499;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
id="rect1235"
width="3.7041667"
height="3.7041805"
x="0.26458335"
y="293.03122" />
<path
style="fill:none;stroke:#808080;stroke-width:0.52916667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:0.52916667, 0.52916666999999995;stroke-opacity:1;stroke-dashoffset:0"
d="m 0.26458333,292.76665 0,3.96875 H 4.2333333"
id="path1231"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#000000;stroke-width:0.5291667;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 0,296.7354 c 2.0787582,0 3.96875,-2.11667 3.96875,-3.96875"
id="path1233"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
inkscape:connector-curvature="0"
id="path5323"
style="fill:#ff0000;fill-rule:evenodd;stroke:#df0000;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.8386428,295.34071 0.9362114,0.93622 m -0.9362104,-10e-6 0.9362094,-0.9362 m -1.1301062,0.4681 H 3.96875 m -0.6620015,0.662 v -1.324" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.91+devel+osxmenu r12922"
sodipodi:docname="NewSoundGroup.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="19.66"
inkscape:cx="6.046968"
inkscape:cy="6.9530957"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="231"
inkscape:window-y="82"
inkscape:window-maximized="0"
showborder="true"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
empspacing="1"
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-292.76665)">
<path
style="opacity:1;fill:#fbd365;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.67643285;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.3229167,293.04538 c 0.2645833,0 0.5291666,0.25044 0.5291666,0.51502 l 0,0.26458 1.8488138,0.0102 c 0.3165926,0.002 0.5239173,0.20975 0.5245413,0.52471 l 0.00313,1.57936 c 6.239e-4,0.31495 -0.2079439,0.53411 -0.5245413,0.53411 l -3.17666623,0 C 0.21076252,296.47339 0,296.2566 0,295.94165 c 4.054e-5,-0.82258 0,-2.38125 0,-2.38125 0,-0.26458 0.26458333,-0.51612 0.52916667,-0.51612 z"
id="rect4141"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccssssscccc" />
<path
id="path5876"
style="fill:#ff0000;fill-rule:evenodd;stroke:#df0000;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2.8386428,295.07809 0.9362114,0.93622 m -0.9362104,-1e-5 0.9362094,-0.9362 m -1.1301062,0.4681 1.324003,0 m -0.6620015,0.662 0,-1.324" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -14,8 +14,8 @@
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.91+devel+osxmenu r12922"
sodipodi:docname="NewSongGroup.svg">
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconNewSongGroup.svg">
<defs
id="defs4" />
<sodipodi:namedview
@ -26,7 +26,7 @@
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="29.730996"
inkscape:cx="3.7002328"
inkscape:cx="3.0052979"
inkscape:cy="9.0045012"
inkscape:document-units="px"
inkscape:current-layer="layer1"
@ -35,7 +35,7 @@
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-y="40"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
@ -55,7 +55,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@ -65,11 +65,16 @@
id="layer1"
transform="translate(0,-292.76665)">
<path
style="opacity:1;fill:#04e2ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.67643285;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
style="opacity:1;fill:#ff5599;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.67643285;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.3229167,293.04538 c 0.2645833,0 0.5291666,0.25044 0.5291666,0.51502 l 0,0.26458 1.8488138,0.0102 c 0.3165926,0.002 0.5239173,0.20975 0.5245413,0.52471 l 0.00313,1.57936 c 6.239e-4,0.31495 -0.2079439,0.53411 -0.5245413,0.53411 l -3.17666623,0 C 0.21076252,296.47339 0,296.2566 0,295.94165 c 4.054e-5,-0.82258 0,-2.38125 0,-2.38125 0,-0.26458 0.26458333,-0.51612 0.52916667,-0.51612 z"
id="rect4141"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccssssscccc" />
<path
inkscape:connector-curvature="0"
id="path868"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-miterlimit:4;stroke-dasharray:none"
d="m 1.8675862,295.87272 a 0.57456181,0.36088177 0 0 1 -0.5745618,0.36089 0.57456181,0.36088177 0 0 1 -0.57456172,-0.36089 0.57456181,0.36088177 0 0 1 0.57456172,-0.36088 0.57456181,0.36088177 0 0 1 0.5745618,0.36088 z m 1.3298265,-0.001 a 0.57456181,0.36088177 0 0 1 -0.5745619,0.36089 0.57456181,0.36088177 0 0 1 -0.5745618,-0.36089 0.57456181,0.36088177 0 0 1 0.5745618,-0.36088 0.57456181,0.36088177 0 0 1 0.5745619,0.36088 z m -0.253445,-1.78193 0.253445,0.006 v 1.77617 c -0.2177221,0.0701 -0.286996,-0.0173 -0.3755473,-0.1676 l 0.00387,-0.79478 H 1.868454 v 0.95424 c -0.2323614,-0.008 -0.2369279,-0.14254 -0.3755412,-0.22458 v -1.54879 z" />
<path
id="path5323"
style="fill:#ff0000;fill-rule:evenodd;stroke:#df0000;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -14,8 +14,8 @@
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.91+devel+osxmenu r12922"
sodipodi:docname="NewSoundGroup.svg">
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconNewSoundGroup.svg">
<defs
id="defs4" />
<sodipodi:namedview
@ -25,17 +25,17 @@
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="19.66"
inkscape:cx="6.046968"
inkscape:zoom="31.94"
inkscape:cx="9.7896878"
inkscape:cy="6.9530957"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="231"
inkscape:window-y="82"
inkscape:window-height="777"
inkscape:window-x="2038"
inkscape:window-y="1136"
inkscape:window-maximized="0"
showborder="true"
objecttolerance="4"
@ -55,7 +55,7 @@
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
@ -65,11 +65,17 @@
id="layer1"
transform="translate(0,-292.76665)">
<path
style="opacity:1;fill:#fbd365;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.67643285;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
style="opacity:1;fill:#04e2ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.67643285;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.3229167,293.04538 c 0.2645833,0 0.5291666,0.25044 0.5291666,0.51502 l 0,0.26458 1.8488138,0.0102 c 0.3165926,0.002 0.5239173,0.20975 0.5245413,0.52471 l 0.00313,1.57936 c 6.239e-4,0.31495 -0.2079439,0.53411 -0.5245413,0.53411 l -3.17666623,0 C 0.21076252,296.47339 0,296.2566 0,295.94165 c 4.054e-5,-0.82258 0,-2.38125 0,-2.38125 0,-0.26458 0.26458333,-0.51612 0.52916667,-0.51612 z"
id="rect4141"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccssssscccc" />
<path
id="path1184"
title="sin(x*4*pi)*sin(x*pi)"
d="m 0.52916699,295.1479 c 0.11759258,-1.5e-4 0.23518516,-0.18652 0.35277774,-0.31191 0.11759258,-0.1254 0.23518517,-0.1236 0.35277777,0.10832 0.1175926,0.23193 0.2351851,0.66463 0.3527777,0.89812 0.1175926,0.23349 0.2351852,0.20457 0.3527778,-0.10832 0.1175925,-0.3129 0.2351851,-0.85907 0.3527777,-1.17242 0.1175926,-0.31334 0.2351852,-0.3414 0.3527777,-0.10832 0.1175926,0.23308 0.2351852,0.66584 0.3527778,0.89812 0.1175926,0.23228 0.2351851,0.23355 0.3527778,0.10832 0.1175925,-0.12522 0.235185,-0.31176 0.3527777,-0.31191"
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.396875;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
inkscape:connector-curvature="0" />
<path
id="path5876"
style="fill:#ff0000;fill-rule:evenodd;stroke:#df0000;stroke-width:0.26458332;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -14,7 +14,7 @@
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.91+devel+osxmenu r12922"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconSongGroup.svg">
<defs
id="defs4" />
@ -25,17 +25,17 @@
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="29.730996"
inkscape:cx="3.7002328"
inkscape:cy="9.0045012"
inkscape:zoom="34.350339"
inkscape:cx="8.8858389"
inkscape:cy="8.8343343"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-height="787"
inkscape:window-x="0"
inkscape:window-y="23"
inkscape:window-x="1944"
inkscape:window-y="288"
inkscape:window-maximized="0"
showborder="false"
objecttolerance="4"
@ -45,7 +45,7 @@
type="xygrid"
id="grid4173"
empspacing="1"
visible="false" />
visible="true" />
</sodipodi:namedview>
<metadata
id="metadata7">
@ -65,10 +65,19 @@
id="layer1"
transform="translate(0,-292.76665)">
<path
style="opacity:1;fill:#04e2ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.67643285;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
style="opacity:1;fill:#ff5599;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.67643285;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.3229167,293.04538 c 0.2645833,0 0.5291666,0.25044 0.5291666,0.51502 l 0,0.26458 1.8488138,0.0102 c 0.3165926,0.002 0.5239173,0.20975 0.5245413,0.52471 l 0.00313,1.57936 c 6.239e-4,0.31495 -0.2079439,0.53411 -0.5245413,0.53411 l -3.17666623,0 C 0.21076252,296.47339 0,296.2566 0,295.94165 c 4.054e-5,-0.82258 0,-2.38125 0,-2.38125 0,-0.26458 0.26458333,-0.51612 0.52916667,-0.51612 z"
id="rect4141"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccssssscccc" />
<g
aria-label="♬"
style="font-style:normal;font-weight:normal;font-size:1.66674709px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.31251505"
id="text820"
transform="matrix(1.1529095,0,0,1.1529095,-0.32840767,-44.985003)" />
<path
id="path868"
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332;stroke-miterlimit:4;stroke-dasharray:none"
d="m 1.8675862,295.87272 a 0.57456183,0.36088178 0 0 1 -0.5745618,0.36089 0.57456183,0.36088178 0 0 1 -0.57456171,-0.36089 0.57456183,0.36088178 0 0 1 0.57456171,-0.36088 0.57456183,0.36088178 0 0 1 0.5745618,0.36088 z m 1.3298265,-0.001 a 0.57456183,0.36088178 0 0 1 -0.5745619,0.36089 0.57456183,0.36088178 0 0 1 -0.5745618,-0.36089 0.57456183,0.36088178 0 0 1 0.5745618,-0.36088 0.57456183,0.36088178 0 0 1 0.5745619,0.36088 z m -0.253445,-1.78193 0.253445,0.006 v 1.77617 c -0.2177221,0.0701 -0.286996,-0.0173 -0.3755473,-0.1676 l 0.00387,-0.79478 H 1.868454 v 0.95424 c -0.2323614,-0.008 -0.2369279,-0.14254 -0.3755412,-0.22458 v -1.54879 z" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@ -14,10 +14,8 @@
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.91+devel+osxmenu r12922"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconSoundGroup.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#393939"
@ -25,28 +23,30 @@
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="19.66"
inkscape:cx="6.046968"
inkscape:cy="6.9530957"
inkscape:zoom="34.25"
inkscape:cx="8.0808195"
inkscape:cy="8.452632"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1260"
inkscape:window-width="1258"
inkscape:window-height="787"
inkscape:window-x="231"
inkscape:window-y="82"
inkscape:window-x="1970"
inkscape:window-y="1150"
inkscape:window-maximized="0"
showborder="true"
showborder="false"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
type="xygrid"
id="grid4173"
visible="true"
empspacing="1"
visible="true" />
id="grid4173"
type="xygrid" />
</sodipodi:namedview>
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
@ -60,15 +60,21 @@
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
transform="translate(0,-292.76665)"
id="layer1"
transform="translate(0,-292.76665)">
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<path
style="opacity:1;fill:#fbd365;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.67643285;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 1.3229167,293.04538 c 0.2645833,0 0.5291666,0.25044 0.5291666,0.51502 l 0,0.26458 1.8488138,0.0102 c 0.3165926,0.002 0.5239173,0.20975 0.5245413,0.52471 l 0.00313,1.57936 c 6.239e-4,0.31495 -0.2079439,0.53411 -0.5245413,0.53411 l -3.17666623,0 C 0.21076252,296.47339 0,296.2566 0,295.94165 c 4.054e-5,-0.82258 0,-2.38125 0,-2.38125 0,-0.26458 0.26458333,-0.51612 0.52916667,-0.51612 z"
id="rect4141"
sodipodi:nodetypes="cccssssscccc"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccssssscccc" />
id="rect4141"
d="m 1.3229167,293.04538 c 0.2645833,0 0.5291666,0.25044 0.5291666,0.51502 l 0,0.26458 1.8488138,0.0102 c 0.3165926,0.002 0.5239173,0.20975 0.5245413,0.52471 l 0.00313,1.57936 c 6.239e-4,0.31495 -0.2079439,0.53411 -0.5245413,0.53411 l -3.17666623,0 C 0.21076252,296.47339 0,296.2566 0,295.94165 c 4.054e-5,-0.82258 0,-2.38125 0,-2.38125 0,-0.26458 0.26458333,-0.51612 0.52916667,-0.51612 z"
style="opacity:1;fill:#04e2ff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1.67643285;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path1184"
title="sin(x*4*pi)*sin(x*pi)"
d="m 0.52916698,295.1479 c 0.11759258,-1.5e-4 0.23518516,-0.18652 0.35277775,-0.31191 0.11759258,-0.1254 0.23518517,-0.1236 0.35277777,0.10832 0.1175926,0.23193 0.2351851,0.66463 0.3527777,0.89812 0.1175926,0.23349 0.2351852,0.20457 0.3527778,-0.10832 0.1175925,-0.3129 0.2351851,-0.85907 0.3527777,-1.17242 0.1175926,-0.31334 0.2351852,-0.3414 0.3527777,-0.10832 0.1175926,0.23308 0.2351852,0.66584 0.3527778,0.89812 0.1175926,0.23228 0.2351852,0.23355 0.3527777,0.10832 0.1175926,-0.12522 0.2351852,-0.31176 0.3527778,-0.31191"
style="fill:none;fill-opacity:1;stroke:#000000;stroke-width:0.396875;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;stroke-linecap:round;paint-order:normal"
inkscape:connector-curvature="0" />
</g>
</svg>

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -13,6 +13,12 @@
<file>IconSongGroup.svg</file>
<file>IconSoundGroup.svg</file>
<file>IconSoundMacro.svg</file>
<file>IconGroup.svg</file>
<file>IconNewGroup.svg</file>
<file>IconADSR.svg</file>
<file>IconNewADSR.svg</file>
<file>IconCurve.svg</file>
<file>IconNewCurve.svg</file>
</qresource>
<qresource prefix="/bg">
<file>FaceGrey.svg</file>

View File

@ -29,6 +29,48 @@ public:
const AudioGroupProject& getProj() const { return m_proj; }
const AudioGroupPool& getPool() const { return m_pool; }
const AudioGroupSampleDirectory& getSdir() const { return m_sdir; }
AudioGroupProject& getProj() { return m_proj; }
AudioGroupPool& getPool() { return m_pool; }
AudioGroupSampleDirectory& getSdir() { return m_sdir; }
};
class AudioGroupDatabase : public AudioGroup
{
amuse::NameDB m_soundMacroDb;
amuse::NameDB m_sampleDb;
amuse::NameDB m_tableDb;
amuse::NameDB m_keymapDb;
amuse::NameDB m_layersDb;
public:
explicit AudioGroupDatabase(const AudioGroupData& data)
: AudioGroup(data) {}
explicit AudioGroupDatabase(SystemStringView groupPath)
: AudioGroup(groupPath) {}
void setIdDatabases()
{
amuse::SoundMacroId::CurNameDB = &m_soundMacroDb;
amuse::SampleId::CurNameDB = &m_sampleDb;
amuse::TableId::CurNameDB = &m_tableDb;
amuse::KeymapId::CurNameDB = &m_keymapDb;
amuse::LayersId::CurNameDB = &m_layersDb;
}
};
class ProjectDatabase
{
amuse::NameDB m_songDb;
amuse::NameDB m_sfxDb;
amuse::NameDB m_groupDb;
public:
void setIdDatabases()
{
amuse::SongId::CurNameDB = &m_songDb;
amuse::SFXId::CurNameDB = &m_sfxDb;
amuse::GroupId::CurNameDB = &m_groupDb;
}
};
}

View File

@ -664,7 +664,7 @@ struct SoundMacro
AT_DECL_DNAV
Value<atUint8> midiControl;
Value<atUint16> scalingPercentage;
Value<bool> combine;
Value<atInt8> combine;
Value<bool> isVar;
Value<atUint8> fineScaling;
bool Do(SoundMacroState& st, Voice& vox) const;
@ -676,7 +676,7 @@ struct SoundMacro
AT_DECL_DNAV
Value<atUint8> midiControl;
Value<atUint16> scalingPercentage;
Value<bool> combine;
Value<atInt8> combine;
Value<bool> isVar;
Value<atUint8> fineScaling;
bool Do(SoundMacroState& st, Voice& vox) const;
@ -688,7 +688,7 @@ struct SoundMacro
AT_DECL_DNAV
Value<atUint8> midiControl;
Value<atUint16> scalingPercentage;
Value<bool> combine;
Value<atInt8> combine;
Value<bool> isVar;
Value<atUint8> fineScaling;
bool Do(SoundMacroState& st, Voice& vox) const;
@ -700,7 +700,7 @@ struct SoundMacro
AT_DECL_DNAV
Value<atUint8> midiControl;
Value<atUint16> scalingPercentage;
Value<bool> combine;
Value<atInt8> combine;
Value<bool> isVar;
Value<atUint8> fineScaling;
bool Do(SoundMacroState& st, Voice& vox) const;
@ -712,7 +712,7 @@ struct SoundMacro
AT_DECL_DNAV
Value<atUint8> midiControl;
Value<atUint16> scalingPercentage;
Value<bool> combine;
Value<atInt8> combine;
Value<bool> isVar;
Value<atUint8> fineScaling;
bool Do(SoundMacroState& st, Voice& vox) const;
@ -724,7 +724,7 @@ struct SoundMacro
AT_DECL_DNAV
Value<atUint8> midiControl;
Value<atUint16> scalingPercentage;
Value<bool> combine;
Value<atInt8> combine;
Value<bool> isVar;
Value<atUint8> fineScaling;
bool Do(SoundMacroState& st, Voice& vox) const;
@ -736,7 +736,7 @@ struct SoundMacro
AT_DECL_DNAV
Value<atUint8> midiControl;
Value<atUint16> scalingPercentage;
Value<bool> combine;
Value<atInt8> combine;
Value<bool> isVar;
Value<atUint8> fineScaling;
bool Do(SoundMacroState& st, Voice& vox) const;
@ -748,7 +748,7 @@ struct SoundMacro
AT_DECL_DNAV
Value<atUint8> midiControl;
Value<atUint16> scalingPercentage;
Value<bool> combine;
Value<atInt8> combine;
Value<bool> isVar;
Value<atUint8> fineScaling;
bool Do(SoundMacroState& st, Voice& vox) const;
@ -760,7 +760,7 @@ struct SoundMacro
AT_DECL_DNAV
Value<atUint8> midiControl;
Value<atUint16> scalingPercentage;
Value<bool> combine;
Value<atInt8> combine;
Value<bool> isVar;
Value<atUint8> fineScaling;
bool Do(SoundMacroState& st, Voice& vox) const;
@ -772,7 +772,7 @@ struct SoundMacro
AT_DECL_DNAV
Value<atUint8> midiControl;
Value<atUint16> scalingPercentage;
Value<bool> combine;
Value<atInt8> combine;
Value<bool> isVar;
Value<atUint8> fineScaling;
bool Do(SoundMacroState& st, Voice& vox) const;
@ -784,7 +784,7 @@ struct SoundMacro
AT_DECL_DNAV
Value<atUint8> midiControl;
Value<atUint16> scalingPercentage;
Value<bool> combine;
Value<atInt8> combine;
Value<bool> isVar;
Value<atUint8> fineScaling;
bool Do(SoundMacroState& st, Voice& vox) const;
@ -796,7 +796,7 @@ struct SoundMacro
AT_DECL_DNAV
Value<atUint8> midiControl;
Value<atUint16> scalingPercentage;
Value<bool> combine;
Value<atInt8> combine;
Value<bool> isVar;
Value<atUint8> fineScaling;
bool Do(SoundMacroState& st, Voice& vox) const;
@ -808,7 +808,7 @@ struct SoundMacro
AT_DECL_DNAV
Value<atUint8> midiControl;
Value<atUint16> scalingPercentage;
Value<bool> combine;
Value<atInt8> combine;
Value<bool> isVar;
Value<atUint8> fineScaling;
bool Do(SoundMacroState& st, Voice& vox) const;
@ -820,7 +820,7 @@ struct SoundMacro
AT_DECL_DNAV
Value<atUint8> midiControl;
Value<atUint16> scalingPercentage;
Value<bool> combine;
Value<atInt8> combine;
Value<bool> isVar;
Value<atUint8> fineScaling;
bool Do(SoundMacroState& st, Voice& vox) const;
@ -1156,6 +1156,15 @@ public:
static AudioGroupPool CreateAudioGroupPool(const AudioGroupData& data);
static AudioGroupPool CreateAudioGroupPool(SystemStringView groupPath);
const std::unordered_map<SoundMacroId, SoundMacro>& soundMacros() const { return m_soundMacros; }
const std::unordered_map<TableId, std::unique_ptr<ITable>>& tables() const { return m_tables; }
const std::unordered_map<KeymapId, Keymap>& keymaps() const { return m_keymaps; }
const std::unordered_map<LayersId, std::vector<LayerMapping>>& layers() const { return m_layers; }
std::unordered_map<SoundMacroId, SoundMacro>& soundMacros() { return m_soundMacros; }
std::unordered_map<TableId, std::unique_ptr<ITable>>& tables() { return m_tables; }
std::unordered_map<KeymapId, Keymap>& keymaps() { return m_keymaps; }
std::unordered_map<LayersId, std::vector<LayerMapping>>& layers() { return m_layers; }
const SoundMacro* soundMacro(ObjectId id) const;
const Keymap* keymap(ObjectId id) const;
const std::vector<LayerMapping>* layer(ObjectId id) const;
@ -1164,6 +1173,11 @@ public:
const Curve* tableAsCurves(ObjectId id) const;
bool toYAML(SystemStringView groupPath) const;
AudioGroupPool(const AudioGroupPool&) = delete;
AudioGroupPool& operator=(const AudioGroupPool&) = delete;
AudioGroupPool(AudioGroupPool&&) = default;
AudioGroupPool& operator=(AudioGroupPool&&) = default;
};
}

View File

@ -25,7 +25,7 @@ GroupHeader : BigDNA
{
AT_DECL_DNA
Value<atUint32, DNAEn> groupEndOff;
Value<atUint16, DNAEn> groupId;
GroupIdDNA<DNAEn> groupId;
Value<GroupType, DNAEn> type;
Value<atUint32, DNAEn> soundMacroIdsOff;
Value<atUint32, DNAEn> samplIdsOff;
@ -180,8 +180,8 @@ struct SFXGroupIndex : AudioGroupIndex
/** Collection of SongGroup and SFXGroup indexes */
class AudioGroupProject
{
std::unordered_map<int, SongGroupIndex> m_songGroups;
std::unordered_map<int, SFXGroupIndex> m_sfxGroups;
std::unordered_map<GroupId, SongGroupIndex> m_songGroups;
std::unordered_map<GroupId, SFXGroupIndex> m_sfxGroups;
AudioGroupProject() = default;
AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag);
@ -199,10 +199,17 @@ public:
const SongGroupIndex* getSongGroupIndex(int groupId) const;
const SFXGroupIndex* getSFXGroupIndex(int groupId) const;
const std::unordered_map<int, SongGroupIndex>& songGroups() const { return m_songGroups; }
const std::unordered_map<int, SFXGroupIndex>& sfxGroups() const { return m_sfxGroups; }
const std::unordered_map<GroupId, SongGroupIndex>& songGroups() const { return m_songGroups; }
const std::unordered_map<GroupId, SFXGroupIndex>& sfxGroups() const { return m_sfxGroups; }
std::unordered_map<GroupId, SongGroupIndex>& songGroups() { return m_songGroups; }
std::unordered_map<GroupId, SFXGroupIndex>& sfxGroups() { return m_sfxGroups; }
bool toYAML(SystemStringView groupPath) const;
AudioGroupProject(const AudioGroupProject&) = delete;
AudioGroupProject& operator=(const AudioGroupProject&) = delete;
AudioGroupProject(AudioGroupProject&&) = default;
AudioGroupProject& operator=(AudioGroupProject&&) = default;
};
}

View File

@ -268,6 +268,11 @@ public:
void extractAllWAV(amuse::SystemStringView destDir, const unsigned char* samp) const;
void extractCompressed(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const;
void extractAllCompressed(amuse::SystemStringView destDir, const unsigned char* samp) const;
AudioGroupSampleDirectory(const AudioGroupSampleDirectory&) = delete;
AudioGroupSampleDirectory& operator=(const AudioGroupSampleDirectory&) = delete;
AudioGroupSampleDirectory(AudioGroupSampleDirectory&&) = default;
AudioGroupSampleDirectory& operator=(AudioGroupSampleDirectory&&) = default;
};
}

View File

@ -86,6 +86,7 @@ DECL_ID_TYPE(KeymapId)
DECL_ID_TYPE(LayersId)
DECL_ID_TYPE(SongId)
DECL_ID_TYPE(SFXId)
DECL_ID_TYPE(GroupId)
/* MusyX has object polymorphism between Keymaps and Layers when
* referenced by a song group's page object. When the upper bit is set,
@ -417,12 +418,26 @@ struct PCDataTag
{
};
template <class T>
static std::vector<std::pair<typename T::key_type,
std::reference_wrapper<typename T::mapped_type>>> SortUnorderedMap(T& um)
{
std::vector<std::pair<typename T::key_type, std::reference_wrapper<typename T::mapped_type>>> ret;
ret.reserve(um.size());
for (auto& p : um)
ret.emplace_back(p.first, p.second);
std::sort(ret.begin(), ret.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
return ret;
}
template <class T>
static std::vector<std::pair<typename T::key_type,
std::reference_wrapper<const typename T::mapped_type>>> SortUnorderedMap(const T& um)
{
std::vector<std::pair<typename T::key_type,
std::reference_wrapper<const typename T::mapped_type>>> ret(um.cbegin(), um.cend());
std::vector<std::pair<typename T::key_type, std::reference_wrapper<const typename T::mapped_type>>> ret;
ret.reserve(um.size());
for (const auto& p : um)
ret.emplace_back(p.first, p.second);
std::sort(ret.begin(), ret.end(), [](const auto& a, const auto& b) { return a.first < b.first; });
return ret;
}
@ -444,6 +459,7 @@ DECL_ID_HASH(KeymapId)
DECL_ID_HASH(LayersId)
DECL_ID_HASH(SongId)
DECL_ID_HASH(SFXId)
DECL_ID_HASH(GroupId)
}
namespace amuse
@ -458,6 +474,7 @@ struct NameDB
Layer,
Song,
SFX,
Group,
Sample
};

View File

@ -210,6 +210,15 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(const AudioGroupDat
}
}
std::string ParseStringSlashId(const std::string& str, uint16_t& idOut)
{
size_t slashPos = str.find('/');
if (slashPos == std::string::npos)
return {};
idOut = uint16_t(strtoul(str.data() + slashPos + 1, nullptr, 0));
return {str.begin(), str.begin() + slashPos};
}
AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView groupPath)
{
AudioGroupProject ret;
@ -222,15 +231,20 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView gr
athena::io::YAMLDocReader r;
if (r.parse(&fi) && r.ValidateClassType("amuse::Project"))
{
size_t songGroupCount;
if (auto __v = r.enterSubVector("songGroups", songGroupCount))
if (auto __v = r.enterSubRecord("songGroups"))
{
ret.m_songGroups.reserve(songGroupCount);
for (int g = 0; g < songGroupCount; ++g)
ret.m_songGroups.reserve(r.getCurNode()->m_mapChildren.size());
for (const auto& grp : r.getCurNode()->m_mapChildren)
{
if (auto __r = r.enterSubRecord(nullptr))
if (auto __r = r.enterSubRecord(grp.first.c_str()))
{
SongGroupIndex& idx = ret.m_songGroups[g];
uint16_t groupId;
std::string groupName = ParseStringSlashId(grp.first, groupId);
if (groupName.empty() || groupId == 0xffff)
continue;
GroupId::CurNameDB->registerPair(groupName, groupId);
SongGroupIndex& idx = ret.m_songGroups[groupId];
if (auto __v2 = r.enterSubRecord("normPages"))
{
idx.m_normPages.reserve(r.getCurNode()->m_mapChildren.size());
@ -253,8 +267,12 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView gr
size_t chanCount;
if (auto __v3 = r.enterSubVector(song.first.c_str(), chanCount))
{
ObjectId songId = SongId::CurNameDB->generateId(NameDB::Type::Song);
SongId::CurNameDB->registerPair(song.first, songId);
uint16_t songId;
std::string songName = ParseStringSlashId(song.first, songId);
if (songName.empty() || songId == 0xffff)
continue;
SongId::CurNameDB->registerPair(songName, songId);
std::array<SongGroupIndex::MIDISetup, 16>& setup = idx.m_midiSetups[songId];
for (int i = 0; i < 16 && i < chanCount; ++i)
if (auto __r2 = r.enterSubRecord(nullptr))
@ -266,20 +284,28 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView gr
}
}
size_t sfxGroupCount;
if (auto __v = r.enterSubVector("sfxGroups", sfxGroupCount))
if (auto __v = r.enterSubRecord("sfxGroups"))
{
ret.m_sfxGroups.reserve(sfxGroupCount);
for (int g = 0; g < sfxGroupCount; ++g)
ret.m_sfxGroups.reserve(r.getCurNode()->m_mapChildren.size());
for (const auto& grp : r.getCurNode()->m_mapChildren)
{
if (auto __r = r.enterSubRecord(nullptr))
{
SFXGroupIndex& idx = ret.m_sfxGroups[g];
uint16_t groupId;
std::string groupName = ParseStringSlashId(grp.first, groupId);
if (groupName.empty() || groupId == 0xffff)
continue;
GroupId::CurNameDB->registerPair(groupName, groupId);
SFXGroupIndex& idx = ret.m_sfxGroups[groupId];
for (const auto& sfx : r.getCurNode()->m_mapChildren)
if (auto __r2 = r.enterSubRecord(sfx.first.c_str()))
{
ObjectId sfxId = SFXId::CurNameDB->generateId(NameDB::Type::SFX);
SFXId::CurNameDB->registerPair(sfx.first, sfxId);
uint16_t sfxId;
std::string sfxName = ParseStringSlashId(sfx.first, sfxId);
if (sfxName.empty() || sfxId == 0xffff)
continue;
SFXId::CurNameDB->registerPair(sfxName, sfxId);
idx.m_sfxEntries[sfxId].read(r);
}
}
@ -321,6 +347,8 @@ void AudioGroupProject::BootstrapObjectIDs(athena::io::IStreamReader& r, GCNData
GroupHeader<athena::Big> header;
header.read(r);
GroupId::CurNameDB->registerPair(NameDB::generateName(header.groupId, NameDB::Type::Group), header.groupId);
/* Sound Macros */
r.seek(header.soundMacroIdsOff, athena::Begin);
while (!AtEnd16(r))
@ -386,6 +414,8 @@ void AudioGroupProject::BootstrapObjectIDs(athena::io::IStreamReader& r, bool ab
GroupHeader<DNAE> header;
header.read(r);
GroupId::CurNameDB->registerPair(NameDB::generateName(header.groupId, NameDB::Type::Group), header.groupId);
/* Sound Macros */
r.seek(subDataOff + header.soundMacroIdsOff, athena::Begin);
while (!AtEnd16(r))
@ -501,11 +531,13 @@ bool AudioGroupProject::toYAML(SystemStringView groupPath) const
if (!m_songGroups.empty())
{
if (auto __v = w.enterSubVector("songGroups"))
if (auto __v = w.enterSubRecord("songGroups"))
{
for (const auto& p : SortUnorderedMap(m_songGroups))
{
if (auto __r = w.enterSubRecord(nullptr))
char groupString[64];
snprintf(groupString, 64, "%s/0x%04X", GroupId::CurNameDB->resolveNameFromId(p.first).data(), int(p.first.id));
if (auto __r = w.enterSubRecord(groupString))
{
if (!p.second.get().m_normPages.empty())
{
@ -545,7 +577,9 @@ bool AudioGroupProject::toYAML(SystemStringView groupPath) const
{
for (const auto& song : SortUnorderedMap(p.second.get().m_midiSetups))
{
if (auto __v3 = w.enterSubVector(SongId::CurNameDB->resolveNameFromId(song.first).data()))
char songString[64];
snprintf(songString, 64, "%s/0x%04X", SongId::CurNameDB->resolveNameFromId(song.first).data(), int(song.first.id));
if (auto __v3 = w.enterSubVector(songString))
for (int i = 0; i < 16; ++i)
if (auto __r2 = w.enterSubRecord(nullptr))
{
@ -562,15 +596,19 @@ bool AudioGroupProject::toYAML(SystemStringView groupPath) const
if (!m_sfxGroups.empty())
{
if (auto __v = w.enterSubVector("sfxGroups"))
if (auto __v = w.enterSubRecord("sfxGroups"))
{
for (const auto& p : SortUnorderedMap(m_sfxGroups))
{
if (auto __r = w.enterSubRecord(nullptr))
char groupString[64];
snprintf(groupString, 64, "%s/0x%04X", GroupId::CurNameDB->resolveNameFromId(p.first).data(), int(p.first.id));
if (auto __r = w.enterSubRecord(groupString))
{
for (const auto& sfx : SortUnorderedMap(p.second.get().m_sfxEntries))
{
if (auto __r2 = w.enterSubRecord(SFXId::CurNameDB->resolveNameFromId(sfx.first).data()))
char sfxString[64];
snprintf(sfxString, 64, "%s/0x%04X", SFXId::CurNameDB->resolveNameFromId(sfx.first).data(), int(sfx.first.id));
if (auto __r2 = w.enterSubRecord(sfxString))
{
w.setStyle(athena::io::YAMLNodeStyle::Flow);
sfx.second.get().write(w);

View File

@ -146,11 +146,23 @@ static uint32_t DSPNibbleToSample(uint32_t nibble)
void AudioGroupSampleDirectory::Entry::loadLooseData(SystemStringView basePath)
{
Sstat theStat;
SystemString filePath = SystemString(basePath) + _S(".dsp");
if (!Stat(filePath.c_str(), &theStat) && (!m_looseData || theStat.st_mtime > m_looseModTime))
SystemString wavPath = SystemString(basePath) + _S(".wav");
SystemString dspPath = SystemString(basePath) + _S(".dsp");
Sstat wavStat, dspStat;
bool wavValid = !Stat(wavPath.c_str(), &wavStat) && S_ISREG(wavStat.st_mode);
bool dspValid = !Stat(dspPath.c_str(), &dspStat) && S_ISREG(dspStat.st_mode);
if (wavValid && dspValid)
{
athena::io::FileReader r(filePath);
if (wavStat.st_mtime > dspStat.st_mtime)
dspValid = false;
else
wavValid = false;
}
if (dspValid && (!m_looseData || dspStat.st_mtime > m_looseModTime))
{
athena::io::FileReader r(dspPath);
if (!r.hasError())
{
DSPADPCMHeader header;
@ -175,13 +187,14 @@ void AudioGroupSampleDirectory::Entry::loadLooseData(SystemStringView basePath)
m_looseData.reset(new uint8_t[dataLen]);
r.readUBytesToBuf(m_looseData.get(), dataLen);
m_looseModTime = theStat.st_mtime;
m_looseModTime = dspStat.st_mtime;
return;
}
}
if (!Stat(filePath.c_str(), &theStat) && (!m_looseData || theStat.st_mtime > m_looseModTime))
if (wavValid && (!m_looseData || wavStat.st_mtime > m_looseModTime))
{
athena::io::FileReader r(filePath);
athena::io::FileReader r(wavPath);
if (!r.hasError())
{
atUint32 riffMagic = r.readUint32Little();
@ -216,7 +229,6 @@ void AudioGroupSampleDirectory::Entry::loadLooseData(SystemStringView basePath)
m_loopStartSample = loop.start;
m_loopLengthSamples = loop.end - loop.start + 1;
}
}
else if (chunkMagic == SBIG('data'))
{
@ -227,7 +239,7 @@ void AudioGroupSampleDirectory::Entry::loadLooseData(SystemStringView basePath)
r.seek(startPos + chunkSize, athena::Begin);
}
m_looseModTime = theStat.st_mtime;
m_looseModTime = wavStat.st_mtime;
return;
}
}

View File

@ -96,6 +96,7 @@ DEFINE_ID_TYPE(KeymapId, "keymap")
DEFINE_ID_TYPE(LayersId, "layers")
DEFINE_ID_TYPE(SongId, "song")
DEFINE_ID_TYPE(SFXId, "sfx")
DEFINE_ID_TYPE(GroupId, "group")
template<> template<>
void PageObjectIdDNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
@ -225,6 +226,9 @@ std::string NameDB::generateName(ObjectId id, Type tp)
case Type::SFX:
snprintf(name, 32, "sfx%04X", id.id);
break;
case Type::Group:
snprintf(name, 32, "group%04X", id.id);
break;
case Type::Sample:
snprintf(name, 32, "sample%04X", id.id);
break;

View File

@ -236,8 +236,8 @@ void Voice::_procSamplePre(int16_t& samp)
float start = m_envelopeStart;
float end = m_envelopeEnd;
float t = clamp(0.f, float(m_envelopeTime / m_envelopeDur), 1.f);
if (m_envelopeCurve)
t = m_envelopeCurve->data.at(t * 127.f) / 127.f;
if (m_envelopeCurve && m_envelopeCurve->data.size() >= 128)
t = m_envelopeCurve->data[t * 127.f] / 127.f;
m_curVol = clamp(0.f, (start * (1.0f - t)) + (end * t), 1.f);
// printf("%d %f\n", m_vid, m_curVol);