More work on Amuse Editor

This commit is contained in:
Jack Andersen 2018-07-17 21:39:26 -10:00
parent 3f265cdb46
commit f50ee6e8f1
38 changed files with 1193 additions and 140 deletions

7
Editor/ADSREditor.cpp Normal file
View File

@ -0,0 +1,7 @@
#include "ADSREditor.hpp"
ADSREditor::ADSREditor(ProjectModel::ADSRNode* node, QWidget* parent)
: EditorWidget(parent)
{
}

14
Editor/ADSREditor.hpp Normal file
View File

@ -0,0 +1,14 @@
#ifndef AMUSE_ADSR_EDITOR_HPP
#define AMUSE_ADSR_EDITOR_HPP
#include "EditorWidget.hpp"
class ADSREditor : public EditorWidget
{
Q_OBJECT
public:
explicit ADSREditor(ProjectModel::ADSRNode* node, QWidget* parent = Q_NULLPTR);
};
#endif //AMUSE_ADSR_EDITOR_HPP

View File

@ -28,15 +28,17 @@ add_executable(amuse-gui WIN32 MACOSX_BUNDLE
Common.hpp Common.cpp
MainWindow.ui MainWindow.hpp MainWindow.cpp
KeyboardWidget.hpp KeyboardWidget.cpp
StatusBarWidget.hpp StatusBarWidget.cpp
StatusBarWidget.hpp
ProjectModel.hpp ProjectModel.cpp
ProjectStatistics.hpp ProjectStatistics.cpp
EditorWidget.hpp EditorWidget.cpp
SoundMacroEditor.hpp SoundMacroEditor.cpp
ADSREditor.hpp ADSREditor.cpp
CurveEditor.hpp CurveEditor.cpp
KeymapEditor.hpp KeymapEditor.cpp
LayersEditor.hpp LayersEditor.cpp
SampleEditor.hpp SampleEditor.cpp
SFXGroupEditor.hpp SFXGroupEditor.cpp
SoundGroupEditor.hpp SoundGroupEditor.cpp
SongGroupEditor.hpp SongGroupEditor.cpp
AudioGroupModel.hpp AudioGroupModel.cpp
resources/resources.qrc qrc_resources.cpp

7
Editor/CurveEditor.cpp Normal file
View File

@ -0,0 +1,7 @@
#include "CurveEditor.hpp"
CurveEditor::CurveEditor(ProjectModel::CurveNode* node, QWidget* parent)
: EditorWidget(parent)
{
}

14
Editor/CurveEditor.hpp Normal file
View File

@ -0,0 +1,14 @@
#ifndef AMUSE_CURVE_EDITOR_HPP
#define AMUSE_CURVE_EDITOR_HPP
#include "EditorWidget.hpp"
class CurveEditor : public EditorWidget
{
Q_OBJECT
public:
explicit CurveEditor(ProjectModel::CurveNode* node, QWidget* parent = Q_NULLPTR);
};
#endif //AMUSE_CURVE_EDITOR_HPP

View File

@ -2,12 +2,14 @@
#define AMUSE_EDITOR_WIDGET_HPP
#include <QWidget>
#include "ProjectModel.hpp"
class EditorWidget : public QWidget
{
Q_OBJECT
public:
explicit EditorWidget(QWidget* parent = Q_NULLPTR);
virtual bool valid() const { return true; }
};

View File

@ -1,7 +1,203 @@
#include "KeyboardWidget.hpp"
#include <QHBoxLayout>
#include <QSvgRenderer>
#include <QMouseEvent>
#include <QScrollArea>
/* Used for generating transform matrices to map coordinate space */
static QTransform RectToRect(const QRectF& from, const QRectF& to)
{
QPolygonF orig(from);
orig.pop_back();
QPolygonF resize(to);
resize.pop_back();
QTransform ret;
QTransform::quadToQuad(orig, resize, ret);
return ret;
}
static const QString NaturalKeyNames[] =
{
QStringLiteral("C"),
QStringLiteral("D"),
QStringLiteral("E"),
QStringLiteral("F"),
QStringLiteral("G"),
QStringLiteral("A"),
QStringLiteral("B")
};
static const QString SharpKeyNames[] =
{
QStringLiteral("Cs"),
QStringLiteral("Ds"),
QStringLiteral("Fs"),
QStringLiteral("Gs"),
QStringLiteral("As")
};
static const QString KeyStrings[] =
{
QStringLiteral("C"),
QStringLiteral("C#"),
QStringLiteral("D"),
QStringLiteral("D#"),
QStringLiteral("E"),
QStringLiteral("F"),
QStringLiteral("F#"),
QStringLiteral("G"),
QStringLiteral("G#"),
QStringLiteral("A"),
QStringLiteral("A#"),
QStringLiteral("B")
};
static const int NaturalKeyNumbers[] =
{
0, 2, 4, 5, 7, 9, 11
};
static const int SharpKeyNumbers[] =
{
1, 3, 6, 8, 10
};
KeyboardOctave::KeyboardOctave(int octave, const QString& svgPath, QWidget* parent)
: QSvgWidget(svgPath, parent), m_octave(octave)
{
for (int i = 0; i < 7; ++i)
if (renderer()->elementExists(NaturalKeyNames[i]))
m_natural[i] = renderer()->matrixForElement(NaturalKeyNames[i]).
mapRect(renderer()->boundsOnElement(NaturalKeyNames[i]));
for (int i = 0; i < 5; ++i)
if (renderer()->elementExists(SharpKeyNames[i]))
m_sharp[i] = renderer()->matrixForElement(SharpKeyNames[i]).
mapRect(renderer()->boundsOnElement(SharpKeyNames[i]));
/* The parent keyboard manages all mouse events */
setAttribute(Qt::WA_TransparentForMouseEvents);
}
int KeyboardOctave::getKey(const QPoint& localPos) const
{
QPointF localPoint = m_widgetToSvg.map(localPos);
for (int i = 0; i < 5; ++i)
if (m_sharp[i].contains(localPoint))
return SharpKeyNumbers[i];
for (int i = 0; i < 7; ++i)
if (m_natural[i].contains(localPoint))
return NaturalKeyNumbers[i];
return -1;
}
void KeyboardOctave::resizeEvent(QResizeEvent *event)
{
m_widgetToSvg = RectToRect(rect(), renderer()->viewBoxF());
}
KeyboardWidget::KeyboardWidget(QWidget* parent)
: QWidget(parent)
{
QHBoxLayout* layout = new QHBoxLayout(this);
layout->setContentsMargins(QMargins());
layout->setSpacing(0);
for (int i = 0; i < 10; ++i)
{
m_widgets[i] = new KeyboardOctave(i, QStringLiteral(":/bg/keyboard.svg"), this);
m_widgets[i]->setGeometry(QRect(0, 0, 141, 50));
m_widgets[i]->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
layout->addWidget(m_widgets[i]);
}
m_widgets[10] = new KeyboardOctave(10, QStringLiteral(":/bg/keyboard_last.svg"), this);
m_widgets[10]->setGeometry(QRect(0, 0, 101, 50));
m_widgets[10]->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred));
layout->addWidget(m_widgets[10]);
setLayout(layout);
setMouseTracking(true);
}
std::pair<int, int> KeyboardWidget::_getOctaveAndKey(QMouseEvent* event) const
{
for (KeyboardOctave* oct : m_widgets)
{
QPoint localPos = oct->mapFromParent(event->pos());
if (oct->rect().contains(localPos))
return {oct->getOctave(), oct->getKey(localPos)};
}
return {-1, -1};
}
void KeyboardWidget::_startKey(int octave, int key)
{
printf("START %d %d\n", octave, key);
}
void KeyboardWidget::_stopKey()
{
printf("STOP\n");
}
void KeyboardWidget::_moveOnKey(int octave, int key)
{
if (m_lastOctave != octave || m_lastKey != key)
{
m_lastOctave = octave;
m_lastKey = key;
if (m_statusFocus)
m_statusFocus->setMessage(QStringLiteral("%1%2").arg(KeyStrings[key]).arg(octave - 1));
if (m_holding)
_startKey(octave, key);
}
}
void KeyboardWidget::_pressOnKey(int octave, int key)
{
_moveOnKey(octave, key);
m_holding = true;
_startKey(octave, key);
}
void KeyboardWidget::mouseMoveEvent(QMouseEvent* event)
{
std::pair<int, int> ok = _getOctaveAndKey(event);
if (ok.first != -1 && ok.second != -1)
_moveOnKey(ok.first, ok.second);
}
void KeyboardWidget::mousePressEvent(QMouseEvent* event)
{
std::pair<int, int> ok = _getOctaveAndKey(event);
if (ok.first != -1 && ok.second != -1)
_pressOnKey(ok.first, ok.second);
}
void KeyboardWidget::mouseReleaseEvent(QMouseEvent* event)
{
_stopKey();
m_holding = false;
}
void KeyboardWidget::enterEvent(QEvent* event)
{
if (m_statusFocus)
m_statusFocus->enter();
}
void KeyboardWidget::leaveEvent(QEvent* event)
{
if (m_statusFocus)
m_statusFocus->exit();
}
void KeyboardWidget::showEvent(QShowEvent* event)
{
if (QScrollArea* scroll = qobject_cast<QScrollArea*>(parentWidget()->parentWidget()))
{
/* Scroll to C3 */
scroll->ensureVisible(141 * 4 + scroll->width(), 0, 0, 0);
}
}

View File

@ -2,12 +2,50 @@
#define AMUSE_KEYBOARD_WIDGET_HPP
#include <QWidget>
#include <QSvgWidget>
#include "StatusBarWidget.hpp"
class KeyboardWidget;
class KeyboardOctave : public QSvgWidget
{
Q_OBJECT
int m_octave;
QRectF m_natural[7];
QRectF m_sharp[5];
QTransform m_widgetToSvg;
public:
explicit KeyboardOctave(int octave, const QString& svgPath, QWidget* parent = Q_NULLPTR);
int getOctave() const { return m_octave; }
int getKey(const QPoint& localPos) const;
void resizeEvent(QResizeEvent *event);
};
class KeyboardWidget : public QWidget
{
Q_OBJECT
KeyboardOctave* m_widgets[11];
StatusBarFocus* m_statusFocus = nullptr;
int m_lastOctave = -1;
int m_lastKey = -1;
bool m_holding = false;
std::pair<int, int> _getOctaveAndKey(QMouseEvent* event) const;
void _startKey(int octave, int key);
void _stopKey();
void _moveOnKey(int octave, int key);
void _pressOnKey(int octave, int key);
public:
explicit KeyboardWidget(QWidget* parent = Q_NULLPTR);
void setStatusFocus(StatusBarFocus* statusFocus) { m_statusFocus = statusFocus; }
void mouseMoveEvent(QMouseEvent* event);
void mousePressEvent(QMouseEvent* event);
void mouseReleaseEvent(QMouseEvent* event);
void enterEvent(QEvent* event);
void leaveEvent(QEvent* event);
void showEvent(QShowEvent *event);
};

View File

@ -1,6 +1,6 @@
#include "KeymapEditor.hpp"
KeymapEditor::KeymapEditor(QWidget* parent)
KeymapEditor::KeymapEditor(ProjectModel::KeymapNode* node, QWidget* parent)
: EditorWidget(parent)
{

View File

@ -7,7 +7,7 @@ class KeymapEditor : public EditorWidget
{
Q_OBJECT
public:
explicit KeymapEditor(QWidget* parent = Q_NULLPTR);
explicit KeymapEditor(ProjectModel::KeymapNode* node, QWidget* parent = Q_NULLPTR);
};

View File

@ -1,6 +1,6 @@
#include "LayersEditor.hpp"
LayersEditor::LayersEditor(QWidget* parent)
LayersEditor::LayersEditor(ProjectModel::LayersNode* node, QWidget* parent)
: EditorWidget(parent)
{

View File

@ -7,7 +7,7 @@ class LayersEditor : public EditorWidget
{
Q_OBJECT
public:
explicit LayersEditor(QWidget* parent = Q_NULLPTR);
explicit LayersEditor(ProjectModel::LayersNode* node, QWidget* parent = Q_NULLPTR);
};

View File

@ -4,9 +4,18 @@
#include <QLineEdit>
#include <QInputDialog>
#include <QProgressDialog>
#include <QMouseEvent>
#include <QtSvg/QtSvg>
#include "amuse/ContainerRegistry.hpp"
#include "Common.hpp"
#include "SongGroupEditor.hpp"
#include "SoundGroupEditor.hpp"
#include "SoundGroupEditor.hpp"
#include "SoundMacroEditor.hpp"
#include "ADSREditor.hpp"
#include "CurveEditor.hpp"
#include "KeymapEditor.hpp"
#include "LayersEditor.hpp"
static void connectMessenger(UIMessenger* messenger, Qt::ConnectionType type)
{
@ -15,6 +24,7 @@ static void connectMessenger(UIMessenger* messenger, Qt::ConnectionType type)
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent),
m_treeDelegate(*this, this),
m_mainMessenger(this),
m_undoStack(new QUndoStack(this)),
m_backgroundThread(this)
@ -22,8 +32,11 @@ MainWindow::MainWindow(QWidget* parent)
m_backgroundThread.start();
m_ui.setupUi(this);
m_ui.projectOutline->setItemDelegate(&m_treeDelegate);
connectMessenger(&m_mainMessenger, Qt::DirectConnection);
m_ui.keyboardContents->setStatusFocus(new StatusBarFocus(m_ui.statusbar));
m_ui.actionNew_Project->setShortcut(QKeySequence::New);
connect(m_ui.actionNew_Project, SIGNAL(triggered()), this, SLOT(newAction()));
m_ui.actionOpen_Project->setShortcut(QKeySequence::Open);
@ -45,11 +58,21 @@ MainWindow::MainWindow(QWidget* parent)
m_ui.actionDelete->setShortcut(QKeySequence::Delete);
onFocusChanged(nullptr, this);
m_ui.editorSvg->load(QStringLiteral(":/bg/FaceGrey.svg"));
QGridLayout* faceLayout = new QGridLayout;
QSvgWidget* faceSvg = new QSvgWidget(QStringLiteral(":/bg/FaceGrey.svg"));
faceSvg->setGeometry(0, 0, 256, 256);
faceSvg->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
faceLayout->addWidget(faceSvg);
m_faceSvg = new QWidget;
m_faceSvg->setLayout(faceLayout);
m_ui.editorContents->addWidget(m_faceSvg);
connect(m_ui.actionNew_Subproject, SIGNAL(triggered()), this, SLOT(newSubprojectAction()));
connect(m_ui.actionNew_SFX_Group, SIGNAL(triggered()), this, SLOT(newSFXGroupAction()));
connect(m_ui.actionNew_Song_Group, SIGNAL(triggered()), this, SLOT(newSongGroupAction()));
connect(m_ui.actionNew_Sound_Macro, SIGNAL(triggered()), this, SLOT(newSoundMacroAction()));
connect(m_ui.actionNew_ADSR, SIGNAL(triggered()), this, SLOT(newADSRAction()));
connect(m_ui.actionNew_Curve, SIGNAL(triggered()), this, SLOT(newCurveAction()));
connect(m_ui.actionNew_Keymap, SIGNAL(triggered()), this, SLOT(newKeymapAction()));
connect(m_ui.actionNew_Layers, SIGNAL(triggered()), this, SLOT(newLayersAction()));
@ -128,6 +151,9 @@ bool MainWindow::setProjectPath(const QString& path)
m_ui.projectOutline->setModel(m_projectModel);
m_ui.actionExport_GameCube_Groups->setEnabled(true);
setWindowFilePath(path);
#ifndef __APPLE__
setWindowTitle(QString("Amuse - %1").arg(dir.dirName()));
#endif
setFocusAudioGroup(nullptr);
onFocusChanged(nullptr, focusWidget());
@ -217,6 +243,88 @@ void MainWindow::startBackgroundTask(const QString& windowTitle, const QString&
QMetaObject::invokeMethod(m_backgroundTask, "run", Qt::QueuedConnection);
}
bool MainWindow::_setEditor(EditorWidget* editor)
{
while (m_ui.editorContents->currentWidget() != m_faceSvg)
{
m_ui.editorContents->currentWidget()->deleteLater();
m_ui.editorContents->removeWidget(m_ui.editorContents->currentWidget());
}
if (!editor)
return false;
if (!editor->valid())
{
editor->deleteLater();
return false;
}
m_ui.editorContents->addWidget(editor);
m_ui.editorContents->setCurrentWidget(editor);
return true;
}
bool MainWindow::openEditor(ProjectModel::SongGroupNode* node)
{
return _setEditor(new SongGroupEditor(node));
}
bool MainWindow::openEditor(ProjectModel::SoundGroupNode* node)
{
return _setEditor(new SoundGroupEditor(node));
}
bool MainWindow::openEditor(ProjectModel::SoundMacroNode* node)
{
return _setEditor(new SoundMacroEditor(node));
}
bool MainWindow::openEditor(ProjectModel::ADSRNode* node)
{
return _setEditor(new ADSREditor(node));
}
bool MainWindow::openEditor(ProjectModel::CurveNode* node)
{
return _setEditor(new CurveEditor(node));
}
bool MainWindow::openEditor(ProjectModel::KeymapNode* node)
{
return _setEditor(new KeymapEditor(node));
}
bool MainWindow::openEditor(ProjectModel::LayersNode* node)
{
return _setEditor(new LayersEditor(node));
}
bool MainWindow::openEditor(ProjectModel::INode* node)
{
switch (node->type())
{
case ProjectModel::INode::Type::SongGroup:
return openEditor(static_cast<ProjectModel::SongGroupNode*>(node));
case ProjectModel::INode::Type::SoundGroup:
return openEditor(static_cast<ProjectModel::SoundGroupNode*>(node));
case ProjectModel::INode::Type::SoundMacro:
return openEditor(static_cast<ProjectModel::SoundMacroNode*>(node));
case ProjectModel::INode::Type::ADSR:
return openEditor(static_cast<ProjectModel::ADSRNode*>(node));
case ProjectModel::INode::Type::Curve:
return openEditor(static_cast<ProjectModel::CurveNode*>(node));
case ProjectModel::INode::Type::Keymap:
return openEditor(static_cast<ProjectModel::KeymapNode*>(node));
case ProjectModel::INode::Type::Layer:
return openEditor(static_cast<ProjectModel::LayersNode*>(node));
default:
return false;
}
}
void MainWindow::closeEditor()
{
_setEditor(nullptr);
}
void MainWindow::newAction()
{
QString path = QFileDialog::getSaveFileName(this, tr("New Project"));
@ -226,6 +334,9 @@ void MainWindow::newAction()
return;
if (!setProjectPath(path))
return;
m_projectModel->clearProjectData();
m_projectModel->ensureModelData();
}
void MainWindow::openAction()
@ -233,10 +344,41 @@ void MainWindow::openAction()
QString path = QFileDialog::getExistingDirectory(this, tr("Open Project"));
if (path.isEmpty())
return;
QDir dir(path);
if (!dir.exists())
{
QString msg = QString(tr("The directory at '%1' does not exist.")).arg(path);
QMessageBox::critical(this, tr("Bad Directory"), msg);
return;
}
if (QFileInfo(dir, QStringLiteral("!project.yaml")).exists() &&
QFileInfo(dir, QStringLiteral("!pool.yaml")).exists())
dir.cdUp();
if (!setProjectPath(path))
return;
ProjectModel* model = m_projectModel;
startBackgroundTask(tr("Opening"), tr("Scanning Project"),
[dir, model](BackgroundTask& task)
{
QStringList childDirs = dir.entryList(QDir::Dirs);
for (const auto& chDir : childDirs)
{
if (task.isCanceled())
return;
QString chPath = dir.filePath(chDir);
if (QFileInfo(chPath, QStringLiteral("!project.yaml")).exists() &&
QFileInfo(chPath, QStringLiteral("!pool.yaml")).exists())
{
task.setLabelText(tr("Opening %1").arg(chDir));
if (!model->openGroupData(chDir, task.uiMessenger()))
return;
}
}
});
}
void MainWindow::importAction()
@ -372,6 +514,33 @@ void MainWindow::exportAction()
}
bool TreeDelegate::editorEvent(QEvent* event,
QAbstractItemModel* _model,
const QStyleOptionViewItem& option,
const QModelIndex& index)
{
ProjectModel* model = static_cast<ProjectModel*>(_model);
ProjectModel::INode* node = model->node(index);
if (!node)
return false;
if ((event->type() == QEvent::MouseButtonDblClick &&
static_cast<QMouseEvent*>(event)->button() == Qt::LeftButton) ||
(event->type() == QEvent::KeyPress &&
static_cast<QKeyEvent*>(event)->key() == Qt::Key_Enter))
{
// Open in editor
return m_window.openEditor(node);
}
return false;
}
void MainWindow::newSubprojectAction()
{
}
void MainWindow::newSFXGroupAction()
{
@ -387,6 +556,16 @@ void MainWindow::newSoundMacroAction()
}
void MainWindow::newADSRAction()
{
}
void MainWindow::newCurveAction()
{
}
void MainWindow::newKeymapAction()
{

View File

@ -5,16 +5,19 @@
#include <QUndoStack>
#include <QProgressDialog>
#include <QThread>
#include <QStyledItemDelegate>
#include "ui_MainWindow.h"
#include "amuse/Engine.hpp"
#include "amuse/BooBackend.hpp"
#include "boo/audiodev/IAudioVoiceEngine.hpp"
#include "ProjectModel.hpp"
#include "EditorWidget.hpp"
namespace Ui {
class MainWindow;
}
class MainWindow;
class AudioGroupModel;
class BackgroundTask : public QObject
@ -41,13 +44,29 @@ public slots:
void cancel() { m_cancelled = true; }
};
class TreeDelegate : public QStyledItemDelegate
{
Q_OBJECT
MainWindow& m_window;
public:
explicit TreeDelegate(MainWindow& window, QObject* parent = Q_NULLPTR)
: QStyledItemDelegate(parent), m_window(window) {}
bool editorEvent(QEvent *event,
QAbstractItemModel *model,
const QStyleOptionViewItem &option,
const QModelIndex &index);
};
class MainWindow : public QMainWindow
{
Q_OBJECT
Ui::MainWindow m_ui;
TreeDelegate m_treeDelegate;
UIMessenger m_mainMessenger;
ProjectModel* m_projectModel = nullptr;
AudioGroupModel* m_focusAudioGroup = nullptr;
QWidget* m_faceSvg;
std::unique_ptr<boo::IAudioVoiceEngine> m_voxEngine;
std::unique_ptr<amuse::BooBackendVoiceAllocator> m_voxAllocator;
@ -79,19 +98,34 @@ class MainWindow : public QMainWindow
void startBackgroundTask(const QString& windowTitle, const QString& label,
std::function<void(BackgroundTask&)>&& task);
bool _setEditor(EditorWidget* widget);
public:
explicit MainWindow(QWidget* parent = Q_NULLPTR);
~MainWindow();
bool openEditor(ProjectModel::SongGroupNode* node);
bool openEditor(ProjectModel::SoundGroupNode* node);
bool openEditor(ProjectModel::SoundMacroNode* node);
bool openEditor(ProjectModel::ADSRNode* node);
bool openEditor(ProjectModel::CurveNode* node);
bool openEditor(ProjectModel::KeymapNode* node);
bool openEditor(ProjectModel::LayersNode* node);
bool openEditor(ProjectModel::INode* node);
void closeEditor();
public slots:
void newAction();
void openAction();
void importAction();
void exportAction();
void newSubprojectAction();
void newSFXGroupAction();
void newSongGroupAction();
void newSoundMacroAction();
void newADSRAction();
void newCurveAction();
void newKeymapAction();
void newLayersAction();

View File

@ -35,41 +35,25 @@
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<widget class="QSplitter" name="leftSplitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
<widget class="QTreeView" name="projectOutline">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>3</verstretch>
</sizepolicy>
</property>
<widget class="QTreeView" name="projectOutline">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>3</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
<widget class="QTableView" name="propertyEditor">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>2</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
</widget>
<property name="minimumSize">
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="animated">
<bool>true</bool>
</property>
<attribute name="headerVisible">
<bool>false</bool>
</attribute>
</widget>
<widget class="QSplitter" name="rightSplitter">
<property name="sizePolicy">
@ -109,7 +93,7 @@
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="editorContents">
<widget class="QStackedWidget" name="editorContents">
<property name="geometry">
<rect>
<x>0</x>
@ -118,24 +102,6 @@
<height>442</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QSvgWidget" name="editorSvg" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>256</width>
<height>256</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QScrollArea" name="keyboardScrollArea">
@ -174,10 +140,22 @@
<rect>
<x>0</x>
<y>0</y>
<width>540</width>
<height>100</height>
<width>1501</width>
<height>85</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>1501</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
@ -213,10 +191,14 @@
<property name="title">
<string>P&amp;roject</string>
</property>
<addaction name="actionNew_Subproject"/>
<addaction name="separator"/>
<addaction name="actionNew_SFX_Group"/>
<addaction name="actionNew_Song_Group"/>
<addaction name="separator"/>
<addaction name="actionNew_Sound_Macro"/>
<addaction name="actionNew_ADSR"/>
<addaction name="actionNew_Curve"/>
<addaction name="actionNew_Keymap"/>
<addaction name="actionNew_Layers"/>
</widget>
@ -418,6 +400,42 @@
<string>Ctrl+E</string>
</property>
</action>
<action name="actionNew_Subproject">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset>
<normaloff>:/icons/IconNewGroup.svg</normaloff>:/icons/IconNewGroup.svg</iconset>
</property>
<property name="text">
<string>N&amp;ew Subproject</string>
</property>
</action>
<action name="actionNew_ADSR">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset>
<normaloff>:/icons/IconNewADSR.svg</normaloff>:/icons/IconNewADSR.svg</iconset>
</property>
<property name="text">
<string>Ne&amp;w ADSR</string>
</property>
</action>
<action name="actionNew_Curve">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset>
<normaloff>:/icons/IconNewCurve.svg</normaloff>:/icons/IconNewCurve.svg</iconset>
</property>
<property name="text">
<string>New &amp;Curve</string>
</property>
</action>
</widget>
<customwidgets>
<customwidget>
@ -431,12 +449,6 @@
<extends>QStatusBar</extends>
<header>StatusBarWidget.hpp</header>
</customwidget>
<customwidget>
<class>QSvgWidget</class>
<extends>QWidget</extends>
<header location="global">QSvgWidget</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>

View File

@ -18,6 +18,25 @@ ProjectModel::ProjectModel(const QString& path, QObject* parent)
SoundGroupNode::Icon = QIcon(":/icons/IconSoundGroup.svg");
}
bool ProjectModel::clearProjectData()
{
m_projectDatabase = amuse::ProjectDatabase();
m_groups.clear();
m_needsReset = true;
return true;
}
bool ProjectModel::openGroupData(const QString& groupName, UIMessenger& messenger)
{
m_projectDatabase.setIdDatabases();
QString path = QFileInfo(m_dir, groupName).filePath();
m_groups.insert(std::make_pair(groupName, QStringToSysString(path)));
m_needsReset = true;
return true;
}
bool ProjectModel::importGroupData(const QString& groupName, const amuse::AudioGroupData& data,
ImportMode mode, UIMessenger& messenger)
{
@ -89,12 +108,13 @@ void ProjectModel::_resetModelData()
{
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();
amuse::AudioGroup& group = it->second;
auto& songGroups = group.getProj().songGroups();
auto& sfxGroups = group.getProj().sfxGroups();
auto& soundMacros = group.getPool().soundMacros();
auto& tables = group.getPool().tables();
auto& keymaps = group.getPool().keymaps();
auto& layers = group.getPool().layers();
gn.reserve(songGroups.size() + sfxGroups.size() + 4);
for (const auto& grp : SortUnorderedMap(songGroups))
gn.makeChild<SongGroupNode>(grp.first, grp.second.get());
@ -106,7 +126,7 @@ void ProjectModel::_resetModelData()
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());
col.makeChild<SoundMacroNode>(macro.first, macro.second.get());
}
if (tables.size())
{
@ -130,7 +150,7 @@ void ProjectModel::_resetModelData()
{
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());
col.makeChild<ADSRNode>(t.first, *t.second.get());
}
}
if (curveCount)
@ -142,7 +162,7 @@ void ProjectModel::_resetModelData()
{
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()));
col.makeChild<CurveNode>(t.first, static_cast<amuse::Curve&>(*t.second.get()));
}
}
}
@ -152,7 +172,7 @@ void ProjectModel::_resetModelData()
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());
col.makeChild<KeymapNode>(keymap.first, keymap.second.get());
}
if (layers.size())
{
@ -160,7 +180,7 @@ void ProjectModel::_resetModelData()
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());
col.makeChild<LayersNode>(keymap.first, keymap.second.get());
}
}
endResetModel();
@ -250,6 +270,13 @@ Qt::ItemFlags ProjectModel::flags(const QModelIndex& index) const
return QAbstractItemModel::flags(index);
}
ProjectModel::INode* ProjectModel::node(const QModelIndex& index) const
{
if (!index.isValid())
return nullptr;
return static_cast<INode*>(index.internalPointer());
}
bool ProjectModel::canDelete() const
{
return false;

View File

@ -29,13 +29,16 @@ private:
amuse::ProjectDatabase m_projectDatabase;
std::map<QString, amuse::AudioGroupDatabase> m_groups;
public:
class INode
{
public:
enum class Type
{
Root,
Group, // Top-level group
SongGroup,
SfxGroup,
SoundGroup,
Collection, // Classified object collection, one of the following:
SoundMacro,
ADSR,
@ -43,6 +46,7 @@ private:
Keymap,
Layer
};
private:
INode* m_parent;
std::vector<std::unique_ptr<INode>> m_children;
int m_row;
@ -63,6 +67,7 @@ private:
return static_cast<T&>(*m_children.back());
}
virtual Type type() const = 0;
virtual QString text() const = 0;
virtual QIcon icon() const = 0;
};
@ -70,6 +75,7 @@ private:
{
RootNode() : INode(nullptr, 0) {}
Type type() const { return Type::Root; }
QString text() const { return {}; }
QIcon icon() const { return {}; }
};
@ -80,6 +86,7 @@ private:
: INode(parent, row), m_it(it) {}
static QIcon Icon;
Type type() const { return Type::Group; }
QString text() const { return m_it->first; }
QIcon icon() const { return Icon; }
};
@ -92,6 +99,7 @@ private:
: INode(parent, row), m_id(id), m_name(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_index(index) {}
static QIcon Icon;
Type type() const { return Type::SongGroup; }
QString text() const { return m_name; }
QIcon icon() const { return Icon; }
};
@ -104,6 +112,7 @@ private:
: INode(parent, row), m_id(id), m_name(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_index(index) {}
static QIcon Icon;
Type type() const { return Type::SoundGroup; }
QString text() const { return m_name; }
QIcon icon() const { return Icon; }
};
@ -114,10 +123,11 @@ private:
CollectionNode(INode* parent, int row, const QString& name, const QIcon& icon)
: INode(parent, row), m_name(name), m_icon(icon) {}
Type type() const { return Type::Collection; }
QString text() const { return m_name; }
QIcon icon() const { return m_icon; }
};
template <class ID, class T>
template <class ID, class T, INode::Type TP>
struct PoolObjectNode : INode
{
ID m_id;
@ -126,9 +136,15 @@ private:
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) {}
Type type() const { return TP; }
QString text() const { return m_name; }
QIcon icon() const { return {}; }
};
using SoundMacroNode = PoolObjectNode<amuse::SoundMacroId, amuse::SoundMacro, INode::Type::SoundMacro>;
using ADSRNode = PoolObjectNode<amuse::TableId, amuse::ITable, INode::Type::ADSR>;
using CurveNode = PoolObjectNode<amuse::TableId, amuse::Curve, INode::Type::Curve>;
using KeymapNode = PoolObjectNode<amuse::KeymapId, amuse::Keymap, INode::Type::Keymap>;
using LayersNode = PoolObjectNode<amuse::LayersId, std::vector<amuse::LayerMapping>, INode::Type::Layer>;
std::unique_ptr<RootNode> m_root;
@ -138,6 +154,8 @@ private:
public:
explicit ProjectModel(const QString& path, QObject* parent = Q_NULLPTR);
bool clearProjectData();
bool openGroupData(const QString& groupName, UIMessenger& messenger);
bool importGroupData(const QString& groupName, const amuse::AudioGroupData& data,
ImportMode mode, UIMessenger& messenger);
bool saveToFile(UIMessenger& messenger);
@ -150,6 +168,7 @@ public:
int columnCount(const QModelIndex& parent = QModelIndex()) const;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
Qt::ItemFlags flags(const QModelIndex& index) const;
INode* node(const QModelIndex& index) const;
QString path() const { return m_dir.path(); }
bool canDelete() const;

View File

@ -1,7 +0,0 @@
#include "SFXGroupEditor.hpp"
SFXGroupEditor::SFXGroupEditor(QWidget* parent)
: EditorWidget(parent)
{
}

View File

@ -1,14 +0,0 @@
#ifndef AMUSE_SFX_GROUP_EDITOR_HPP
#define AMUSE_SFX_GROUP_EDITOR_HPP
#include "EditorWidget.hpp"
class SFXGroupEditor : public EditorWidget
{
Q_OBJECT
public:
explicit SFXGroupEditor(QWidget* parent = Q_NULLPTR);
};
#endif //AMUSE_SFX_GROUP_EDITOR_HPP

View File

@ -1,6 +1,6 @@
#include "SongGroupEditor.hpp"
SongGroupEditor::SongGroupEditor(QWidget* parent)
SongGroupEditor::SongGroupEditor(ProjectModel::SongGroupNode* node, QWidget* parent)
: EditorWidget(parent)
{

View File

@ -7,7 +7,7 @@ class SongGroupEditor : public EditorWidget
{
Q_OBJECT
public:
explicit SongGroupEditor(QWidget* parent = Q_NULLPTR);
explicit SongGroupEditor(ProjectModel::SongGroupNode* node, QWidget* parent = Q_NULLPTR);
};

View File

@ -0,0 +1,7 @@
#include "SoundGroupEditor.hpp"
SoundGroupEditor::SoundGroupEditor(ProjectModel::SoundGroupNode* node, QWidget* parent)
: EditorWidget(parent)
{
}

View File

@ -0,0 +1,14 @@
#ifndef AMUSE_SOUND_GROUP_EDITOR_HPP
#define AMUSE_SOUND_GROUP_EDITOR_HPP
#include "EditorWidget.hpp"
class SoundGroupEditor : public EditorWidget
{
Q_OBJECT
public:
explicit SoundGroupEditor(ProjectModel::SoundGroupNode* node, QWidget* parent = Q_NULLPTR);
};
#endif //AMUSE_SOUND_GROUP_EDITOR_HPP

View File

@ -1,7 +1,10 @@
#include "SoundMacroEditor.hpp"
#include <QLabel>
SoundMacroEditor::SoundMacroEditor(QWidget* parent)
SoundMacroEditor::SoundMacroEditor(ProjectModel::SoundMacroNode* node, QWidget* parent)
: EditorWidget(parent)
{
QLabel* lab = new QLabel;
lab->setText(node->m_name);
lab->setParent(this);
}

View File

@ -7,7 +7,7 @@ class SoundMacroEditor : public EditorWidget
{
Q_OBJECT
public:
explicit SoundMacroEditor(QWidget* parent = Q_NULLPTR);
explicit SoundMacroEditor(ProjectModel::SoundMacroNode* node, QWidget* parent = Q_NULLPTR);
};

View File

@ -1,7 +0,0 @@
#include "StatusBarWidget.hpp"
StatusBarWidget::StatusBarWidget(QWidget* parent)
: QStatusBar(parent)
{
}

View File

@ -2,13 +2,65 @@
#define AMUSE_STATUSBAR_WIDGET_HPP
#include <QStatusBar>
#include <QLabel>
class StatusBarFocus;
class StatusBarWidget : public QStatusBar
{
friend class StatusBarFocus;
Q_OBJECT
QLabel* m_normalMessage;
StatusBarFocus* m_curFocus = nullptr;
public:
explicit StatusBarWidget(QWidget* parent = Q_NULLPTR);
explicit StatusBarWidget(QWidget* parent = Q_NULLPTR) : QStatusBar(parent) {}
void setNormalMessage(const QString& message) { m_normalMessage->setText(message); }
};
class StatusBarFocus : public QObject
{
Q_OBJECT
QString m_message;
public:
explicit StatusBarFocus(StatusBarWidget* statusWidget)
: QObject(statusWidget) {}
~StatusBarFocus() { exit(); }
void setMessage(const QString& message)
{
m_message = message;
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent()))
{
if (widget->m_curFocus == this)
{
if (m_message.isEmpty())
widget->clearMessage();
else
widget->showMessage(m_message);
}
}
}
void enter()
{
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent()))
{
widget->m_curFocus = this;
if (m_message.isEmpty())
widget->clearMessage();
else
widget->showMessage(m_message);
}
}
void exit()
{
if (StatusBarWidget* widget = qobject_cast<StatusBarWidget*>(parent()))
{
if (widget->m_curFocus == this)
{
widget->clearMessage();
widget->m_curFocus = nullptr;
}
}
}
};
#endif //AMUSE_STATUSBAR_WIDGET_HPP

View File

@ -0,0 +1,239 @@
<?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:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="140"
height="100"
viewBox="0 0 37.041665 26.458336"
version="1.1"
id="svg8"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="keyboard.svg">
<defs
id="defs2">
<linearGradient
id="whiteKey">
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="0"
id="stop4540" />
<stop
id="stop4552"
offset="0.67000026"
style="stop-color:#cecece;stop-opacity:1;" />
<stop
id="stop4550"
offset="0.68000031"
style="stop-color:#ffffff;stop-opacity:1;" />
<stop
style="stop-color:#ffffff;stop-opacity:1;"
offset="1"
id="stop4542" />
</linearGradient>
<linearGradient
id="blackKey">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop4528" />
<stop
id="stop4538"
offset="0.33333373"
style="stop-color:#000000;stop-opacity:1;" />
<stop
id="stop4536"
offset="0.36666706"
style="stop-color:#313131;stop-opacity:1;" />
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="1"
id="stop4530" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#blackKey"
id="linearGradient4534"
x1="2.6458333"
y1="286.41666"
x2="2.6458333"
y2="270.54166"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0.66145845)" />
<linearGradient
inkscape:collect="always"
xlink:href="#blackKey"
id="linearGradient4534-3"
x1="2.645833"
y1="286.41663"
x2="2.6458333"
y2="270.54166"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(6.6145838,1.0903046e-5)" />
<linearGradient
inkscape:collect="always"
xlink:href="#blackKey"
id="linearGradient4534-6"
x1="2.6458328"
y1="286.41666"
x2="2.6458333"
y2="270.54166"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(15.875001,7.6333333e-6)" />
<linearGradient
inkscape:collect="always"
xlink:href="#blackKey"
id="linearGradient4534-0"
x1="2.645833"
y1="286.41663"
x2="2.6458333"
y2="270.54166"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(21.828126,1.4333333e-5)" />
<linearGradient
inkscape:collect="always"
xlink:href="#blackKey"
id="linearGradient4534-2"
x1="2.645833"
y1="286.41666"
x2="2.6458333"
y2="270.54166"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(27.781251,7.7033334e-6)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#313131"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="3.4618283"
inkscape:cx="44.115558"
inkscape:cy="71.875845"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1286"
inkscape:window-height="1176"
inkscape:window-x="1170"
inkscape:window-y="447"
inkscape:window-maximized="0"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0">
<inkscape:grid
type="xygrid"
id="grid4518"
spacingx="0.13229167"
spacingy="0.26458333"
visible="false"
empspacing="5"
originx="0.13229165"
originy="0.13228834" />
</sodipodi:namedview>
<metadata
id="metadata5">
<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.13229166,-270.67394)">
<path
style="fill:#fdfdfd;fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 0,270.54165 v 26.45833 H 5.2916666 V 286.41665 H 3.3072917 v -15.875 z"
id="C"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
inkscape:label="C" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 5.2916666,296.99998 H 10.583333 V 286.41665 H 9.2604166 v -15.875 H 7.2760419 v 15.875 H 5.2916666 Z"
id="D"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc"
inkscape:label="D" />
<path
style="fill:url(#linearGradient4534);fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.3072917,270.54165 v 15.875 h 3.9687499 v -15.875 z"
id="Cs"
inkscape:connector-curvature="0"
inkscape:label="Cs" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 10.583333,296.99998 H 15.875 v -26.45833 h -2.645834 v 15.875 h -2.645833 z"
id="E"
inkscape:connector-curvature="0"
inkscape:label="E" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 15.875,296.99998 h 5.291666 v -10.58333 h -2.645833 v -15.875 H 15.875 Z"
id="F"
inkscape:connector-curvature="0"
inkscape:label="F" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 21.166666,296.99998 h 5.291667 v -10.58333 h -1.984374 v -15.875 h -1.984376 v 15.875 h -1.322917 z"
id="G"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc"
inkscape:label="G" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 26.458333,296.99998 H 31.75 v -10.58333 h -1.322917 v -15.875 h -1.984374 v 15.875 h -1.984376 z"
id="A"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc"
inkscape:label="A" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 31.75,296.99998 h 5.291666 v -10.58333 -15.875 h -2.645833 v 15.875 H 31.75 Z"
id="B"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc"
inkscape:label="B" />
<path
style="fill:url(#linearGradient4534-3);fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 9.260417,270.54165 v 15.875 h 3.96875 v -15.875 z"
id="Ds"
inkscape:connector-curvature="0"
inkscape:label="Ds" />
<path
style="fill:url(#linearGradient4534-6);fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 18.520834,270.54165 v 15.875 h 3.96875 v -15.875 z"
id="Fs"
inkscape:connector-curvature="0"
inkscape:label="Fs" />
<path
style="fill:url(#linearGradient4534-0);fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 24.473959,270.54165 v 15.875 h 3.96875 v -15.875 z"
id="Gs"
inkscape:connector-curvature="0"
inkscape:label="Gs" />
<path
style="fill:url(#linearGradient4534-2);fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 30.427084,270.54165 v 15.875 h 3.96875 v -15.875 z"
id="As"
inkscape:connector-curvature="0"
inkscape:label="As" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -0,0 +1,174 @@
<?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:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="101"
height="100"
viewBox="0 0 26.722915 26.458338"
version="1.1"
id="svg8"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="keyboard_last.svg">
<defs
id="defs2">
<linearGradient
id="blackKey">
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop4528" />
<stop
id="stop4538"
offset="0.33333373"
style="stop-color:#000000;stop-opacity:1;" />
<stop
id="stop4536"
offset="0.36666706"
style="stop-color:#313131;stop-opacity:1;" />
<stop
style="stop-color:#000000;stop-opacity:1;"
offset="1"
id="stop4530" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#blackKey"
id="linearGradient4534"
x1="2.6458333"
y1="286.41666"
x2="2.6458333"
y2="270.54166"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(0.66145845)" />
<linearGradient
inkscape:collect="always"
xlink:href="#blackKey"
id="linearGradient4534-3"
x1="2.645833"
y1="286.41663"
x2="2.6458333"
y2="270.54166"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(6.6145838,1.0903046e-5)" />
<linearGradient
inkscape:collect="always"
xlink:href="#blackKey"
id="linearGradient4534-6"
x1="2.6458328"
y1="286.41666"
x2="2.6458333"
y2="270.54166"
gradientUnits="userSpaceOnUse"
gradientTransform="translate(15.875001,7.6333333e-6)" />
</defs>
<sodipodi:namedview
id="base"
pagecolor="#313131"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="5.25169"
inkscape:cx="63.580658"
inkscape:cy="56.621412"
inkscape:document-units="mm"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="1424"
inkscape:window-height="1104"
inkscape:window-x="2192"
inkscape:window-y="236"
inkscape:window-maximized="0"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0">
<inkscape:grid
type="xygrid"
id="grid4518"
spacingx="0.13229167"
spacingy="0.26458333"
visible="false"
empspacing="5"
originx="0.13229164"
originy="0.13228669" />
</sodipodi:namedview>
<metadata
id="metadata5">
<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.13229166,-270.67394)">
<path
style="fill:#fdfdfd;fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 0,270.54165 v 26.45833 H 5.2916666 V 286.41665 H 3.3072917 v -15.875 z"
id="C"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
inkscape:label="C" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 5.2916666,296.99998 H 10.583333 V 286.41665 H 9.2604166 v -15.875 H 7.2760419 v 15.875 H 5.2916666 Z"
id="D"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccccc"
inkscape:label="D" />
<path
style="fill:url(#linearGradient4534);fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 3.3072917,270.54165 v 15.875 h 3.9687499 v -15.875 z"
id="Cs"
inkscape:connector-curvature="0"
inkscape:label="Cs" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="M 10.583333,296.99998 H 15.875 v -26.45833 h -2.645834 v 15.875 h -2.645833 z"
id="E"
inkscape:connector-curvature="0"
inkscape:label="E" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 15.875,296.99998 h 5.291666 v -10.58333 h -2.645833 v -15.875 H 15.875 Z"
id="F"
inkscape:connector-curvature="0"
inkscape:label="F" />
<path
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 21.166666,296.99998 h 5.291667 c 0,0 1e-6,-26.45833 1e-6,-26.45833 h -3.968751 v 15.875 h -1.322917 z"
id="G"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccc"
inkscape:label="G" />
<path
style="fill:url(#linearGradient4534-3);fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 9.260417,270.54165 v 15.875 h 3.96875 v -15.875 z"
id="Ds"
inkscape:connector-curvature="0"
inkscape:label="Ds" />
<path
style="fill:url(#linearGradient4534-6);fill-opacity:1;stroke:#000000;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 18.520834,270.54165 v 15.875 h 3.96875 v -15.875 z"
id="Fs"
inkscape:connector-curvature="0"
inkscape:label="Fs" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

@ -22,5 +22,7 @@
</qresource>
<qresource prefix="/bg">
<file>FaceGrey.svg</file>
<file>keyboard.svg</file>
<file>keyboard_last.svg</file>
</qresource>
</RCC>

View File

@ -21,8 +21,12 @@ class AudioGroup
public:
operator bool() const { return m_valid; }
explicit AudioGroup(const AudioGroupData& data);
explicit AudioGroup(SystemStringView groupPath);
AudioGroup() = default;
explicit AudioGroup(const AudioGroupData& data) { assign(data); }
explicit AudioGroup(SystemStringView groupPath) { assign(groupPath); }
void assign(const AudioGroupData& data);
void assign(SystemStringView groupPath);
const AudioGroupSampleDirectory::Entry* getSample(SampleId sfxId) const;
const unsigned char* getSampleData(SampleId sfxId, const AudioGroupSampleDirectory::Entry* sample) const;
@ -43,10 +47,16 @@ class AudioGroupDatabase : public AudioGroup
amuse::NameDB m_layersDb;
public:
AudioGroupDatabase() = default;
explicit AudioGroupDatabase(const AudioGroupData& data)
: AudioGroup(data) {}
{
assign(data);
}
explicit AudioGroupDatabase(SystemStringView groupPath)
: AudioGroup(groupPath) {}
{
setIdDatabases();
assign(groupPath);
}
void setIdDatabases()
{

View File

@ -118,6 +118,7 @@ struct SoundMacro
MulVars,
DivVars,
AddIVars,
SetVar,
IfEqual = 0x70,
IfLess,
Invalid = 0xff
@ -926,6 +927,17 @@ struct SoundMacro
bool Do(SoundMacroState& st, Voice& vox) const;
CmdOp Isa() const { return CmdOp::AddIVars; }
};
struct CmdSetVar : ICmd
{
AT_DECL_DNA_YAML
AT_DECL_DNAV
Value<bool> varCtrlA;
Value<atInt8> a;
Seek<1, athena::Current> pad;
Value<atInt16> imm;
bool Do(SoundMacroState& st, Voice& vox) const;
CmdOp Isa() const { return CmdOp::SetVar; }
};
struct CmdIfEqual : ICmd
{
AT_DECL_DNA_YAML
@ -1149,10 +1161,10 @@ class AudioGroupPool
std::unordered_map<KeymapId, Keymap> m_keymaps;
std::unordered_map<LayersId, std::vector<LayerMapping>> m_layers;
AudioGroupPool() = default;
template <athena::Endian DNAE>
static AudioGroupPool _AudioGroupPool(athena::io::IStreamReader& r);
public:
AudioGroupPool() = default;
static AudioGroupPool CreateAudioGroupPool(const AudioGroupData& data);
static AudioGroupPool CreateAudioGroupPool(SystemStringView groupPath);

View File

@ -183,7 +183,6 @@ class AudioGroupProject
std::unordered_map<GroupId, SongGroupIndex> m_songGroups;
std::unordered_map<GroupId, SFXGroupIndex> m_sfxGroups;
AudioGroupProject() = default;
AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag);
template <athena::Endian DNAE>
static AudioGroupProject _AudioGroupProject(athena::io::IStreamReader& r, bool absOffs);
@ -192,6 +191,7 @@ class AudioGroupProject
template <athena::Endian DNAE>
static void BootstrapObjectIDs(athena::io::IStreamReader& r, bool absOffs);
public:
AudioGroupProject() = default;
static AudioGroupProject CreateAudioGroupProject(const AudioGroupData& data);
static AudioGroupProject CreateAudioGroupProject(SystemStringView groupPath);
static void BootstrapObjectIDs(const AudioGroupData& data);

View File

@ -253,9 +253,8 @@ private:
static void _extractCompressed(SampleId id, const Entry& ent, amuse::SystemStringView destDir,
const unsigned char* samp);
AudioGroupSampleDirectory() = default;
public:
AudioGroupSampleDirectory() = default;
AudioGroupSampleDirectory(athena::io::IStreamReader& r, GCNDataTag);
AudioGroupSampleDirectory(athena::io::IStreamReader& r, const unsigned char* sampData, bool absOffs, N64DataTag);
AudioGroupSampleDirectory(athena::io::IStreamReader& r, bool absOffs, PCDataTag);

View File

@ -4,19 +4,21 @@
namespace amuse
{
AudioGroup::AudioGroup(const AudioGroupData& data)
: m_proj(AudioGroupProject::CreateAudioGroupProject(data))
, m_pool(AudioGroupPool::CreateAudioGroupPool(data))
, m_sdir(AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(data))
, m_samp(data.getSamp())
{}
AudioGroup::AudioGroup(SystemStringView groupPath)
: m_proj(AudioGroupProject::CreateAudioGroupProject(groupPath))
, m_pool(AudioGroupPool::CreateAudioGroupPool(groupPath))
, m_sdir(AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(groupPath))
, m_groupPath(groupPath)
{}
void AudioGroup::assign(const AudioGroupData& data)
{
m_proj = AudioGroupProject::CreateAudioGroupProject(data);
m_pool = AudioGroupPool::CreateAudioGroupPool(data);
m_sdir = AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(data);
m_samp = data.getSamp();
}
void AudioGroup::assign(SystemStringView groupPath)
{
/* Reverse order when loading intermediates */
m_sdir = AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(groupPath);
m_pool = AudioGroupPool::CreateAudioGroupPool(groupPath);
m_proj = AudioGroupProject::CreateAudioGroupProject(groupPath);
m_samp = nullptr;
}
const AudioGroupSampleDirectory::Entry* AudioGroup::getSample(SampleId sfxId) const
{

View File

@ -134,7 +134,7 @@ AudioGroupPool AudioGroupPool::CreateAudioGroupPool(SystemStringView groupPath)
if (!fi.hasError())
{
athena::io::YAMLDocReader r;
if (r.parse(&fi) && r.ValidateClassType("amuse::Pool"))
if (r.parse(&fi) && !r.readString("DNAType").compare("amuse::Pool"))
{
if (auto __r = r.enterSubRecord("soundMacros"))
{
@ -518,6 +518,8 @@ std::unique_ptr<SoundMacro::ICmd> SoundMacro::MakeCmd(R& r)
cmd = _MakeCmd<CmdDivVars>(r); break;
case CmdOp::AddIVars:
cmd = _MakeCmd<CmdAddIVars>(r); break;
case CmdOp::SetVar:
cmd = _MakeCmd<CmdSetVar>(r); break;
case CmdOp::IfEqual:
cmd = _MakeCmd<CmdIfEqual>(r); break;
case CmdOp::IfLess:
@ -684,6 +686,8 @@ std::string_view SoundMacro::CmdOpToStr(CmdOp op)
return "DivVars"sv;
case CmdOp::AddIVars:
return "AddIVars"sv;
case CmdOp::SetVar:
return "SetVar"sv;
case CmdOp::IfEqual:
return "IfEqual"sv;
case CmdOp::IfLess:
@ -845,6 +849,8 @@ SoundMacro::CmdOp SoundMacro::CmdStrToOp(std::string_view op)
return CmdOp::DivVars;
else if (!CompareCaseInsensitive(op.data(), "AddIVars"))
return CmdOp::AddIVars;
else if (!CompareCaseInsensitive(op.data(), "SetVar"))
return CmdOp::SetVar;
else if (!CompareCaseInsensitive(op.data(), "IfEqual"))
return CmdOp::IfEqual;
else if (!CompareCaseInsensitive(op.data(), "IfLess"))

View File

@ -229,7 +229,7 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView gr
if (!fi.hasError())
{
athena::io::YAMLDocReader r;
if (r.parse(&fi) && r.ValidateClassType("amuse::Project"))
if (r.parse(&fi) && !r.readString("DNAType").compare("amuse::Project"))
{
if (auto __v = r.enterSubRecord("songGroups"))
{
@ -289,7 +289,7 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView gr
ret.m_sfxGroups.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()))
{
uint16_t groupId;
std::string groupName = ParseStringSlashId(grp.first, groupId);

View File

@ -1024,6 +1024,16 @@ bool SoundMacro::CmdAddIVars::Do(SoundMacroState& st, Voice& vox) const
return false;
}
bool SoundMacro::CmdSetVar::Do(SoundMacroState& st, Voice& vox) const
{
if (varCtrlA)
vox.setCtrlValue(a, imm);
else
st.m_variables[a] = imm;
return false;
}
bool SoundMacro::CmdIfEqual::Do(SoundMacroState& st, Voice& vox) const
{
int32_t useA, useB;