Lots of foundational work for Amuse editor

This commit is contained in:
Jack Andersen 2018-07-13 20:06:33 -10:00
parent 1e8b6e599c
commit 4c884d019d
47 changed files with 4202 additions and 2044 deletions

View File

@ -16,6 +16,9 @@ if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/boo AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}
include_directories(athena/include) include_directories(athena/include)
endif() endif()
atdna(atdna_AudioGroupPool.cpp include/amuse/AudioGroupPool.hpp)
atdna(atdna_AudioGroupProject.cpp include/amuse/AudioGroupProject.hpp)
set(SOURCES set(SOURCES
lib/AudioGroup.cpp lib/AudioGroup.cpp
lib/AudioGroupData.cpp lib/AudioGroupData.cpp
@ -38,8 +41,11 @@ set(SOURCES
lib/EffectChorus.cpp lib/EffectChorus.cpp
lib/EffectDelay.cpp lib/EffectDelay.cpp
lib/ContainerRegistry.cpp lib/ContainerRegistry.cpp
lib/Common.cpp
lib/DSPCodec.cpp lib/DSPCodec.cpp
lib/N64MusyXCodec.cpp) lib/N64MusyXCodec.cpp
atdna_AudioGroupPool.cpp
atdna_AudioGroupProject.cpp)
set(HEADERS set(HEADERS
include/amuse/AudioGroup.hpp include/amuse/AudioGroup.hpp

View File

@ -0,0 +1 @@
#include "AudioGroupModel.hpp"

View File

@ -0,0 +1,21 @@
#ifndef AMUSE_AUDIO_GROUP_MODEL_HPP
#define AMUSE_AUDIO_GROUP_MODEL_HPP
#include "amuse/AudioGroup.hpp"
class AudioGroupModel
{
};
class SFXGroupModel : public AudioGroupModel
{
};
class SongGroupModel : public AudioGroupModel
{
};
#endif //AMUSE_AUDIO_GROUP_MODEL_HPP

View File

@ -8,13 +8,12 @@ set(AUTORCC ON)
find_package(Qt5Widgets) find_package(Qt5Widgets)
find_package(Qt5Network) find_package(Qt5Network)
find_package(Qt5Xml) find_package(Qt5Xml)
find_package(Qt5Svg)
if(WIN32) if(WIN32)
list(APPEND PLAT_SRCS platforms/win/amuse-gui.rc platforms/win/amuse-gui.manifest) list(APPEND PLAT_SRCS platforms/win/amuse-gui.rc platforms/win/amuse-gui.manifest)
elseif(APPLE) elseif(APPLE)
list(APPEND PLAT_SRCS platforms/mac/mainicon.icns MacOSExtras.mm) list(APPEND PLAT_SRCS platforms/mac/mainicon.icns MacOSExtras.mm)
find_library(APPKIT_LIBRARY AppKit)
list(APPEND PLAT_LIBS ${APPKIT_LIBRARY})
set_source_files_properties(platforms/mac/mainicon.icns PROPERTIES set_source_files_properties(platforms/mac/mainicon.icns PROPERTIES
MACOSX_PACKAGE_LOCATION Resources) MACOSX_PACKAGE_LOCATION Resources)
endif() endif()
@ -26,20 +25,26 @@ list(APPEND PLAT_SRCS mainicon_qt.cpp)
QT5_ADD_RESOURCES(qrc_resources.cpp resources/resources.qrc) QT5_ADD_RESOURCES(qrc_resources.cpp resources/resources.qrc)
add_executable(amuse-gui WIN32 MACOSX_BUNDLE add_executable(amuse-gui WIN32 MACOSX_BUNDLE
Common.hpp Common.cpp
MainWindow.ui MainWindow.hpp MainWindow.cpp MainWindow.ui MainWindow.hpp MainWindow.cpp
KeyboardWidget.hpp KeyboardWidget.cpp KeyboardWidget.hpp KeyboardWidget.cpp
StatusBarWidget.hpp StatusBarWidget.cpp StatusBarWidget.hpp StatusBarWidget.cpp
ProjectModel.hpp ProjectModel.cpp ProjectModel.hpp ProjectModel.cpp
ProjectStatistics.hpp ProjectStatistics.cpp ProjectStatistics.hpp ProjectStatistics.cpp
EditorWidget.hpp EditorWidget.cpp
SoundMacroEditor.hpp SoundMacroEditor.cpp SoundMacroEditor.hpp SoundMacroEditor.cpp
KeymapEditor.hpp KeymapEditor.cpp KeymapEditor.hpp KeymapEditor.cpp
LayersEditor.hpp LayersEditor.cpp LayersEditor.hpp LayersEditor.cpp
SampleEditor.hpp SampleEditor.cpp SampleEditor.hpp SampleEditor.cpp
SoundGroupEditor.hpp SoundGroupEditor.cpp SFXGroupEditor.hpp SFXGroupEditor.cpp
SongGroupEditor.hpp SongGroupEditor.cpp SongGroupEditor.hpp SongGroupEditor.cpp
AudioGroupModel.hpp AudioGroupModel.cpp
resources/resources.qrc qrc_resources.cpp resources/resources.qrc qrc_resources.cpp
${PLAT_SRCS} ${PLAT_SRCS}
main.cpp) main.cpp)
if(COMMAND add_sanitizers)
add_sanitizers(amuse-gui)
endif()
set_target_properties(amuse-gui PROPERTIES set_target_properties(amuse-gui PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/platforms/mac/Info.plist") MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/platforms/mac/Info.plist")
@ -48,4 +53,5 @@ target_link_libraries(amuse-gui ${PLAT_LIBS}
${Qt5Widgets_LIBRARIES} ${Qt5Widgets_LIBRARIES}
${Qt5Network_LIBRARIES} ${Qt5Network_LIBRARIES}
${Qt5Xml_LIBRARIES} ${Qt5Xml_LIBRARIES}
boo logvisor zeus athena-core athena-libyaml xxhash z) ${Qt5Svg_LIBRARIES}
amuse boo ${BOO_SYS_LIBS} logvisor zeus athena-core athena-libyaml xxhash ${ZLIB_LIBRARIES} ${LZO_LIB})

38
Editor/Common.cpp Normal file
View File

@ -0,0 +1,38 @@
#include "Common.hpp"
#include <QMessageBox>
#include <QObject>
boo::SystemString QStringToSysString(const QString& str)
{
#ifdef _WIN32
return (wchar_t*)str.utf16();
#else
return str.toUtf8().toStdString();
#endif
}
QString SysStringToQString(const boo::SystemString& str)
{
#ifdef _WIN32
return QString::fromStdWString(str);
#else
return QString::fromStdString(str);
#endif
}
bool MkPath(const QString& path, QWidget* parent)
{
QFileInfo fInfo(path);
return MkPath(fInfo.dir(), fInfo.fileName(), parent);
}
bool MkPath(const QDir& dir, const QString& file, QWidget* parent)
{
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);
return false;
}
return true;
}

14
Editor/Common.hpp Normal file
View File

@ -0,0 +1,14 @@
#ifndef AMUSE_COMMON_HPP
#define AMUSE_COMMON_HPP
#include "boo/System.hpp"
#include <QString>
#include <QDir>
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);
#endif //AMUSE_COMMON_HPP

7
Editor/EditorWidget.cpp Normal file
View File

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

14
Editor/EditorWidget.hpp Normal file
View File

@ -0,0 +1,14 @@
#ifndef AMUSE_EDITOR_WIDGET_HPP
#define AMUSE_EDITOR_WIDGET_HPP
#include <QWidget>
class EditorWidget : public QWidget
{
Q_OBJECT
public:
explicit EditorWidget(QWidget* parent = Q_NULLPTR);
};
#endif //AMUSE_EDITOR_WIDGET_HPP

View File

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

View File

@ -1,9 +1,9 @@
#ifndef AMUSE_KEYMAP_EDITOR_HPP #ifndef AMUSE_KEYMAP_EDITOR_HPP
#define AMUSE_KEYMAP_EDITOR_HPP #define AMUSE_KEYMAP_EDITOR_HPP
#include <QWidget> #include "EditorWidget.hpp"
class KeymapEditor : public QWidget class KeymapEditor : public EditorWidget
{ {
Q_OBJECT Q_OBJECT
public: public:

View File

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

View File

@ -1,9 +1,9 @@
#ifndef AMUSE_LAYERS_EDITOR_HPP #ifndef AMUSE_LAYERS_EDITOR_HPP
#define AMUSE_LAYERS_EDITOR_HPP #define AMUSE_LAYERS_EDITOR_HPP
#include <QWidget> #include "EditorWidget.hpp"
class LayersEditor : public QWidget class LayersEditor : public EditorWidget
{ {
Q_OBJECT Q_OBJECT
public: public:

View File

@ -1,10 +1,398 @@
#include "MainWindow.hpp" #include "MainWindow.hpp"
#include "ui_MainWindow.h" #include <QFileDialog>
#include <QMessageBox>
#include <QLineEdit>
#include <QInputDialog>
#include <QtSvg/QtSvg>
#include "amuse/ContainerRegistry.hpp"
#include "Common.hpp"
MainWindow::MainWindow(QWidget* parent) MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent), : QMainWindow(parent),
m_ui(new Ui::MainWindow) m_undoStack(new QUndoStack(this))
{ {
m_ui->setupUi(this); m_ui.setupUi(this);
m_ui.actionNew_Project->setShortcut(QKeySequence::New);
connect(m_ui.actionNew_Project, SIGNAL(triggered()), this, SLOT(newAction()));
m_ui.actionOpen_Project->setShortcut(QKeySequence::Open);
connect(m_ui.actionOpen_Project, SIGNAL(triggered()), this, SLOT(openAction()));
connect(m_ui.actionImport, SIGNAL(triggered()), this, SLOT(importAction()));
connect(m_ui.actionExport_GameCube_Groups, SIGNAL(triggered()), this, SLOT(exportAction()));
#ifndef __APPLE__
m_ui.menuFile->addSeparator();
QAction* quitAction = m_ui.menuFile->addAction(tr("Quit"));
quitAction->setShortcut(QKeySequence::Quit);
connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
#endif
m_ui.actionUndo->setShortcut(QKeySequence::Undo);
m_ui.actionRedo->setShortcut(QKeySequence::Redo);
m_ui.actionCut->setShortcut(QKeySequence::Cut);
m_ui.actionCopy->setShortcut(QKeySequence::Copy);
m_ui.actionPaste->setShortcut(QKeySequence::Paste);
m_ui.actionDelete->setShortcut(QKeySequence::Delete);
onFocusChanged(nullptr, this);
m_ui.editorSvg->load(QStringLiteral(":/bg/FaceGrey.svg"));
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_Keymap, SIGNAL(triggered()), this, SLOT(newKeymapAction()));
connect(m_ui.actionNew_Layers, SIGNAL(triggered()), this, SLOT(newLayersAction()));
connect(m_ui.menuAudio, SIGNAL(aboutToShow()), this, SLOT(aboutToShowAudioIOMenu()));
connect(m_ui.menuMIDI, SIGNAL(aboutToShow()), this, SLOT(aboutToShowMIDIIOMenu()));
connect(qApp, SIGNAL(focusChanged(QWidget*,QWidget*)), this, SLOT(onFocusChanged(QWidget*,QWidget*)));
setFocusAudioGroup(nullptr);
m_voxEngine = boo::NewAudioVoiceEngine();
m_voxAllocator = std::make_unique<amuse::BooBackendVoiceAllocator>(*m_voxEngine);
m_engine = std::make_unique<amuse::Engine>(*m_voxAllocator);
} }
MainWindow::~MainWindow()
{
printf("IM DYING\n");
}
bool MainWindow::setProjectPath(const QString& path)
{
if (m_projectModel && m_projectModel->path() == path)
return true;
QDir dir(path);
if (!dir.exists())
{
QString msg = QString(tr("The directory at '%1' must exist for the Amuse editor.")).arg(path);
QMessageBox::critical(this, tr("Directory does not exist"), msg);
return false;
}
QString testWritePath = dir.filePath(tr("test"));
QFile testWriteFile(testWritePath);
if (!testWriteFile.open(QFile::ReadWrite))
{
QString msg = QString(tr("The directory at '%1' must be writable for the Amuse editor.")).arg(path);
QMessageBox::critical(this, tr("Unable to write to directory"), msg);
return false;
}
testWriteFile.remove();
if (m_projectModel)
m_projectModel->deleteLater();
m_projectModel = new ProjectModel(path, this);
m_ui.projectOutline->setModel(m_projectModel);
m_ui.actionExport_GameCube_Groups->setEnabled(true);
setWindowFilePath(path);
setFocusAudioGroup(nullptr);
onFocusChanged(nullptr, focusWidget());
return true;
}
void MainWindow::setFocusAudioGroup(AudioGroupModel* group)
{
m_focusAudioGroup = group;
bool active = m_focusAudioGroup != nullptr;
m_ui.actionNew_Sound_Macro->setEnabled(active);
m_ui.actionNew_Keymap->setEnabled(active);
m_ui.actionNew_Layers->setEnabled(active);
}
void MainWindow::refreshAudioIO()
{
QList<QAction*> audioActions = m_ui.menuAudio->actions();
if (audioActions.size() > 3)
for (auto it = audioActions.begin() + 3 ; it != audioActions.end() ; ++it)
m_ui.menuAudio->removeAction(*it);
bool addedDev = false;
// TODO: Do
if (!addedDev)
m_ui.menuAudio->addAction(tr("No Audio Devices Found"))->setEnabled(false);
}
void MainWindow::refreshMIDIIO()
{
QList<QAction*> midiActions = m_ui.menuMIDI->actions();
if (midiActions.size() > 2)
for (auto it = midiActions.begin() + 2 ; it != midiActions.end() ; ++it)
m_ui.menuMIDI->removeAction(*it);
bool addedDev = false;
if (m_voxEngine)
{
for (const auto& dev : m_voxEngine->enumerateMIDIDevices())
{
QAction* act = m_ui.menuMIDI->addAction(QString::fromStdString(dev.second));
act->setData(QString::fromStdString(dev.first));
connect(act, SIGNAL(triggered()), this, SLOT(setMIDIIO()));
addedDev = true;
}
}
if (!addedDev)
m_ui.menuMIDI->addAction(tr("No MIDI Devices Found"))->setEnabled(false);
}
void MainWindow::newAction()
{
QString path = QFileDialog::getSaveFileName(this, tr("New Project"));
if (path.isEmpty())
return;
if (!MkPath(path, this))
return;
setProjectPath(path);
}
void MainWindow::openAction()
{
QString path = QFileDialog::getExistingDirectory(this, tr("Open Project"));
if (path.isEmpty())
return;
setProjectPath(path);
}
void MainWindow::importAction()
{
QString path = QFileDialog::getOpenFileName(this, tr("Import Project"));
if (path.isEmpty())
return;
/* Validate input file */
amuse::ContainerRegistry::Type tp =
amuse::ContainerRegistry::DetectContainerType(QStringToSysString(path).c_str());
if (tp == amuse::ContainerRegistry::Type::Invalid)
{
QString msg = QString(tr("The file at '%1' could not be interpreted as a MusyX container.")).arg(path);
QMessageBox::critical(this, tr("Unsupported MusyX Container"), msg);
return;
}
/* Ask user about sample conversion */
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."),
tr("Import Compressed"), tr("Import WAVs"), tr("Import Both"));
switch (impMode)
{
case 0:
case 1:
case 2:
break;
default:
return;
}
/* Special handling for raw groups - gather sibling groups in filesystem */
if (tp == amuse::ContainerRegistry::Type::Raw4)
{
int scanMode = QMessageBox::question(this, tr("Raw Import Mode"),
tr("Would you like to scan for all MusyX group files in this directory?"),
QMessageBox::Yes, QMessageBox::No);
if (scanMode == QMessageBox::Yes)
{
/* Auto-create project */
if (m_projectModel == nullptr)
{
QString newName;
bool ok = true;
while (ok && newName.isEmpty())
newName = QInputDialog::getText(this, tr("Project Name"), tr("What should this project be named?"),
QLineEdit::Normal, QString(), &ok);
if (!ok)
return;
QFileInfo fInfo(path);
QString newPath = QFileInfo(fInfo.dir(), newName).filePath();
printf("%s\n", newPath.toUtf8().data());
if (!MkPath(fInfo.dir(), newName, this))
return;
if (!setProjectPath(newPath))
return;
}
QDir dir = QFileInfo(path).dir();
QStringList filters;
filters << "*.proj" << "*.pro";
QStringList files = dir.entryList(filters, QDir::Files);
for (const QString& fPath : files)
{
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),
ProjectModel::ImportMode(impMode), this))
return;
}
m_projectModel->saveToFile(this);
return;
}
else if (scanMode == QMessageBox::No)
{}
else
{
return;
}
}
/* Auto-create project */
if (m_projectModel == nullptr)
{
QFileInfo fInfo(path);
QString newPath = QFileInfo(fInfo.dir(), fInfo.completeBaseName()).filePath();
if (!MkPath(fInfo.dir(), fInfo.completeBaseName(), this))
return;
if (!setProjectPath(newPath))
return;
}
/* Handle single container */
auto data = amuse::ContainerRegistry::LoadContainer(QStringToSysString(path).c_str());
for (auto& p : data)
if (!m_projectModel->importGroupData(SysStringToQString(p.first), std::move(p.second),
ProjectModel::ImportMode(impMode), this))
return;
m_projectModel->saveToFile(this);
}
void MainWindow::exportAction()
{
}
void MainWindow::newSFXGroupAction()
{
}
void MainWindow::newSongGroupAction()
{
}
void MainWindow::newSoundMacroAction()
{
}
void MainWindow::newKeymapAction()
{
}
void MainWindow::newLayersAction()
{
}
void MainWindow::aboutToShowAudioIOMenu()
{
refreshAudioIO();
}
void MainWindow::aboutToShowMIDIIOMenu()
{
refreshMIDIIO();
}
void MainWindow::setAudioIO()
{
// TODO: Do
}
void MainWindow::setMIDIIO()
{
// TODO: Do
//qobject_cast<QAction*>(sender())->data().toString().toUtf8().constData();
}
void MainWindow::onFocusChanged(QWidget* old, QWidget* now)
{
disconnect(m_undoConn);
disconnect(m_canUndoConn);
disconnect(m_redoConn);
disconnect(m_canRedoConn);
disconnect(m_cutConn);
disconnect(m_copyConn);
disconnect(m_pasteConn);
disconnect(m_deleteConn);
disconnect(m_canEditConn);
if (QLineEdit* le = qobject_cast<QLineEdit*>(now))
{
m_undoConn = connect(m_ui.actionUndo, SIGNAL(triggered()), le, SLOT(undo()));
m_canUndoConn = connect(le, SIGNAL(textChanged()), this, SLOT(onTextEdited()));
m_ui.actionUndo->setEnabled(le->isUndoAvailable());
m_redoConn = connect(m_ui.actionRedo, SIGNAL(triggered()), le, SLOT(redo()));
m_ui.actionRedo->setEnabled(le->isRedoAvailable());
m_cutConn = connect(m_ui.actionCut, SIGNAL(triggered()), le, SLOT(cut()));
m_ui.actionCut->setEnabled(le->hasSelectedText());
m_copyConn = connect(m_ui.actionCopy, SIGNAL(triggered()), le, SLOT(copy()));
m_ui.actionCopy->setEnabled(le->hasSelectedText());
m_pasteConn = connect(m_ui.actionPaste, SIGNAL(triggered()), le, SLOT(paste()));
m_ui.actionPaste->setEnabled(true);
m_deleteConn = connect(m_ui.actionDelete, SIGNAL(triggered()), this, SLOT(onTextDelete()));
m_ui.actionDelete->setEnabled(true);
m_canEditConn = connect(le, SIGNAL(selectionChanged()), this, SLOT(onTextSelect()));
return;
}
m_undoConn = connect(m_ui.actionUndo, SIGNAL(triggered()), m_undoStack, SLOT(undo()));
m_canUndoConn = connect(m_undoStack, SIGNAL(canUndoChanged(bool)), m_ui.actionUndo, SLOT(setEnabled(bool)));
m_ui.actionUndo->setEnabled(m_undoStack->canUndo());
m_redoConn = connect(m_ui.actionRedo, SIGNAL(triggered()), m_undoStack, SLOT(redo()));
m_canRedoConn = connect(m_undoStack, SIGNAL(canRedoChanged(bool)), m_ui.actionRedo, SLOT(setEnabled(bool)));
m_ui.actionRedo->setEnabled(m_undoStack->canRedo());
if (now == m_ui.projectOutline || m_ui.projectOutline->isAncestorOf(now))
{
m_ui.actionCut->setEnabled(false);
m_ui.actionCopy->setEnabled(false);
m_ui.actionPaste->setEnabled(false);
if (m_projectModel)
{
m_deleteConn = connect(m_ui.actionDelete, SIGNAL(triggered()), m_projectModel, SLOT(del()));
m_ui.actionDelete->setEnabled(m_projectModel->canDelete());
m_canEditConn = connect(m_projectModel, SIGNAL(canDeleteChanged(bool)),
m_ui.actionDelete, SLOT(setEnabled(bool)));
}
}
else if (now == m_ui.editorContents || m_ui.editorContents->isAncestorOf(now))
{
}
}
void MainWindow::onTextEdited()
{
if (QLineEdit* le = qobject_cast<QLineEdit*>(sender()))
{
m_ui.actionUndo->setEnabled(le->isUndoAvailable());
m_ui.actionRedo->setEnabled(le->isRedoAvailable());
}
}
void MainWindow::onTextSelect()
{
if (QLineEdit* le = qobject_cast<QLineEdit*>(sender()))
{
m_ui.actionCut->setEnabled(le->hasSelectedText());
m_ui.actionCopy->setEnabled(le->hasSelectedText());
}
}
void MainWindow::onTextDelete()
{
if (QLineEdit* le = qobject_cast<QLineEdit*>(focusWidget()))
{
le->del();
}
}

View File

@ -2,16 +2,74 @@
#define AMUSE_MAINWINDOW_HPP #define AMUSE_MAINWINDOW_HPP
#include <QMainWindow> #include <QMainWindow>
#include <QUndoStack>
#include "ui_MainWindow.h"
#include "amuse/Engine.hpp"
#include "amuse/BooBackend.hpp"
#include "boo/audiodev/IAudioVoiceEngine.hpp"
#include "ProjectModel.hpp"
namespace Ui { namespace Ui {
class MainWindow; class MainWindow;
} }
class AudioGroupModel;
class MainWindow : public QMainWindow class MainWindow : public QMainWindow
{ {
Q_OBJECT Q_OBJECT
Ui::MainWindow* m_ui; Ui::MainWindow m_ui;
ProjectModel* m_projectModel = nullptr;
AudioGroupModel* m_focusAudioGroup = nullptr;
std::unique_ptr<boo::IAudioVoiceEngine> m_voxEngine;
std::unique_ptr<amuse::BooBackendVoiceAllocator> m_voxAllocator;
std::unique_ptr<amuse::Engine> m_engine;
QUndoStack* m_undoStack;
QMetaObject::Connection m_undoConn;
QMetaObject::Connection m_canUndoConn;
QMetaObject::Connection m_redoConn;
QMetaObject::Connection m_canRedoConn;
QMetaObject::Connection m_cutConn;
QMetaObject::Connection m_copyConn;
QMetaObject::Connection m_pasteConn;
QMetaObject::Connection m_deleteConn;
QMetaObject::Connection m_canEditConn;
bool setProjectPath(const QString& path);
void setFocusAudioGroup(AudioGroupModel* group);
void refreshAudioIO();
void refreshMIDIIO();
public: public:
explicit MainWindow(QWidget* parent = Q_NULLPTR); explicit MainWindow(QWidget* parent = Q_NULLPTR);
~MainWindow();
public slots:
void newAction();
void openAction();
void importAction();
void exportAction();
void newSFXGroupAction();
void newSongGroupAction();
void newSoundMacroAction();
void newKeymapAction();
void newLayersAction();
void aboutToShowAudioIOMenu();
void aboutToShowMIDIIOMenu();
void setAudioIO();
void setMIDIIO();
void onFocusChanged(QWidget* old, QWidget* now);
void onTextEdited();
void onTextSelect();
void onTextDelete();
}; };

View File

@ -91,6 +91,18 @@
<height>400</height> <height>400</height>
</size> </size>
</property> </property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="midLineWidth">
<number>-2</number>
</property>
<property name="widgetResizable"> <property name="widgetResizable">
<bool>true</bool> <bool>true</bool>
</property> </property>
@ -99,10 +111,28 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>538</width> <width>540</width>
<height>450</height> <height>442</height>
</rect> </rect>
</property> </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> </widget>
<widget class="QScrollArea" name="keyboardScrollArea"> <widget class="QScrollArea" name="keyboardScrollArea">
@ -124,6 +154,15 @@
<height>100</height> <height>100</height>
</size> </size>
</property> </property>
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Plain</enum>
</property>
<property name="lineWidth">
<number>0</number>
</property>
<property name="widgetResizable"> <property name="widgetResizable">
<bool>true</bool> <bool>true</bool>
</property> </property>
@ -132,8 +171,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>538</width> <width>540</width>
<height>98</height> <height>100</height>
</rect> </rect>
</property> </property>
<property name="maximumSize"> <property name="maximumSize">
@ -155,23 +194,23 @@
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>800</width> <width>800</width>
<height>22</height> <height>27</height>
</rect> </rect>
</property> </property>
<widget class="QMenu" name="menuFile"> <widget class="QMenu" name="menuFile">
<property name="title"> <property name="title">
<string>File</string> <string>Fi&amp;le</string>
</property> </property>
<addaction name="actionNew_Project"/> <addaction name="actionNew_Project"/>
<addaction name="actionOpen_Project"/> <addaction name="actionOpen_Project"/>
<addaction name="actionImport_Project"/> <addaction name="actionImport"/>
<addaction name="separator"/> <addaction name="actionExport_GameCube_Groups"/>
</widget> </widget>
<widget class="QMenu" name="menuProject"> <widget class="QMenu" name="menuProject">
<property name="title"> <property name="title">
<string>Project</string> <string>P&amp;roject</string>
</property> </property>
<addaction name="actionNew_Sound_Group"/> <addaction name="actionNew_SFX_Group"/>
<addaction name="actionNew_Song_Group"/> <addaction name="actionNew_Song_Group"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionNew_Sound_Macro"/> <addaction name="actionNew_Sound_Macro"/>
@ -180,7 +219,7 @@
</widget> </widget>
<widget class="QMenu" name="menuAudio"> <widget class="QMenu" name="menuAudio">
<property name="title"> <property name="title">
<string>Audio</string> <string>A&amp;udio</string>
</property> </property>
<addaction name="actionAuto_Play"/> <addaction name="actionAuto_Play"/>
<addaction name="separator"/> <addaction name="separator"/>
@ -188,7 +227,7 @@
</widget> </widget>
<widget class="QMenu" name="menuMIDI"> <widget class="QMenu" name="menuMIDI">
<property name="title"> <property name="title">
<string>MIDI</string> <string>&amp;MIDI</string>
</property> </property>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionInput_Device"/> <addaction name="actionInput_Device"/>
@ -214,129 +253,128 @@
<widget class="StatusBarWidget" name="statusbar"/> <widget class="StatusBarWidget" name="statusbar"/>
<action name="actionNew_Project"> <action name="actionNew_Project">
<property name="text"> <property name="text">
<string>New Project</string> <string>&amp;New Project</string>
</property>
<property name="shortcut">
<string>Ctrl+N</string>
</property> </property>
</action> </action>
<action name="actionOpen_Project"> <action name="actionOpen_Project">
<property name="text"> <property name="text">
<string>Open Project</string> <string>&amp;Open Project</string>
</property>
<property name="shortcut">
<string>Ctrl+O</string>
</property>
</action>
<action name="actionDuplicate_Project">
<property name="text">
<string>Duplicate Project</string>
</property>
</action>
<action name="actionQuit">
<property name="text">
<string>Quit</string>
</property> </property>
</action> </action>
<action name="actionUndo"> <action name="actionUndo">
<property name="text"> <property name="enabled">
<string>Undo</string> <bool>false</bool>
</property> </property>
<property name="shortcut"> <property name="text">
<string>Ctrl+Z</string> <string>&amp;Undo</string>
</property> </property>
</action> </action>
<action name="actionRedo"> <action name="actionRedo">
<property name="text"> <property name="enabled">
<string>Redo</string> <bool>false</bool>
</property> </property>
<property name="shortcut"> <property name="text">
<string>Ctrl+Shift+Z</string> <string>&amp;Redo</string>
</property> </property>
</action> </action>
<action name="actionCut"> <action name="actionCut">
<property name="text"> <property name="enabled">
<string>Cut</string> <bool>false</bool>
</property> </property>
<property name="shortcut"> <property name="text">
<string>Ctrl+X</string> <string>&amp;Cut</string>
</property> </property>
</action> </action>
<action name="actionCopy"> <action name="actionCopy">
<property name="text"> <property name="enabled">
<string>Copy</string> <bool>false</bool>
</property> </property>
<property name="shortcut"> <property name="text">
<string>Ctrl+C</string> <string>C&amp;opy</string>
</property> </property>
</action> </action>
<action name="actionPaste"> <action name="actionPaste">
<property name="text"> <property name="enabled">
<string>Paste</string> <bool>false</bool>
</property> </property>
<property name="shortcut"> <property name="text">
<string>Ctrl+V</string> <string>&amp;Paste</string>
</property> </property>
</action> </action>
<action name="actionDelete"> <action name="actionDelete">
<property name="text"> <property name="enabled">
<string>Delete</string> <bool>false</bool>
</property> </property>
<property name="shortcut"> <property name="text">
<string>Del</string> <string>&amp;Delete</string>
</property> </property>
</action> </action>
<action name="actionImport_Project"> <action name="actionImport">
<property name="text"> <property name="text">
<string>Import Project</string> <string>&amp;Import</string>
</property> </property>
<property name="shortcut"> <property name="shortcut">
<string>Ctrl+I</string> <string>Ctrl+I</string>
</property> </property>
</action> </action>
<action name="actionNew_Sound_Group"> <action name="actionNew_SFX_Group">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon"> <property name="icon">
<iconset> <iconset>
<normaloff>:/icons/IconNewSoundGroup.svg</normaloff>:/icons/IconNewSoundGroup.svg</iconset> <normaloff>:/icons/IconNewSoundGroup.svg</normaloff>:/icons/IconNewSoundGroup.svg</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>New Sound Group</string> <string>&amp;New SFX Group</string>
</property> </property>
</action> </action>
<action name="actionNew_Song_Group"> <action name="actionNew_Song_Group">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon"> <property name="icon">
<iconset> <iconset>
<normaloff>:/icons/IconNewSongGroup.svg</normaloff>:/icons/IconNewSongGroup.svg</iconset> <normaloff>:/icons/IconNewSongGroup.svg</normaloff>:/icons/IconNewSongGroup.svg</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>New Song Group</string> <string>New &amp;Song Group</string>
</property> </property>
</action> </action>
<action name="actionNew_Sound_Macro"> <action name="actionNew_Sound_Macro">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon"> <property name="icon">
<iconset> <iconset>
<normaloff>:/icons/IconNewSoundMacro.svg</normaloff>:/icons/IconNewSoundMacro.svg</iconset> <normaloff>:/icons/IconNewSoundMacro.svg</normaloff>:/icons/IconNewSoundMacro.svg</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>New Sound Macro</string> <string>New Sound &amp;Macro</string>
</property> </property>
</action> </action>
<action name="actionNew_Keymap"> <action name="actionNew_Keymap">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon"> <property name="icon">
<iconset> <iconset>
<normaloff>:/icons/IconNewKeymap.svg</normaloff>:/icons/IconNewKeymap.svg</iconset> <normaloff>:/icons/IconNewKeymap.svg</normaloff>:/icons/IconNewKeymap.svg</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>New Keymap</string> <string>New &amp;Keymap</string>
</property> </property>
</action> </action>
<action name="actionNew_Layers"> <action name="actionNew_Layers">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon"> <property name="icon">
<iconset> <iconset>
<normaloff>:/icons/IconNewLayers.svg</normaloff>:/icons/IconNewLayers.svg</iconset> <normaloff>:/icons/IconNewLayers.svg</normaloff>:/icons/IconNewLayers.svg</iconset>
</property> </property>
<property name="text"> <property name="text">
<string>New Layers</string> <string>New &amp;Layers</string>
</property> </property>
</action> </action>
<action name="actionAuto_Play"> <action name="actionAuto_Play">
@ -347,7 +385,7 @@
<bool>true</bool> <bool>true</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Auto-Play</string> <string>&amp;Auto-Play</string>
</property> </property>
</action> </action>
<action name="actionSelect_Output_Device"> <action name="actionSelect_Output_Device">
@ -355,7 +393,7 @@
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Output Device:</string> <string>&amp;Output Device:</string>
</property> </property>
</action> </action>
<action name="actionInput_Device"> <action name="actionInput_Device">
@ -363,7 +401,18 @@
<bool>false</bool> <bool>false</bool>
</property> </property>
<property name="text"> <property name="text">
<string>Input Device:</string> <string>&amp;Input Device:</string>
</property>
</action>
<action name="actionExport_GameCube_Groups">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&amp;Export GameCube Groups</string>
</property>
<property name="shortcut">
<string>Ctrl+E</string>
</property> </property>
</action> </action>
</widget> </widget>
@ -379,6 +428,12 @@
<extends>QStatusBar</extends> <extends>QStatusBar</extends>
<header>StatusBarWidget.hpp</header> <header>StatusBarWidget.hpp</header>
</customwidget> </customwidget>
<customwidget>
<class>QSvgWidget</class>
<extends>QWidget</extends>
<header location="global">QSvgWidget</header>
<container>1</container>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <connections/>

View File

@ -1,7 +1,163 @@
#include <athena/FileWriter.hpp>
#include <athena/FileReader.hpp>
#include "ProjectModel.hpp" #include "ProjectModel.hpp"
#include "Common.hpp"
#include "athena/YAMLDocWriter.hpp"
ProjectModel::ProjectModel(QObject* parent) ProjectModel::ProjectModel(const QString& path, QObject* parent)
: QAbstractItemModel(parent) : QAbstractItemModel(parent), m_dir(path)
{
}
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,
ImportMode mode, QWidget* parent)
{
amuse::SongId::CurNameDB = &m_songDb;
amuse::SongId::CurNameDB = &m_sfxDb;
ProjectGroup& grp = m_groups.insert(std::make_pair(groupName, std::move(data))).first->second;
for (const auto& p : grp.m_proj.songGroups())
{
for (const auto& song : p.second.m_midiSetups)
{
char name[16];
snprintf(name, 16, "song%d", song.first.id);
m_songDb.registerPair(name, song.first);
}
}
for (const auto& p : grp.m_proj.sfxGroups())
{
for (const auto& sfx : p.second.m_sfxEntries)
{
char name[16];
snprintf(name, 16, "sfx%d", sfx.first.id);
m_sfxDb.registerPair(name, sfx.first);
}
}
return true;
}
bool ProjectModel::saveToFile(QWidget* parent)
{
amuse::SongId::CurNameDB = &m_songDb;
amuse::SongId::CurNameDB = &m_sfxDb;
if (!MkPath(m_dir.path(), parent))
return false;
for (auto& g : m_groups)
{
athena::io::YAMLDocWriter w("amuse::Group");
QDir dir(QFileInfo(m_dir, g.first).filePath());
if (!MkPath(dir.path(), parent))
return false;
if (auto __v = w.enterSubVector("songGroups"))
{
for (const auto& p : g.second.m_proj.songGroups())
{
if (auto __r = w.enterSubRecord(nullptr))
{
if (auto __v2 = w.enterSubRecord("normPages"))
{
for (const auto& pg : p.second.m_normPages)
{
char name[16];
snprintf(name, 16, "%d", pg.first);
if (auto __r2 = w.enterSubRecord(name))
pg.second.toDNA<athena::Big>(pg.first).write(w);
}
}
if (auto __v2 = w.enterSubRecord("drumPages"))
{
for (const auto& pg : p.second.m_drumPages)
{
char name[16];
snprintf(name, 16, "%d", pg.first);
if (auto __r2 = w.enterSubRecord(name))
pg.second.toDNA<athena::Big>(pg.first).write(w);
}
}
if (auto __v2 = w.enterSubRecord("songs"))
{
for (const auto& song : p.second.m_midiSetups)
{
if (auto __v3 = w.enterSubVector(m_songDb.resolveNameFromId(song.first).data()))
for (int i = 0; i < 16; ++i)
if (auto __r2 = w.enterSubRecord(nullptr))
song.second[i].write(w);
}
}
}
}
}
if (auto __v = w.enterSubVector("sfxGroups"))
{
for (const auto& p : g.second.m_proj.sfxGroups())
{
if (auto __r = w.enterSubRecord(nullptr))
{
for (const auto& sfx : p.second.m_sfxEntries)
{
if (auto __r2 = w.enterSubRecord(m_sfxDb.resolveNameFromId(sfx.first).data()))
sfx.second.toDNA<athena::Big>(sfx.first).write(w);
}
}
}
}
athena::io::FileWriter fo(QStringToSysString(dir.filePath("project.yaml")));
w.finish(&fo);
}
return true;
}
QModelIndex ProjectModel::index(int row, int column, const QModelIndex& parent) const
{
return createIndex(row, column, nullptr);
}
QModelIndex ProjectModel::parent(const QModelIndex& child) const
{
return {};
}
int ProjectModel::rowCount(const QModelIndex& parent) const
{
return 0;
}
int ProjectModel::columnCount(const QModelIndex& parent) const
{
return 0;
}
QVariant ProjectModel::data(const QModelIndex& index, int role) const
{
return {};
}
bool ProjectModel::canDelete() const
{
return false;
}
void ProjectModel::del()
{ {
} }

View File

@ -2,12 +2,61 @@
#define AMUSE_PROJECT_MODEL_HPP #define AMUSE_PROJECT_MODEL_HPP
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include <QDir>
#include <map>
#include "amuse/AudioGroupData.hpp"
#include "amuse/AudioGroupProject.hpp"
#include "amuse/AudioGroupPool.hpp"
#include "amuse/AudioGroupSampleDirectory.hpp"
class ProjectModel : public QAbstractItemModel class ProjectModel : public QAbstractItemModel
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit ProjectModel(QObject* parent = Q_NULLPTR); enum class ImportMode
{
Original,
WAVs,
Both
};
struct ProjectGroup
{
amuse::IntrusiveAudioGroupData m_data;
amuse::AudioGroupProject m_proj;
amuse::AudioGroupPool m_pool;
amuse::AudioGroupSampleDirectory m_sdir;
explicit ProjectGroup(amuse::IntrusiveAudioGroupData&& data);
};
private:
QDir m_dir;
amuse::NameDB m_songDb;
amuse::NameDB m_sfxDb;
std::map<QString, ProjectGroup> m_groups;
public:
explicit ProjectModel(const QString& path, QObject* parent = Q_NULLPTR);
bool importGroupData(const QString& groupName, amuse::IntrusiveAudioGroupData&& data,
ImportMode mode, QWidget* parent);
bool saveToFile(QWidget* parent);
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;
QString path() const { return m_dir.path(); }
bool canDelete() const;
public slots:
void del();
signals:
void canDeleteChanged(bool canDelete);
}; };

View File

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

14
Editor/SFXGroupEditor.hpp Normal file
View File

@ -0,0 +1,14 @@
#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,7 +1,7 @@
#include "SampleEditor.hpp" #include "SampleEditor.hpp"
SampleEditor::SampleEditor(QWidget* parent) SampleEditor::SampleEditor(QWidget* parent)
: QWidget(parent) : EditorWidget(parent)
{ {
} }

View File

@ -1,9 +1,9 @@
#ifndef AMUSE_SAMPLE_EDITOR_HPP #ifndef AMUSE_SAMPLE_EDITOR_HPP
#define AMUSE_SAMPLE_EDITOR_HPP #define AMUSE_SAMPLE_EDITOR_HPP
#include <QWidget> #include "EditorWidget.hpp"
class SampleEditor : public QWidget class SampleEditor : public EditorWidget
{ {
Q_OBJECT Q_OBJECT
public: public:

View File

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

View File

@ -1,9 +1,9 @@
#ifndef AMUSE_SONG_GROUP_EDITOR_HPP #ifndef AMUSE_SONG_GROUP_EDITOR_HPP
#define AMUSE_SONG_GROUP_EDITOR_HPP #define AMUSE_SONG_GROUP_EDITOR_HPP
#include <QWidget> #include "EditorWidget.hpp"
class SongGroupEditor : public QWidget class SongGroupEditor : public EditorWidget
{ {
Q_OBJECT Q_OBJECT
public: public:

View File

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

View File

@ -1,14 +0,0 @@
#ifndef AMUSE_SOUND_GROUP_EDITOR_HPP
#define AMUSE_SOUND_GROUP_EDITOR_HPP
#include <QWidget>
class SoundGroupEditor : public QWidget
{
Q_OBJECT
public:
explicit SoundGroupEditor(QWidget* parent = Q_NULLPTR);
};
#endif //AMUSE_SOUND_GROUP_EDITOR_HPP

View File

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

View File

@ -1,9 +1,9 @@
#ifndef AMUSE_SOUND_MACRO_EDITOR_HPP #ifndef AMUSE_SOUND_MACRO_EDITOR_HPP
#define AMUSE_SOUND_MACRO_EDITOR_HPP #define AMUSE_SOUND_MACRO_EDITOR_HPP
#include <QWidget> #include "EditorWidget.hpp"
class SoundMacroEditor : public QWidget class SoundMacroEditor : public EditorWidget
{ {
Q_OBJECT Q_OBJECT
public: public:

View File

@ -2,6 +2,9 @@
#include <QApplication> #include <QApplication>
#include <QStyleFactory> #include <QStyleFactory>
#include "MainWindow.hpp" #include "MainWindow.hpp"
#include "boo/IApplication.hpp"
using namespace std::literals;
#ifdef __APPLE__ #ifdef __APPLE__
void MacOSSetDarkAppearance(); void MacOSSetDarkAppearance();
@ -28,6 +31,24 @@ static QIcon MakeAppIcon()
return ret; return ret;
} }
/* This is for adapting the get*Name methods */
class BooInterface : public boo::IApplication
{
std::vector<boo::SystemString> m_args;
void _deletedWindow(boo::IWindow* window) {}
public:
EPlatformType getPlatformType() const { return EPlatformType::Qt; }
int run() { return 0; }
boo::SystemStringView getUniqueName() const { return _S("amuse-gui"sv); }
boo::SystemStringView getFriendlyName() const { return _S("Amuse"sv); }
boo::SystemStringView getProcessName() const { return _S("amuse-gui"sv); }
const std::vector<boo::SystemString>& getArgs() const { return m_args; }
/* Constructors/initializers for sub-objects */
std::shared_ptr<boo::IWindow> newWindow(boo::SystemStringView title) { return {}; }
};
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) #if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
@ -48,6 +69,7 @@ int main(int argc, char* argv[])
darkPalette.setColor(QPalette::ToolTipText, Qt::white); darkPalette.setColor(QPalette::ToolTipText, Qt::white);
darkPalette.setColor(QPalette::Text, Qt::white); darkPalette.setColor(QPalette::Text, Qt::white);
darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(255,255,255,120)); darkPalette.setColor(QPalette::Disabled, QPalette::Text, QColor(255,255,255,120));
darkPalette.setColor(QPalette::Disabled, QPalette::Light, QColor(0,0,0,0));
darkPalette.setColor(QPalette::Button, QColor(53,53,53)); darkPalette.setColor(QPalette::Button, QColor(53,53,53));
darkPalette.setColor(QPalette::Disabled, QPalette::Button, QColor(53,53,53,53)); darkPalette.setColor(QPalette::Disabled, QPalette::Button, QColor(53,53,53,53));
darkPalette.setColor(QPalette::ButtonText, Qt::white); darkPalette.setColor(QPalette::ButtonText, Qt::white);
@ -64,6 +86,9 @@ int main(int argc, char* argv[])
MacOSSetDarkAppearance(); MacOSSetDarkAppearance();
#endif #endif
BooInterface booApp;
boo::APP = &booApp;
MainWindow w; MainWindow w;
w.show(); w.show();
return a.exec(); return a.exec();

View File

@ -10,13 +10,13 @@
xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1024" width="247.54231"
height="1024" height="247.54233"
viewBox="0 0 270.93 270.93333" viewBox="0 0 261.98227 261.98229"
id="svg2" id="svg2"
version="1.1" version="1.1"
inkscape:version="0.92.2 5c3e80d, 2017-08-06" inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="faceGrey.svg" sodipodi:docname="FaceGrey.svg"
inkscape:export-filename="/Users/jacko/Desktop/amuse-logo/face1024.png" inkscape:export-filename="/Users/jacko/Desktop/amuse-logo/face1024.png"
inkscape:export-xdpi="96.000008" inkscape:export-xdpi="96.000008"
inkscape:export-ydpi="96.000008"> inkscape:export-ydpi="96.000008">
@ -27,19 +27,19 @@
<stop <stop
id="stop4353" id="stop4353"
offset="0" offset="0"
style="stop-color:#5f5f5f;stop-opacity:1;" /> style="stop-color:#858585;stop-opacity:1;" />
<stop <stop
style="stop-color:#424242;stop-opacity:1;" style="stop-color:#4f4f4f;stop-opacity:1;"
offset="0.27903292" offset="0.27903292"
id="stop4351" /> id="stop4351" />
<stop <stop
id="stop4349" id="stop4349"
offset="0.56446373" offset="0.56446373"
style="stop-color:#4e4e4e;stop-opacity:1;" /> style="stop-color:#606060;stop-opacity:1;" />
<stop <stop
id="stop4347" id="stop4347"
offset="1" offset="1"
style="stop-color:#212121;stop-opacity:1;" /> style="stop-color:#484848;stop-opacity:1;" />
</linearGradient> </linearGradient>
<linearGradient <linearGradient
id="linearGradient4315"> id="linearGradient4315">
@ -334,23 +334,28 @@
</defs> </defs>
<sodipodi:namedview <sodipodi:namedview
id="base" id="base"
pagecolor="#2a2a2a" pagecolor="#3a3a3a"
bordercolor="#666666" bordercolor="#666666"
borderopacity="1.0" borderopacity="1.0"
inkscape:pageopacity="0" inkscape:pageopacity="0.00392157"
inkscape:pageshadow="2" inkscape:pageshadow="2"
inkscape:zoom="0.63234634" inkscape:zoom="2.5293854"
inkscape:cx="247.50409" inkscape:cx="57.159723"
inkscape:cy="571.18657" inkscape:cy="115.45029"
inkscape:document-units="mm" inkscape:document-units="mm"
inkscape:current-layer="layer3" inkscape:current-layer="layer3"
showgrid="false" showgrid="false"
units="px" units="px"
inkscape:window-width="1680" inkscape:window-width="1555"
inkscape:window-height="1005" inkscape:window-height="1280"
inkscape:window-x="0" inkscape:window-x="1890"
inkscape:window-y="0" inkscape:window-y="286"
inkscape:window-maximized="0" /> inkscape:window-maximized="0"
scale-x="4"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata <metadata
id="metadata7"> id="metadata7">
<rdf:RDF> <rdf:RDF>
@ -367,7 +372,7 @@
inkscape:label="source" inkscape:label="source"
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer1" id="layer1"
transform="translate(0,-26.06665)" transform="translate(-4.3711946,-30.440026)"
style="display:none" style="display:none"
sodipodi:insensitive="true"> sodipodi:insensitive="true">
<text <text
@ -412,7 +417,8 @@
id="layer2" id="layer2"
inkscape:label="path" inkscape:label="path"
style="display:inline;opacity:0.76700003" style="display:inline;opacity:0.76700003"
sodipodi:insensitive="true"> sodipodi:insensitive="true"
transform="translate(-4.3711946,-4.3733763)">
<g <g
id="g4360" id="g4360"
style="fill:url(#linearGradient4375);fill-opacity:1" style="fill:url(#linearGradient4375);fill-opacity:1"
@ -453,7 +459,8 @@
id="layer5" id="layer5"
inkscape:label="path 1" inkscape:label="path 1"
style="display:inline;opacity:0.24734982" style="display:inline;opacity:0.24734982"
sodipodi:insensitive="true"> sodipodi:insensitive="true"
transform="translate(-4.3711946,-4.3733763)">
<path <path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
id="path4439" id="path4439"
@ -466,7 +473,8 @@
inkscape:label="path 2" inkscape:label="path 2"
id="g4512" id="g4512"
inkscape:groupmode="layer" inkscape:groupmode="layer"
sodipodi:insensitive="true"> sodipodi:insensitive="true"
transform="translate(-4.3711946,-4.3733763)">
<g <g
transform="matrix(1.0065636,0,0,1.0065636,-0.88913164,-0.88914604)" transform="matrix(1.0065636,0,0,1.0065636,-0.88913164,-0.88914604)"
style="fill:url(#linearGradient4530);fill-opacity:1" style="fill:url(#linearGradient4530);fill-opacity:1"
@ -507,7 +515,8 @@
inkscape:label="path 3" inkscape:label="path 3"
id="g4538" id="g4538"
inkscape:groupmode="layer" inkscape:groupmode="layer"
sodipodi:insensitive="true"> sodipodi:insensitive="true"
transform="translate(-4.3711946,-4.3733763)">
<g <g
transform="matrix(1.0065636,0,0,1.0065636,-0.88913164,-0.88914604)" transform="matrix(1.0065636,0,0,1.0065636,-0.88913164,-0.88914604)"
style="fill:url(#linearGradient4556);fill-opacity:1" style="fill:url(#linearGradient4556);fill-opacity:1"
@ -547,19 +556,20 @@
inkscape:groupmode="layer" inkscape:groupmode="layer"
id="layer3" id="layer3"
inkscape:label="light" inkscape:label="light"
style="display:inline;opacity:1"> style="display:inline;opacity:1"
transform="translate(-4.3711946,-4.3733763)">
<g <g
id="g4360-5" id="g4360-5"
style="display:inline;fill:url(#linearGradient4332);fill-opacity:1.0" style="display:inline;fill:url(#linearGradient4332);fill-opacity:1"
transform="matrix(1.002648,0,0,1.002648,-1.0727108,-1.0727166)"> transform="matrix(1.002648,0,0,1.002648,-1.0727108,-1.0727166)">
<g <g
id="text4141-2-9" id="text4141-2-9"
aria-label=":D" aria-label=":D"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:125%;font-family:'Fira Code';-inkscape-font-specification:'Fira Code';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient4187);fill-opacity:1.0;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.58333302px;line-height:125%;font-family:'Fira Code';-inkscape-font-specification:'Fira Code';letter-spacing:0px;word-spacing:0px;fill:url(#linearGradient4187);fill-opacity:1;stroke:none;stroke-width:0.26458332px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
transform="rotate(90)"> transform="rotate(90)">
<path <path
inkscape:connector-curvature="0" inkscape:connector-curvature="0"
style="font-size:176.3888855px;letter-spacing:-26.45833206px;fill:url(#linearGradient4184);fill-opacity:1.0;stroke-width:0.99999994px" style="font-size:176.3888855px;letter-spacing:-26.45833206px;fill:url(#linearGradient4184);fill-opacity:1;stroke-width:0.99999994px"
d="M 512,20.529297 C 240.58464,20.529297 20.527344,240.58461 20.527344,512 c 0,271.41536 220.057296,491.4727 491.472656,491.4727 271.4154,0 491.4707,-220.05734 491.4707,-491.4727 C 1003.4707,240.58461 783.4154,20.529297 512,20.529297 Z m 0,2.605469 c 270.00802,0 488.8652,218.857204 488.8652,488.865234 0,270.00802 -218.85718,488.8652 -488.8652,488.8652 C 241.992,1000.8652 23.132813,782.00802 23.132812,512 23.132812,241.99197 241.992,23.134766 512,23.134766 Z m 0,32.326172 C 259.91593,55.460938 55.458984,259.9159 55.458984,512 55.458984,764.08406 259.91593,968.54102 512,968.54102 764.0841,968.54102 968.53906,764.08406 968.53906,512 968.53906,259.9159 764.0841,55.460938 512,55.460938 Z m 0,9.367187 C 759.02055,64.828125 959.17188,264.97946 959.17188,512 959.17188,759.02051 759.02055,959.17383 512,959.17383 264.97949,959.17383 64.826172,759.02051 64.826172,512 64.826172,264.97946 264.97949,64.828125 512,64.828125 Z M 512,96.5625 C 282.67144,96.5625 96.568359,282.67142 96.568359,512 96.568359,741.32854 282.67144,927.43164 512,927.43164 741.32858,927.43164 927.4375,741.32854 927.4375,512 927.4375,282.67142 741.32858,96.5625 512,96.5625 Z m 0,18.47656 C 731.34481,115.03906 908.96094,292.6552 908.96094,512 908.96094,731.34477 731.34481,908.96094 512,908.96094 292.65523,908.96094 115.03906,731.34477 115.03906,512 115.03906,292.6552 292.65523,115.03906 512,115.03906 Z m 0,32.90235 C 311.12038,147.94141 147.93945,311.12036 147.93945,512 147.93945,712.87962 311.12038,876.06055 512,876.06055 712.87966,876.06055 876.05859,712.87962 876.05859,512 876.05859,311.12036 712.87966,147.94141 512,147.94141 Z m 0,31.16601 c 184.03556,0 332.89258,148.85702 332.89258,332.89258 0,184.03552 -148.85702,332.89453 -332.89258,332.89453 -184.03553,0 -332.89453,-148.85901 -332.89453,-332.89453 0,-184.03556 148.859,-332.89258 332.89453,-332.89258 z m -129.66602,71.68946 c -15.55555,0 -28.44574,5.11176 -38.66796,15.33398 -10.22223,10.22222 -15.33204,22.88889 -15.33204,38 0,15.55555 5.10981,28.44379 15.33204,38.66602 10.22222,10.22222 23.11241,15.33398 38.66796,15.33398 15.11111,0 27.77777,-5.11176 38,-15.33398 10.66666,-10.22223 16,-23.11047 16,-38.66602 0,-15.11111 -5.33334,-27.77778 -16,-38 -10.66668,-10.22222 -23.33334,-15.33398 -38,-15.33398 z m 240.66602,0 c -15.11111,0 -27.99933,5.11176 -38.66602,15.33398 -10.66665,10.22222 -16,22.88889 -16,38 0,15.55555 5.33335,28.44379 16,38.66602 10.66669,10.22222 23.55491,15.33398 38.66602,15.33398 14.66666,0 27.11175,-5.11176 37.33398,-15.33398 10.66666,-10.22223 16,-23.11047 16,-38.66602 0,-15.11111 -5.33334,-27.77778 -16,-38 C 650.11175,255.90864 637.66666,250.79688 623,250.79688 Z M 282.33398,459.86914 v 105.33398 c 0,58.66666 16.88824,108 50.66602,148 33.77778,40.00001 94.22288,60 181.33398,60 86.66666,0 146.22157,-19.77843 178.66602,-59.33398 32.44445,-39.55555 48.66602,-91.55555 48.66602,-156 v -98 z m 46.66602,58 h 365.33398 v 54 c 0,25.77779 -4.44509,48.66797 -13.33398,68.66797 -8.44445,20 -25.99936,36.66668 -52.66602,50 -26.22221,13.77779 -64.22221,20.66601 -114,20.66601 -68.88889,0 -117.11242,-13.33331 -144.66796,-40 C 342.5549,644.98091 329,612.09266 329,572.53711 Z" d="M 512,20.529297 C 240.58464,20.529297 20.527344,240.58461 20.527344,512 c 0,271.41536 220.057296,491.4727 491.472656,491.4727 271.4154,0 491.4707,-220.05734 491.4707,-491.4727 C 1003.4707,240.58461 783.4154,20.529297 512,20.529297 Z m 0,2.605469 c 270.00802,0 488.8652,218.857204 488.8652,488.865234 0,270.00802 -218.85718,488.8652 -488.8652,488.8652 C 241.992,1000.8652 23.132813,782.00802 23.132812,512 23.132812,241.99197 241.992,23.134766 512,23.134766 Z m 0,32.326172 C 259.91593,55.460938 55.458984,259.9159 55.458984,512 55.458984,764.08406 259.91593,968.54102 512,968.54102 764.0841,968.54102 968.53906,764.08406 968.53906,512 968.53906,259.9159 764.0841,55.460938 512,55.460938 Z m 0,9.367187 C 759.02055,64.828125 959.17188,264.97946 959.17188,512 959.17188,759.02051 759.02055,959.17383 512,959.17383 264.97949,959.17383 64.826172,759.02051 64.826172,512 64.826172,264.97946 264.97949,64.828125 512,64.828125 Z M 512,96.5625 C 282.67144,96.5625 96.568359,282.67142 96.568359,512 96.568359,741.32854 282.67144,927.43164 512,927.43164 741.32858,927.43164 927.4375,741.32854 927.4375,512 927.4375,282.67142 741.32858,96.5625 512,96.5625 Z m 0,18.47656 C 731.34481,115.03906 908.96094,292.6552 908.96094,512 908.96094,731.34477 731.34481,908.96094 512,908.96094 292.65523,908.96094 115.03906,731.34477 115.03906,512 115.03906,292.6552 292.65523,115.03906 512,115.03906 Z m 0,32.90235 C 311.12038,147.94141 147.93945,311.12036 147.93945,512 147.93945,712.87962 311.12038,876.06055 512,876.06055 712.87966,876.06055 876.05859,712.87962 876.05859,512 876.05859,311.12036 712.87966,147.94141 512,147.94141 Z m 0,31.16601 c 184.03556,0 332.89258,148.85702 332.89258,332.89258 0,184.03552 -148.85702,332.89453 -332.89258,332.89453 -184.03553,0 -332.89453,-148.85901 -332.89453,-332.89453 0,-184.03556 148.859,-332.89258 332.89453,-332.89258 z m -129.66602,71.68946 c -15.55555,0 -28.44574,5.11176 -38.66796,15.33398 -10.22223,10.22222 -15.33204,22.88889 -15.33204,38 0,15.55555 5.10981,28.44379 15.33204,38.66602 10.22222,10.22222 23.11241,15.33398 38.66796,15.33398 15.11111,0 27.77777,-5.11176 38,-15.33398 10.66666,-10.22223 16,-23.11047 16,-38.66602 0,-15.11111 -5.33334,-27.77778 -16,-38 -10.66668,-10.22222 -23.33334,-15.33398 -38,-15.33398 z m 240.66602,0 c -15.11111,0 -27.99933,5.11176 -38.66602,15.33398 -10.66665,10.22222 -16,22.88889 -16,38 0,15.55555 5.33335,28.44379 16,38.66602 10.66669,10.22222 23.55491,15.33398 38.66602,15.33398 14.66666,0 27.11175,-5.11176 37.33398,-15.33398 10.66666,-10.22223 16,-23.11047 16,-38.66602 0,-15.11111 -5.33334,-27.77778 -16,-38 C 650.11175,255.90864 637.66666,250.79688 623,250.79688 Z M 282.33398,459.86914 v 105.33398 c 0,58.66666 16.88824,108 50.66602,148 33.77778,40.00001 94.22288,60 181.33398,60 86.66666,0 146.22157,-19.77843 178.66602,-59.33398 32.44445,-39.55555 48.66602,-91.55555 48.66602,-156 v -98 z m 46.66602,58 h 365.33398 v 54 c 0,25.77779 -4.44509,48.66797 -13.33398,68.66797 -8.44445,20 -25.99936,36.66668 -52.66602,50 -26.22221,13.77779 -64.22221,20.66601 -114,20.66601 -68.88889,0 -117.11242,-13.33331 -144.66796,-40 C 342.5549,644.98091 329,612.09266 329,572.53711 Z"
transform="matrix(0,-0.26458333,0.26458333,0,0,0.001665)" transform="matrix(0,-0.26458333,0.26458333,0,0,0.001665)"
id="path4247-2" /> id="path4247-2" />

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -14,4 +14,7 @@
<file>IconSoundGroup.svg</file> <file>IconSoundGroup.svg</file>
<file>IconSoundMacro.svg</file> <file>IconSoundMacro.svg</file>
</qresource> </qresource>
<qresource prefix="/bg">
<file>FaceGrey.svg</file>
</qresource>
</RCC> </RCC>

File diff suppressed because it is too large Load Diff

View File

@ -12,58 +12,167 @@ namespace amuse
{ {
class AudioGroupData; class AudioGroupData;
/** Common index members of SongGroups and SFXGroups */ enum class GroupType : atUint16
struct AudioGroupIndex
{ {
const uint16_t* m_soundMacroIndex; Song,
const uint16_t* m_tablesIndex; SFX
const uint16_t* m_keymapsIndex;
const uint16_t* m_layersIndex;
}; };
/** Header at top of project file */
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
GroupHeader : BigDNA
{
AT_DECL_DNA
Value<atUint32, DNAEn> groupEndOff;
Value<atUint16, DNAEn> groupId;
Value<GroupType, DNAEn> type;
Value<atUint32, DNAEn> soundMacroIdsOff;
Value<atUint32, DNAEn> samplIdsOff;
Value<atUint32, DNAEn> tableIdsOff;
Value<atUint32, DNAEn> keymapIdsOff;
Value<atUint32, DNAEn> layerIdsOff;
Value<atUint32, DNAEn> pageTableOff;
Value<atUint32, DNAEn> drumTableOff;
Value<atUint32, DNAEn> midiSetupsOff;
};
/** Common index members of SongGroups and SFXGroups */
struct AudioGroupIndex {};
/** Root index of SongGroup */ /** Root index of SongGroup */
struct SongGroupIndex : AudioGroupIndex struct SongGroupIndex : AudioGroupIndex
{ {
/** Maps GM program numbers to sound entities */ /** Maps GM program numbers to sound entities */
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
PageEntryDNA : BigDNA
{
AT_DECL_DNA_YAML
ObjectIdDNA<DNAEn> objId;
Value<atUint8> priority;
Value<atUint8> maxVoices;
Value<atUint8> programNo;
Seek<1, athena::Current> pad;
};
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
MusyX1PageEntryDNA : BigDNA
{
AT_DECL_DNA_YAML
ObjectIdDNA<DNAEn> objId;
Value<atUint8> priority;
Value<atUint8> maxVoices;
Value<atUint8> unk;
Value<atUint8> programNo;
Seek<2, athena::Current> pad;
};
struct PageEntry struct PageEntry
{ {
ObjectId objId; ObjectId objId;
uint8_t priority; atUint8 priority;
uint8_t maxVoices; atUint8 maxVoices;
uint8_t programNo;
uint8_t pad; PageEntry() = default;
template <athena::Endian DNAE>
PageEntry(const PageEntryDNA<DNAE>& in)
: objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices) {}
template <athena::Endian DNAE>
PageEntry(const MusyX1PageEntryDNA<DNAE>& in)
: objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices) {}
template <athena::Endian DNAEn>
PageEntryDNA<DNAEn> toDNA(uint8_t programNo) const
{
PageEntryDNA<DNAEn> ret;
ret.objId.id = objId;
ret.priority = priority;
ret.maxVoices = maxVoices;
ret.programNo = programNo;
return ret;
}
}; };
std::unordered_map<uint8_t, const PageEntry*> m_normPages; std::unordered_map<uint8_t, PageEntry> m_normPages;
std::unordered_map<uint8_t, const PageEntry*> m_drumPages; std::unordered_map<uint8_t, PageEntry> m_drumPages;
/** Maps SongID to 16 MIDI channel numbers to GM program numbers and settings */ /** Maps SongID to 16 MIDI channel numbers to GM program numbers and settings */
struct MIDISetup struct MusyX1MIDISetup : BigDNA
{ {
uint8_t programNo; AT_DECL_DNA_YAML
uint8_t volume; Value<atUint8> programNo;
uint8_t panning; Value<atUint8> volume;
uint8_t reverb; Value<atUint8> panning;
uint8_t chorus; Value<atUint8> reverb;
Value<atUint8> chorus;
Seek<3, athena::Current> pad;
}; };
std::unordered_map<int, const std::array<MIDISetup, 16>*> m_midiSetups; struct MIDISetup : BigDNA
{
AT_DECL_DNA_YAML
Value<atUint8> programNo;
Value<atUint8> volume;
Value<atUint8> panning;
Value<atUint8> reverb;
Value<atUint8> chorus;
MIDISetup() = default;
MIDISetup(const MusyX1MIDISetup& setup)
: programNo(setup.programNo), volume(setup.volume), panning(setup.panning),
reverb(setup.reverb), chorus(setup.chorus) {}
};
std::unordered_map<SongId, std::array<MIDISetup, 16>> m_midiSetups;
}; };
/** Root index of SFXGroup */ /** Root index of SFXGroup */
struct SFXGroupIndex : AudioGroupIndex struct SFXGroupIndex : AudioGroupIndex
{ {
/** Maps game-side SFX define IDs to sound entities */ /** Maps game-side SFX define IDs to sound entities */
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
SFXEntryDNA : BigDNA
{
AT_DECL_DNA_YAML
SFXIdDNA<DNAEn> defineId;
ObjectIdDNA<DNAEn> objId;
Value<atUint8> priority;
Value<atUint8> maxVoices;
Value<atUint8> defVel;
Value<atUint8> panning;
Value<atUint8> defKey;
Seek<1, athena::Current> pad;
};
struct SFXEntry struct SFXEntry
{ {
uint16_t defineId;
ObjectId objId; ObjectId objId;
uint8_t priority; atUint8 priority;
uint8_t maxVoices; atUint8 maxVoices;
uint8_t defVel; atUint8 defVel;
uint8_t panning; atUint8 panning;
uint8_t defKey; atUint8 defKey;
uint8_t pad;
SFXEntry() = default;
template <athena::Endian DNAE>
SFXEntry(const SFXEntryDNA<DNAE>& in)
: objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices),
defVel(in.defVel), panning(in.panning), defKey(in.defKey) {}
template <athena::Endian DNAEn>
SFXEntryDNA<DNAEn> toDNA(SFXId defineId) const
{
SFXEntryDNA<DNAEn> ret;
ret.defineId.id = defineId;
ret.objId.id = objId;
ret.priority = priority;
ret.maxVoices = maxVoices;
ret.defVel = defVel;
ret.panning = panning;
ret.defKey = defKey;
return ret;
}
}; };
std::unordered_map<uint16_t, const SFXEntry*> m_sfxEntries; std::unordered_map<SFXId, SFXEntry> m_sfxEntries;
}; };
/** Collection of SongGroup and SFXGroup indexes */ /** Collection of SongGroup and SFXGroup indexes */
@ -72,17 +181,11 @@ class AudioGroupProject
std::unordered_map<int, SongGroupIndex> m_songGroups; std::unordered_map<int, SongGroupIndex> m_songGroups;
std::unordered_map<int, SFXGroupIndex> m_sfxGroups; std::unordered_map<int, SFXGroupIndex> m_sfxGroups;
/* MusyX 1.0 structures converted to MusyX 2.0 structures for pointer-compatible access */ AudioGroupProject() = default;
std::unique_ptr<SongGroupIndex::PageEntry[]> m_convNormalPages; AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag);
std::unique_ptr<SongGroupIndex::PageEntry[]> m_convDrumPages; template <athena::Endian DNAE>
std::unique_ptr<std::array<SongGroupIndex::MIDISetup, 16>[]> m_convMidiSetups; static AudioGroupProject _AudioGroupProject(athena::io::IStreamReader& r, bool absOffs);
void _allocateConvBuffers(const unsigned char* data, N64DataTag);
void _allocateConvBuffers(const unsigned char* data, PCDataTag);
public: public:
AudioGroupProject(const unsigned char* data, GCNDataTag);
AudioGroupProject(const unsigned char* data, bool absOffs, N64DataTag);
AudioGroupProject(const unsigned char* data, bool absOffs, PCDataTag);
static AudioGroupProject CreateAudioGroupProject(const AudioGroupData& data); static AudioGroupProject CreateAudioGroupProject(const AudioGroupData& data);
const SongGroupIndex* getSongGroupIndex(int groupId) const; const SongGroupIndex* getSongGroupIndex(int groupId) const;

View File

@ -7,6 +7,7 @@
namespace amuse namespace amuse
{ {
class AudioGroupData;
/** Indexes individual samples in SAMP chunk */ /** Indexes individual samples in SAMP chunk */
class AudioGroupSampleDirectory class AudioGroupSampleDirectory
@ -52,6 +53,7 @@ public:
AudioGroupSampleDirectory(const unsigned char* data, GCNDataTag); AudioGroupSampleDirectory(const unsigned char* data, GCNDataTag);
AudioGroupSampleDirectory(const unsigned char* data, const unsigned char* sampData, bool absOffs, N64DataTag); AudioGroupSampleDirectory(const unsigned char* data, const unsigned char* sampData, bool absOffs, N64DataTag);
AudioGroupSampleDirectory(const unsigned char* data, bool absOffs, PCDataTag); AudioGroupSampleDirectory(const unsigned char* data, bool absOffs, PCDataTag);
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(const AudioGroupData& data);
const std::unordered_map<uint16_t, std::pair<Entry, ADPCMParms>>& sampleEntries() const { return m_entries; } const std::unordered_map<uint16_t, std::pair<Entry, ADPCMParms>>& sampleEntries() const { return m_entries; }
}; };

View File

@ -9,6 +9,7 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <cstring> #include <cstring>
#include "athena/DNA.hpp"
#ifndef _WIN32 #ifndef _WIN32
#include <strings.h> #include <strings.h>
@ -28,6 +29,95 @@ constexpr float NativeSampleRate = 32000.0f;
namespace amuse namespace amuse
{ {
struct NameDB;
using BigDNA = athena::io::DNA<athena::Big>;
using LittleDNA = athena::io::DNA<athena::Little>;
using BigDNAV = athena::io::DNAVYaml<athena::Big>;
using LittleDNAV = athena::io::DNAVYaml<athena::Little>;
/** Common ID structure statically tagging
* SoundMacros, Tables, Keymaps, Layers */
struct ObjectId
{
uint16_t id = 0xffff;
operator uint16_t() const { return id; }
ObjectId() = default;
ObjectId(uint16_t idIn) : id(idIn) {}
ObjectId& operator=(uint16_t idIn) { id = idIn; return *this; }
static thread_local NameDB* CurNameDB;
};
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
ObjectIdDNA : BigDNA
{
AT_DECL_EXPLICIT_DNA_YAML
void _read(athena::io::YAMLDocReader& r);
void _write(athena::io::YAMLDocWriter& w);
ObjectId id;
};
struct SampleId : ObjectId
{
using ObjectId::ObjectId;
SampleId(const ObjectId& id) : ObjectId(id) {}
static thread_local NameDB* CurNameDB;
};
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
SampleIdDNA : BigDNA
{
AT_DECL_EXPLICIT_DNA_YAML
void _read(athena::io::YAMLDocReader& r);
void _write(athena::io::YAMLDocWriter& w);
SampleId id;
};
struct SongId : ObjectId
{
using ObjectId::ObjectId;
SongId(const ObjectId& id) : ObjectId(id) {}
static thread_local NameDB* CurNameDB;
};
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
SongIdDNA : BigDNA
{
AT_DECL_EXPLICIT_DNA_YAML
void _read(athena::io::YAMLDocReader& r);
void _write(athena::io::YAMLDocWriter& w);
SongId id;
};
struct SFXId : ObjectId
{
using ObjectId::ObjectId;
SFXId(const ObjectId& id) : ObjectId(id) {}
static thread_local NameDB* CurNameDB;
};
template <athena::Endian DNAEn>
struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little)
SFXIdDNA : BigDNA
{
AT_DECL_EXPLICIT_DNA_YAML
void _read(athena::io::YAMLDocReader& r);
void _write(athena::io::YAMLDocWriter& w);
SFXId id;
};
struct LittleUInt24 : LittleDNA
{
AT_DECL_EXPLICIT_DNA_YAML
atUint32 val;
operator uint32_t() const { return val; }
LittleUInt24() = default;
LittleUInt24(uint32_t valIn) : val(valIn) {}
LittleUInt24& operator=(uint32_t valIn) { val = valIn; return *this; }
};
#ifndef PRISize #ifndef PRISize
#ifdef _MSC_VER #ifdef _MSC_VER
@ -334,4 +424,51 @@ struct PCDataTag
}; };
} }
namespace std
{
template<>
struct hash<amuse::ObjectId>
{
size_t operator()(const amuse::ObjectId& val) const noexcept { return val.id; }
};
template<>
struct hash<amuse::SampleId>
{
size_t operator()(const amuse::SampleId& val) const noexcept { return val.id; }
};
template<>
struct hash<amuse::SongId>
{
size_t operator()(const amuse::SongId& val) const noexcept { return val.id; }
};
template<>
struct hash<amuse::SFXId>
{
size_t operator()(const amuse::SFXId& val) const noexcept { return val.id; }
};
}
namespace amuse
{
struct NameDB
{
enum class Type
{
SoundMacro = 0,
Table = 1,
Keymap = 4,
Layer = 8
};
std::unordered_map<std::string, ObjectId> m_stringToId;
std::unordered_map<ObjectId, std::string> m_idToString;
ObjectId generateId(Type tp);
static std::string generateName(ObjectId id);
std::string_view registerPair(std::string_view str, ObjectId id);
std::string_view resolveNameFromId(ObjectId id) const;
ObjectId resolveIdFromName(std::string_view str) const;
};
}
#endif // __AMUSE_COMMON_HPP__ #endif // __AMUSE_COMMON_HPP__

View File

@ -4,23 +4,20 @@
#include <cstdint> #include <cstdint>
#include <functional> #include <functional>
#include <cassert> #include <cassert>
#include "Common.hpp"
namespace amuse namespace amuse
{ {
class Engine; class Engine;
class AudioGroup; class AudioGroup;
/** Common ID structure statically tagging
* SoundMacros, Tables, Keymaps, Layers */
using ObjectId = uint16_t;
/** Common 'engine child' class */ /** Common 'engine child' class */
class Entity class Entity
{ {
/* Only the Engine will manage Entity lifetimes, /* Only the Engine will manage Entity lifetimes,
* but shared_ptrs are issued to the client so it can safely track state */ * but shared_ptrs are issued to the client so it can safely track state */
friend class Engine; friend class Engine;
friend class SoundMacroState; friend struct SoundMacroState;
protected: protected:
bool m_destroyed = false; bool m_destroyed = false;
@ -32,7 +29,7 @@ protected:
Engine& m_engine; Engine& m_engine;
const AudioGroup& m_audioGroup; const AudioGroup& m_audioGroup;
int m_groupId; int m_groupId;
ObjectId m_objectId = 0xffff; /* if applicable */ ObjectId m_objectId; /* if applicable */
public: public:
Entity(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid = ObjectId()) Entity(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid = ObjectId())
: m_engine(engine), m_audioGroup(group), m_groupId(groupId), m_objectId(oid) : m_engine(engine), m_audioGroup(group), m_groupId(groupId), m_objectId(oid)
@ -50,9 +47,6 @@ public:
ObjectId getObjectId() const { return m_objectId; } ObjectId getObjectId() const { return m_objectId; }
}; };
/** Curves for mapping velocity to volume and other functional mappings
* (defined here for visibility)*/
using Curve = uint8_t[128];
} }
#endif // __AMUSE_ENTITY_HPP__ #endif // __AMUSE_ENTITY_HPP__

View File

@ -6,6 +6,7 @@
#include <list> #include <list>
#include "Entity.hpp" #include "Entity.hpp"
#include "Common.hpp" #include "Common.hpp"
#include "AudioGroupPool.hpp"
/* Squelch Win32 macro pollution >.< */ /* Squelch Win32 macro pollution >.< */
#undef SendMessage #undef SendMessage
@ -16,123 +17,13 @@ namespace amuse
class Voice; class Voice;
/** Real-time state of SoundMacro execution */ /** Real-time state of SoundMacro execution */
class SoundMacroState struct SoundMacroState
{ {
friend class Voice;
friend class Envelope;
/** SoundMacro header */
struct Header
{
uint32_t m_size;
ObjectId m_macroId;
uint8_t m_volume;
uint8_t m_pan;
void swapBig();
} m_header;
/** SoundMacro command operations */
enum class Op : uint8_t
{
End,
Stop,
SplitKey,
SplitVel,
WaitTicks,
Loop,
Goto,
WaitMs,
PlayMacro,
SendKeyOff,
SplitMod,
PianoPan,
SetAdsr,
ScaleVolume,
Panning,
Envelope,
StartSample,
StopSample,
KeyOff,
SplitRnd,
FadeIn,
Spanning,
SetAdsrCtrl,
RndNote,
AddNote,
SetNote,
LastNote,
Portamento,
Vibrato,
PitchSweep1,
PitchSweep2,
SetPitch,
SetPitchAdsr,
ScaleVolumeDLS,
Mod2Vibrange,
SetupTremolo,
Return,
GoSub,
TrapEvent = 0x28,
UntrapEvent,
SendMessage,
GetMessage,
GetVid,
AddAgeCount = 0x30, /* unimplemented */
SetAgeCount, /* unimplemented */
SendFlag, /* unimplemented */
PitchWheelR,
SetPriority = 0x36, /* unimplemented */
AddPriority, /* unimplemented */
AgeCntSpeed, /* unimplemented */
AgeCntVel, /* unimplemented */
VolSelect = 0x40,
PanSelect,
PitchWheelSelect,
ModWheelSelect,
PedalSelect,
PortamentoSelect,
ReverbSelect, /* serves as PostASelect */
SpanSelect,
DopplerSelect,
TremoloSelect,
PreASelect,
PreBSelect,
PostBSelect,
AuxAFXSelect, /* unimplemented */
AuxBFXSelect, /* unimplemented */
SetupLFO = 0x50,
ModeSelect = 0x58,
SetKeygroup,
SRCmodeSelect, /* unimplemented */
AddVars = 0x60,
SubVars,
MulVars,
DivVars,
AddIVars,
IfEqual = 0x70,
IfLess,
};
/** SoundMacro command structure */
struct Command
{
Op m_op;
char m_data[7];
void swapBig();
};
/** 'program counter' stack for the active SoundMacro */ /** 'program counter' stack for the active SoundMacro */
std::vector<std::pair<const unsigned char*, int>> m_pc; std::vector<std::tuple<ObjectId, const SoundMacro*, int>> m_pc;
static int _assertPC(int pc, uint32_t size);
static int _assertPC(int pc, uint32_t size, bool swapSize)
{
return _assertPC(pc, swapSize ? SBig(size) : size);
}
void _setPC(int pc) void _setPC(int pc)
{ {
m_pc.back().second = _assertPC(pc, m_header.m_size); std::get<2>(m_pc.back()) = std::get<1>(m_pc.back())->assertPC(pc);
} }
double m_ticksPerSec; /**< ratio for resolving ticks in commands that use them */ double m_ticksPerSec; /**< ratio for resolving ticks in commands that use them */
@ -233,9 +124,9 @@ class SoundMacroState
public: public:
/** initialize state for SoundMacro data at `ptr` */ /** initialize state for SoundMacro data at `ptr` */
void initialize(const unsigned char* ptr, int step, bool swapData); void initialize(ObjectId id, const SoundMacro* macro, int step);
void initialize(const unsigned char* ptr, int step, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, void initialize(ObjectId id, const SoundMacro* macro, int step, double ticksPerSec,
uint8_t midiMod, bool swapData); uint8_t midiKey, uint8_t midiVel, uint8_t midiMod);
/** advances `dt` seconds worth of commands in the SoundMacro /** advances `dt` seconds worth of commands in the SoundMacro
* @return `true` if END reached * @return `true` if END reached

View File

@ -31,9 +31,20 @@ class Voice : public Entity
{ {
friend class Engine; friend class Engine;
friend class Sequencer; friend class Sequencer;
friend class SoundMacroState; friend struct SoundMacroState;
friend class Envelope; friend class Envelope;
friend class Emitter; friend class Emitter;
friend class SoundMacro::CmdScaleVolume;
friend class SoundMacro::CmdKeyOff;
friend class SoundMacro::CmdScaleVolumeDLS;
friend class SoundMacro::CmdReturn;
friend class SoundMacro::CmdGoSub;
friend class SoundMacro::CmdTrapEvent;
friend class SoundMacro::CmdUntrapEvent;
friend class SoundMacro::CmdGetMessage;
void _setObjectId(ObjectId id) { m_objectId = id; }
int m_vid; /**< VoiceID of this voice instance */ int m_vid; /**< VoiceID of this voice instance */
bool m_emitter; /**< Voice is part of an Emitter */ bool m_emitter; /**< Voice is part of an Emitter */
std::shared_ptr<Studio> m_studio; /**< Studio this voice outputs to */ std::shared_ptr<Studio> m_studio; /**< Studio this voice outputs to */
@ -160,12 +171,12 @@ class Voice : public Entity
std::list<std::shared_ptr<Voice>>::iterator _allocateVoice(double sampleRate, bool dynamicPitch); std::list<std::shared_ptr<Voice>>::iterator _allocateVoice(double sampleRate, bool dynamicPitch);
std::list<std::shared_ptr<Voice>>::iterator _destroyVoice(std::list<std::shared_ptr<Voice>>::iterator it); std::list<std::shared_ptr<Voice>>::iterator _destroyVoice(std::list<std::shared_ptr<Voice>>::iterator it);
bool _loadSoundMacro(const unsigned char* macroData, int macroStep, double ticksPerSec, uint8_t midiKey, bool _loadSoundMacro(ObjectId id, const SoundMacro* macroData, int macroStep, double ticksPerSec,
uint8_t midiVel, uint8_t midiMod, bool pushPc = false); uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
bool _loadKeymap(const Keymap* keymap, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, bool _loadKeymap(ObjectId id, const Keymap* keymap, int macroStep, double ticksPerSec, uint8_t midiKey,
uint8_t midiMod, bool pushPc = false);
bool _loadLayer(const std::vector<const LayerMapping*>& layer, int macroStep, double ticksPerSec, uint8_t midiKey,
uint8_t midiVel, uint8_t midiMod, bool pushPc = false); uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
bool _loadLayer(ObjectId id, const std::vector<LayerMapping>& layer, int macroStep, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
std::shared_ptr<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, std::shared_ptr<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey,
uint8_t midiVel, uint8_t midiMod, bool pushPc = false); uint8_t midiVel, uint8_t midiMod, bool pushPc = false);

View File

@ -5,8 +5,8 @@ namespace amuse
{ {
AudioGroup::AudioGroup(const AudioGroupData& data, GCNDataTag) AudioGroup::AudioGroup(const AudioGroupData& data, GCNDataTag)
: m_proj(data.getProj(), GCNDataTag{}) : m_proj(AudioGroupProject::CreateAudioGroupProject(data))
, m_pool(data.getPool()) , m_pool(AudioGroupPool::CreateAudioGroupPool(data))
, m_sdir(data.getSdir(), GCNDataTag{}) , m_sdir(data.getSdir(), GCNDataTag{})
, m_samp(data.getSamp()) , m_samp(data.getSamp())
, m_fmt(DataFormat::GCN) , m_fmt(DataFormat::GCN)
@ -14,8 +14,8 @@ AudioGroup::AudioGroup(const AudioGroupData& data, GCNDataTag)
} }
AudioGroup::AudioGroup(const AudioGroupData& data, bool absOffs, N64DataTag) AudioGroup::AudioGroup(const AudioGroupData& data, bool absOffs, N64DataTag)
: m_proj(data.getProj(), absOffs, N64DataTag{}) : m_proj(AudioGroupProject::CreateAudioGroupProject(data))
, m_pool(data.getPool()) , m_pool(AudioGroupPool::CreateAudioGroupPool(data))
, m_sdir(data.getSdir(), data.getSamp(), absOffs, N64DataTag{}) , m_sdir(data.getSdir(), data.getSamp(), absOffs, N64DataTag{})
, m_samp(data.getSamp()) , m_samp(data.getSamp())
, m_fmt(DataFormat::N64) , m_fmt(DataFormat::N64)
@ -23,8 +23,8 @@ AudioGroup::AudioGroup(const AudioGroupData& data, bool absOffs, N64DataTag)
} }
AudioGroup::AudioGroup(const AudioGroupData& data, bool absOffs, PCDataTag) AudioGroup::AudioGroup(const AudioGroupData& data, bool absOffs, PCDataTag)
: m_proj(data.getProj(), absOffs, PCDataTag{}) : m_proj(AudioGroupProject::CreateAudioGroupProject(data))
, m_pool(data.getPool(), PCDataTag{}) , m_pool(AudioGroupPool::CreateAudioGroupPool(data))
, m_sdir(data.getSdir(), absOffs, PCDataTag{}) , m_sdir(data.getSdir(), absOffs, PCDataTag{})
, m_samp(data.getSamp()) , m_samp(data.getSamp())
, m_fmt(DataFormat::PC) , m_fmt(DataFormat::PC)

View File

@ -1,152 +1,326 @@
#include "amuse/AudioGroupPool.hpp" #include "amuse/AudioGroupPool.hpp"
#include "amuse/Common.hpp" #include "amuse/Common.hpp"
#include "amuse/Entity.hpp" #include "amuse/Entity.hpp"
#include "amuse/AudioGroupData.hpp"
#include "athena/MemoryReader.hpp"
#include "logvisor/logvisor.hpp"
namespace amuse namespace amuse
{ {
static logvisor::Module Log("amuse::AudioGroupPool");
struct Header static bool AtEnd(athena::io::IStreamReader& r)
{ {
uint32_t soundMacrosOffset; uint32_t v = r.readUint32Big();
uint32_t tablesOffset; r.seek(-4, athena::Current);
uint32_t keymapsOffset; return v == 0xffffffff;
uint32_t layersOffset; }
void swapBig()
{
soundMacrosOffset = SBig(soundMacrosOffset);
tablesOffset = SBig(tablesOffset);
keymapsOffset = SBig(keymapsOffset);
layersOffset = SBig(layersOffset);
}
};
AudioGroupPool::AudioGroupPool(const unsigned char* data) template <athena::Endian DNAE>
AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r)
{ {
Header head = *reinterpret_cast<const Header*>(data); AudioGroupPool ret;
head.swapBig();
PoolHeader<DNAE> head;
head.read(r);
if (head.soundMacrosOffset) if (head.soundMacrosOffset)
{ {
const unsigned char* cur = data + head.soundMacrosOffset; r.seek(head.soundMacrosOffset, athena::Begin);
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff) while (!AtEnd(r))
{ {
uint32_t size = SBig(*reinterpret_cast<const uint32_t*>(cur)); ObjectHeader<DNAE> objHead;
ObjectId id = SBig(*reinterpret_cast<const ObjectId*>(cur + 4)); atInt64 startPos = r.position();
m_soundMacros[id] = cur; objHead.read(r);
cur += size; SoundMacro& macro = ret.m_soundMacros[objHead.objectId.id];
macro.readCmds<DNAE>(r, objHead.size - 8);
r.seek(startPos + objHead.size, athena::Begin);
} }
} }
if (head.tablesOffset) if (head.tablesOffset)
{ {
const unsigned char* cur = data + head.tablesOffset; r.seek(head.tablesOffset, athena::Begin);
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff) while (!AtEnd(r))
{ {
uint32_t size = SBig(*reinterpret_cast<const uint32_t*>(cur)); ObjectHeader<DNAE> objHead;
ObjectId id = SBig(*reinterpret_cast<const ObjectId*>(cur + 4)); atInt64 startPos = r.position();
m_tables[id] = cur + 8; objHead.read(r);
cur += size; auto& ptr = ret.m_tables[objHead.objectId.id];
switch (objHead.size)
{
case 8:
ptr = std::make_unique<ADSR>();
static_cast<ADSR&>(*ptr).read(r);
break;
case 0x14:
ptr = std::make_unique<ADSRDLS>();
static_cast<ADSRDLS&>(*ptr).read(r);
break;
default:
ptr = std::make_unique<Curve>();
static_cast<Curve&>(*ptr).data.resize(objHead.size - 8);
r.readUBytesToBuf(&static_cast<Curve&>(*ptr).data[0], objHead.size - 8);
break;
}
r.seek(startPos + objHead.size, athena::Begin);
} }
} }
if (head.keymapsOffset) if (head.keymapsOffset)
{ {
const unsigned char* cur = data + head.keymapsOffset; r.seek(head.keymapsOffset, athena::Begin);
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff) while (!AtEnd(r))
{ {
uint32_t size = SBig(*reinterpret_cast<const uint32_t*>(cur)); ObjectHeader<DNAE> objHead;
ObjectId id = SBig(*reinterpret_cast<const ObjectId*>(cur + 4)); atInt64 startPos = r.position();
m_keymaps[id] = reinterpret_cast<const Keymap*>(cur + 8); objHead.read(r);
cur += size; Keymap& km = ret.m_keymaps[objHead.objectId.id];
km.read(r);
r.seek(startPos + objHead.size, athena::Begin);
} }
} }
if (head.layersOffset) if (head.layersOffset)
{ {
const unsigned char* cur = data + head.layersOffset; r.seek(head.layersOffset, athena::Begin);
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff) while (!AtEnd(r))
{ {
uint32_t size = SBig(*reinterpret_cast<const uint32_t*>(cur)); ObjectHeader<DNAE> objHead;
ObjectId id = SBig(*reinterpret_cast<const ObjectId*>(cur + 4)); atInt64 startPos = r.position();
std::vector<const LayerMapping*>& mappingsOut = m_layers[id]; objHead.read(r);
std::vector<LayerMapping>& lm = ret.m_layers[objHead.objectId.id];
uint32_t count = SBig(*reinterpret_cast<const uint32_t*>(cur + 8)); uint32_t count;
mappingsOut.reserve(count); athena::io::Read<athena::io::PropType::None>::Do<decltype(count), DNAE>({}, count, r);
const unsigned char* subcur = cur + 12; lm.reserve(count);
for (uint32_t i = 0; i < count; ++i) for (uint32_t i = 0; i < count; ++i)
mappingsOut.push_back(reinterpret_cast<const LayerMapping*>(subcur + i * 12)); {
lm.emplace_back();
lm.back().read(r);
}
r.seek(startPos + objHead.size, athena::Begin);
}
}
cur += size; return ret;
}
}
} }
template AudioGroupPool AudioGroupPool::_AudioGroupPool<athena::Big>(athena::io::IStreamReader& r);
template AudioGroupPool AudioGroupPool::_AudioGroupPool<athena::Little>(athena::io::IStreamReader& r);
AudioGroupPool::AudioGroupPool(const unsigned char* data, PCDataTag) AudioGroupPool AudioGroupPool::CreateAudioGroupPool(const AudioGroupData& data)
{ {
const Header* head = reinterpret_cast<const Header*>(data); athena::io::MemoryReader r(data.getPool(), data.getPoolSize());
switch (data.getDataFormat())
if (head->soundMacrosOffset)
{ {
const unsigned char* cur = data + head->soundMacrosOffset; case DataFormat::PC:
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff) return _AudioGroupPool<athena::Little>(r);
{ default:
uint32_t size = *reinterpret_cast<const uint32_t*>(cur); return _AudioGroupPool<athena::Big>(r);
ObjectId id = *reinterpret_cast<const ObjectId*>(cur + 4);
m_soundMacros[id] = cur;
cur += size;
}
}
if (head->tablesOffset)
{
const unsigned char* cur = data + head->tablesOffset;
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff)
{
uint32_t size = *reinterpret_cast<const uint32_t*>(cur);
ObjectId id = *reinterpret_cast<const ObjectId*>(cur + 4);
m_tables[id] = cur + 8;
cur += size;
}
}
if (head->keymapsOffset)
{
const unsigned char* cur = data + head->keymapsOffset;
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff)
{
uint32_t size = *reinterpret_cast<const uint32_t*>(cur);
ObjectId id = *reinterpret_cast<const ObjectId*>(cur + 4);
m_keymaps[id] = reinterpret_cast<const Keymap*>(cur + 8);
cur += size;
}
}
if (head->layersOffset)
{
const unsigned char* cur = data + head->layersOffset;
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff)
{
uint32_t size = *reinterpret_cast<const uint32_t*>(cur);
ObjectId id = *reinterpret_cast<const ObjectId*>(cur + 4);
std::vector<const LayerMapping*>& mappingsOut = m_layers[id];
uint32_t count = *reinterpret_cast<const uint32_t*>(cur + 8);
mappingsOut.reserve(count);
const unsigned char* subcur = cur + 12;
for (uint32_t i = 0; i < count; ++i)
mappingsOut.push_back(reinterpret_cast<const LayerMapping*>(subcur + i * 12));
cur += size;
}
} }
} }
const unsigned char* AudioGroupPool::soundMacro(ObjectId id) const template <class Tp>
static std::unique_ptr<SoundMacro::ICmd> MakeCmd(athena::io::MemoryReader& r)
{
std::unique_ptr<SoundMacro::ICmd> ret = std::make_unique<Tp>();
static_cast<Tp&>(*ret).read(r);
return ret;
}
int SoundMacro::assertPC(int pc) const
{
if (pc == -1)
return -1;
if (pc >= m_cmds.size())
{
fprintf(stderr, "SoundMacro PC bounds exceeded [%d/%d]\n", pc, int(m_cmds.size()));
abort();
}
return pc;
}
template <athena::Endian DNAE>
void SoundMacro::readCmds(athena::io::IStreamReader& r, uint32_t size)
{
uint32_t numCmds = size / 8;
m_cmds.reserve(numCmds);
for (int i = 0; i < numCmds; ++i)
{
uint32_t data[2];
athena::io::Read<athena::io::PropType::None>::Do<decltype(data), DNAE>({}, data, r);
athena::io::MemoryReader r(data, 8);
std::unique_ptr<ICmd> cmd;
switch (CmdOp(r.readUByte()))
{
case CmdOp::End:
cmd = MakeCmd<CmdEnd>(r); break;
case CmdOp::Stop:
cmd = MakeCmd<CmdStop>(r); break;
case CmdOp::SplitKey:
cmd = MakeCmd<CmdSplitKey>(r); break;
case CmdOp::SplitVel:
cmd = MakeCmd<CmdSplitVel>(r); break;
case CmdOp::WaitTicks:
cmd = MakeCmd<CmdWaitTicks>(r); break;
case CmdOp::Loop:
cmd = MakeCmd<CmdLoop>(r); break;
case CmdOp::Goto:
cmd = MakeCmd<CmdGoto>(r); break;
case CmdOp::WaitMs:
cmd = MakeCmd<CmdWaitMs>(r); break;
case CmdOp::PlayMacro:
cmd = MakeCmd<CmdPlayMacro>(r); break;
case CmdOp::SendKeyOff:
cmd = MakeCmd<CmdSendKeyOff>(r); break;
case CmdOp::SplitMod:
cmd = MakeCmd<CmdSplitMod>(r); break;
case CmdOp::PianoPan:
cmd = MakeCmd<CmdPianoPan>(r); break;
case CmdOp::SetAdsr:
cmd = MakeCmd<CmdSetAdsr>(r); break;
case CmdOp::ScaleVolume:
cmd = MakeCmd<CmdScaleVolume>(r); break;
case CmdOp::Panning:
cmd = MakeCmd<CmdPanning>(r); break;
case CmdOp::Envelope:
cmd = MakeCmd<CmdEnvelope>(r); break;
case CmdOp::StartSample:
cmd = MakeCmd<CmdStartSample>(r); break;
case CmdOp::StopSample:
cmd = MakeCmd<CmdStopSample>(r); break;
case CmdOp::KeyOff:
cmd = MakeCmd<CmdKeyOff>(r); break;
case CmdOp::SplitRnd:
cmd = MakeCmd<CmdSplitRnd>(r); break;
case CmdOp::FadeIn:
cmd = MakeCmd<CmdFadeIn>(r); break;
case CmdOp::Spanning:
cmd = MakeCmd<CmdSpanning>(r); break;
case CmdOp::SetAdsrCtrl:
cmd = MakeCmd<CmdSetAdsrCtrl>(r); break;
case CmdOp::RndNote:
cmd = MakeCmd<CmdRndNote>(r); break;
case CmdOp::AddNote:
cmd = MakeCmd<CmdAddNote>(r); break;
case CmdOp::SetNote:
cmd = MakeCmd<CmdSetNote>(r); break;
case CmdOp::LastNote:
cmd = MakeCmd<CmdLastNote>(r); break;
case CmdOp::Portamento:
cmd = MakeCmd<CmdPortamento>(r); break;
case CmdOp::Vibrato:
cmd = MakeCmd<CmdVibrato>(r); break;
case CmdOp::PitchSweep1:
cmd = MakeCmd<CmdPitchSweep1>(r); break;
case CmdOp::PitchSweep2:
cmd = MakeCmd<CmdPitchSweep2>(r); break;
case CmdOp::SetPitch:
cmd = MakeCmd<CmdSetPitch>(r); break;
case CmdOp::SetPitchAdsr:
cmd = MakeCmd<CmdSetPitchAdsr>(r); break;
case CmdOp::ScaleVolumeDLS:
cmd = MakeCmd<CmdScaleVolumeDLS>(r); break;
case CmdOp::Mod2Vibrange:
cmd = MakeCmd<CmdMod2Vibrange>(r); break;
case CmdOp::SetupTremolo:
cmd = MakeCmd<CmdSetupTremolo>(r); break;
case CmdOp::Return:
cmd = MakeCmd<CmdReturn>(r); break;
case CmdOp::GoSub:
cmd = MakeCmd<CmdGoSub>(r); break;
case CmdOp::TrapEvent:
cmd = MakeCmd<CmdTrapEvent>(r); break;
case CmdOp::UntrapEvent:
cmd = MakeCmd<CmdUntrapEvent>(r); break;
case CmdOp::SendMessage:
cmd = MakeCmd<CmdSendMessage>(r); break;
case CmdOp::GetMessage:
cmd = MakeCmd<CmdGetMessage>(r); break;
case CmdOp::GetVid:
cmd = MakeCmd<CmdGetVid>(r); break;
case CmdOp::AddAgeCount:
cmd = MakeCmd<CmdAddAgeCount>(r); break;
case CmdOp::SetAgeCount:
cmd = MakeCmd<CmdSetAgeCount>(r); break;
case CmdOp::SendFlag:
cmd = MakeCmd<CmdSendFlag>(r); break;
case CmdOp::PitchWheelR:
cmd = MakeCmd<CmdPitchWheelR>(r); break;
case CmdOp::SetPriority:
cmd = MakeCmd<CmdSetPriority>(r); break;
case CmdOp::AddPriority:
cmd = MakeCmd<CmdAddPriority>(r); break;
case CmdOp::AgeCntSpeed:
cmd = MakeCmd<CmdAgeCntSpeed>(r); break;
case CmdOp::AgeCntVel:
cmd = MakeCmd<CmdAgeCntVel>(r); break;
case CmdOp::VolSelect:
cmd = MakeCmd<CmdVolSelect>(r); break;
case CmdOp::PanSelect:
cmd = MakeCmd<CmdPanSelect>(r); break;
case CmdOp::PitchWheelSelect:
cmd = MakeCmd<CmdPitchWheelSelect>(r); break;
case CmdOp::ModWheelSelect:
cmd = MakeCmd<CmdModWheelSelect>(r); break;
case CmdOp::PedalSelect:
cmd = MakeCmd<CmdPedalSelect>(r); break;
case CmdOp::PortamentoSelect:
cmd = MakeCmd<CmdPortamentoSelect>(r); break;
case CmdOp::ReverbSelect:
cmd = MakeCmd<CmdReverbSelect>(r); break;
case CmdOp::SpanSelect:
cmd = MakeCmd<CmdSpanSelect>(r); break;
case CmdOp::DopplerSelect:
cmd = MakeCmd<CmdDopplerSelect>(r); break;
case CmdOp::TremoloSelect:
cmd = MakeCmd<CmdTremoloSelect>(r); break;
case CmdOp::PreASelect:
cmd = MakeCmd<CmdPreASelect>(r); break;
case CmdOp::PreBSelect:
cmd = MakeCmd<CmdPreBSelect>(r); break;
case CmdOp::PostBSelect:
cmd = MakeCmd<CmdPostBSelect>(r); break;
case CmdOp::AuxAFXSelect:
cmd = MakeCmd<CmdAuxAFXSelect>(r); break;
case CmdOp::AuxBFXSelect:
cmd = MakeCmd<CmdAuxBFXSelect>(r); break;
case CmdOp::SetupLFO:
cmd = MakeCmd<CmdSetupLFO>(r); break;
case CmdOp::ModeSelect:
cmd = MakeCmd<CmdModeSelect>(r); break;
case CmdOp::SetKeygroup:
cmd = MakeCmd<CmdSetKeygroup>(r); break;
case CmdOp::SRCmodeSelect:
cmd = MakeCmd<CmdSRCmodeSelect>(r); break;
case CmdOp::AddVars:
cmd = MakeCmd<CmdAddVars>(r); break;
case CmdOp::SubVars:
cmd = MakeCmd<CmdSubVars>(r); break;
case CmdOp::MulVars:
cmd = MakeCmd<CmdMulVars>(r); break;
case CmdOp::DivVars:
cmd = MakeCmd<CmdDivVars>(r); break;
case CmdOp::AddIVars:
cmd = MakeCmd<CmdAddIVars>(r); break;
case CmdOp::IfEqual:
cmd = MakeCmd<CmdIfEqual>(r); break;
case CmdOp::IfLess:
cmd = MakeCmd<CmdIfLess>(r); break;
default:
break;
}
m_cmds.push_back(std::move(cmd));
}
}
template void SoundMacro::readCmds<athena::Big>(athena::io::IStreamReader& r, uint32_t size);
template void SoundMacro::readCmds<athena::Little>(athena::io::IStreamReader& r, uint32_t size);
const SoundMacro* AudioGroupPool::soundMacro(ObjectId id) const
{ {
auto search = m_soundMacros.find(id); auto search = m_soundMacros.find(id);
if (search == m_soundMacros.cend()) if (search == m_soundMacros.cend())
return nullptr; return nullptr;
return search->second; return &search->second;
} }
const Keymap* AudioGroupPool::keymap(ObjectId id) const const Keymap* AudioGroupPool::keymap(ObjectId id) const
@ -154,10 +328,10 @@ const Keymap* AudioGroupPool::keymap(ObjectId id) const
auto search = m_keymaps.find(id); auto search = m_keymaps.find(id);
if (search == m_keymaps.cend()) if (search == m_keymaps.cend())
return nullptr; return nullptr;
return search->second; return &search->second;
} }
const std::vector<const LayerMapping*>* AudioGroupPool::layer(ObjectId id) const const std::vector<LayerMapping>* AudioGroupPool::layer(ObjectId id) const
{ {
auto search = m_layers.find(id); auto search = m_layers.find(id);
if (search == m_layers.cend()) if (search == m_layers.cend())
@ -170,6 +344,41 @@ const ADSR* AudioGroupPool::tableAsAdsr(ObjectId id) const
auto search = m_tables.find(id); auto search = m_tables.find(id);
if (search == m_tables.cend()) if (search == m_tables.cend())
return nullptr; return nullptr;
return reinterpret_cast<const ADSR*>(search->second); return static_cast<const ADSR*>(search->second.get());
}
template <>
void amuse::Curve::Enumerate<LittleDNA::Read>(athena::io::IStreamReader& r)
{
Log.report(logvisor::Fatal, "Curve binary DNA read not supported");
}
template <>
void amuse::Curve::Enumerate<LittleDNA::Write>(athena::io::IStreamWriter& w)
{
Log.report(logvisor::Fatal, "Curve binary DNA write not supported");
}
template <>
void amuse::Curve::Enumerate<LittleDNA::BinarySize>(size_t& sz)
{
Log.report(logvisor::Fatal, "Curve binary DNA size not supported");
}
template <>
void amuse::Curve::Enumerate<LittleDNA::ReadYaml>(athena::io::YAMLDocReader& r)
{
r.enumerate(nullptr, data);
}
template <>
void amuse::Curve::Enumerate<LittleDNA::WriteYaml>(athena::io::YAMLDocWriter& w)
{
w.enumerate(nullptr, data);
}
const char* amuse::Curve::DNAType()
{
return "amuse::ADSR";
} }
} }

View File

@ -1,548 +1,224 @@
#include "amuse/AudioGroupProject.hpp" #include "amuse/AudioGroupProject.hpp"
#include "amuse/AudioGroupData.hpp" #include "amuse/AudioGroupData.hpp"
#include "amuse/Common.hpp" #include "athena/MemoryReader.hpp"
#include <stdint.h>
namespace amuse namespace amuse
{ {
enum class GroupType : uint16_t static bool AtEnd32(athena::io::IStreamReader& r)
{ {
Song, uint32_t v = r.readUint32Big();
SFX r.seek(-4, athena::Current);
}; return v == 0xffffffff;
}
struct GroupHeader static bool AtEnd16(athena::io::IStreamReader& r)
{ {
uint32_t groupEndOff; uint16_t v = r.readUint16Big();
uint16_t groupId; r.seek(-2, athena::Current);
GroupType type; return v == 0xffff;
uint32_t soundMacroIdsOff; }
uint32_t samplIdsOff;
uint32_t tableIdsOff;
uint32_t keymapIdsOff;
uint32_t layerIdsOff;
uint32_t pageTableOff;
uint32_t drumTableOff;
uint32_t midiSetupsOff;
void swapBig() AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag)
{
while (!AtEnd32(r))
{ {
groupEndOff = SBig(groupEndOff); GroupHeader<athena::Big> header;
groupId = SBig(groupId); header.read(r);
type = GroupType(SBig(uint16_t(type)));
soundMacroIdsOff = SBig(soundMacroIdsOff);
samplIdsOff = SBig(samplIdsOff);
tableIdsOff = SBig(tableIdsOff);
keymapIdsOff = SBig(keymapIdsOff);
layerIdsOff = SBig(layerIdsOff);
pageTableOff = SBig(pageTableOff);
drumTableOff = SBig(drumTableOff);
midiSetupsOff = SBig(midiSetupsOff);
}
};
AudioGroupProject::AudioGroupProject(const unsigned char* data, GCNDataTag)
{
const GroupHeader* group = reinterpret_cast<const GroupHeader*>(data);
while (group->groupEndOff != 0xffffffff)
{
GroupHeader header = *group;
header.swapBig();
AudioGroupIndex* bIdx = nullptr;
if (header.type == GroupType::Song) if (header.type == GroupType::Song)
{ {
SongGroupIndex& idx = m_songGroups[header.groupId]; SongGroupIndex& idx = m_songGroups[header.groupId];
bIdx = &idx;
/* Normal pages */ /* Normal pages */
const SongGroupIndex::PageEntry* normEntries = r.seek(header.pageTableOff, athena::Begin);
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + header.pageTableOff); while (!AtEnd16(r))
while (normEntries->objId != 0xffff)
{ {
idx.m_normPages[normEntries->programNo] = normEntries; SongGroupIndex::PageEntryDNA<athena::Big> entry;
++normEntries; entry.read(r);
idx.m_normPages[entry.programNo] = entry;
} }
/* Drum pages */ /* Drum pages */
const SongGroupIndex::PageEntry* drumEntries = r.seek(header.drumTableOff, athena::Begin);
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + header.drumTableOff); while (!AtEnd16(r))
while (drumEntries->objId != 0xffff)
{ {
idx.m_drumPages[drumEntries->programNo] = drumEntries; SongGroupIndex::PageEntryDNA<athena::Big> entry;
++drumEntries; entry.read(r);
idx.m_drumPages[entry.programNo] = entry;
} }
/* MIDI setups */ /* MIDI setups */
const uint8_t* setupData = data + header.midiSetupsOff; r.seek(header.midiSetupsOff, athena::Begin);
const uint8_t* setupEnd = data + header.groupEndOff; while (r.position() < header.groupEndOff)
while (setupData < setupEnd)
{ {
uint16_t songId = SBig(*reinterpret_cast<const uint16_t*>(setupData)); uint16_t songId = r.readUint16Big();
idx.m_midiSetups[songId] = r.seek(2, athena::Current);
reinterpret_cast<const std::array<SongGroupIndex::MIDISetup, 16>*>(setupData + 4); std::array<SongGroupIndex::MIDISetup, 16>& setup = idx.m_midiSetups[songId];
setupData += 5 * 16 + 4; for (int i = 0; i < 16 ; ++i)
setup[i].read(r);
} }
} }
else if (header.type == GroupType::SFX) else if (header.type == GroupType::SFX)
{ {
SFXGroupIndex& idx = m_sfxGroups[header.groupId]; SFXGroupIndex& idx = m_sfxGroups[header.groupId];
bIdx = &idx;
/* SFX entries */ /* SFX entries */
uint16_t count = SBig(*reinterpret_cast<const uint16_t*>(data + header.pageTableOff)); r.seek(header.pageTableOff, athena::Begin);
uint16_t count = r.readUint16Big();
r.seek(2, athena::Current);
idx.m_sfxEntries.reserve(count); idx.m_sfxEntries.reserve(count);
const SFXGroupIndex::SFXEntry* entries =
reinterpret_cast<const SFXGroupIndex::SFXEntry*>(data + header.pageTableOff + 4);
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
{ {
idx.m_sfxEntries[SBig(entries->defineId)] = entries; SFXGroupIndex::SFXEntryDNA<athena::Big> entry;
++entries; entry.read(r);
idx.m_sfxEntries[entry.defineId.id] = entry;
} }
} }
if (bIdx) r.seek(header.groupEndOff, athena::Begin);
{
bIdx->m_soundMacroIndex = reinterpret_cast<const uint16_t*>(data + header.soundMacroIdsOff);
bIdx->m_tablesIndex = reinterpret_cast<const uint16_t*>(data + header.tableIdsOff);
bIdx->m_keymapsIndex = reinterpret_cast<const uint16_t*>(data + header.keymapIdsOff);
bIdx->m_layersIndex = reinterpret_cast<const uint16_t*>(data + header.layerIdsOff);
}
group = reinterpret_cast<const GroupHeader*>(data + header.groupEndOff);
} }
} }
struct MusyX1PageEntry template <athena::Endian DNAE>
AudioGroupProject AudioGroupProject::_AudioGroupProject(athena::io::IStreamReader& r, bool absOffs)
{ {
ObjectId objId; AudioGroupProject ret;
uint8_t priority;
uint8_t maxVoices;
uint8_t unk;
uint8_t programNo;
uint8_t pad[2];
void setIntoMusyX2(SongGroupIndex::PageEntry& ent) const while (!AtEnd32(r))
{ {
ent.objId = objId; atInt64 groupBegin = r.position();
ent.priority = priority; atInt64 subDataOff = absOffs ? 0 : groupBegin + 8;
ent.maxVoices = maxVoices; GroupHeader<DNAE> header;
ent.programNo = programNo; header.read(r);
}
};
struct MusyX1MIDISetup
{
uint8_t programNo;
uint8_t volume;
uint8_t panning;
uint8_t reverb;
uint8_t chorus;
uint8_t pad[3];
void setIntoMusyX2(SongGroupIndex::MIDISetup& ent) const
{
ent.programNo = programNo;
ent.volume = volume;
ent.panning = panning;
ent.reverb = reverb;
ent.chorus = chorus;
}
};
void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, N64DataTag)
{
size_t normPageCount = 0;
size_t drumPageCount = 0;
size_t midiSetupCount = 0;
const GroupHeader* group = reinterpret_cast<const GroupHeader*>(data);
while (group->groupEndOff != 0xffffffff)
{
const unsigned char* subData = data + 8;
GroupHeader header = *group;
header.swapBig();
if (header.type == GroupType::Song) if (header.type == GroupType::Song)
{ {
/* Normal pages */ SongGroupIndex& idx = ret.m_songGroups[header.groupId];
const MusyX1PageEntry* normEntries =
reinterpret_cast<const MusyX1PageEntry*>(subData + header.pageTableOff);
while (normEntries->objId != 0xffff)
{
++normPageCount;
++normEntries;
}
/* Drum pages */
const MusyX1PageEntry* drumEntries =
reinterpret_cast<const MusyX1PageEntry*>(subData + header.drumTableOff);
while (drumEntries->objId != 0xffff)
{
++drumPageCount;
++drumEntries;
}
/* MIDI setups */
const uint8_t* setupData = subData + header.midiSetupsOff;
const uint8_t* setupEnd = subData + header.groupEndOff;
while (setupData < setupEnd)
{
++midiSetupCount;
setupData += 8 * 16 + 4;
}
}
data += header.groupEndOff;
group = reinterpret_cast<const GroupHeader*>(data);
}
if (normPageCount)
m_convNormalPages.reset(new SongGroupIndex::PageEntry[normPageCount]);
if (drumPageCount)
m_convDrumPages.reset(new SongGroupIndex::PageEntry[drumPageCount]);
if (midiSetupCount)
m_convMidiSetups.reset(new std::array<SongGroupIndex::MIDISetup, 16>[ midiSetupCount ]);
}
AudioGroupProject::AudioGroupProject(const unsigned char* data, bool absOffs, N64DataTag)
{
if (!absOffs)
_allocateConvBuffers(data, N64DataTag{});
SongGroupIndex::PageEntry* normPagesBuf = m_convNormalPages.get();
SongGroupIndex::PageEntry* drumPagesBuf = m_convDrumPages.get();
std::array<SongGroupIndex::MIDISetup, 16>* midiSetupsBuf = m_convMidiSetups.get();
const GroupHeader* group = reinterpret_cast<const GroupHeader*>(data);
while (group->groupEndOff != 0xffffffff)
{
const unsigned char* subData = absOffs ? data : data + 8;
GroupHeader header = *group;
header.swapBig();
AudioGroupIndex* bIdx = nullptr;
if (header.type == GroupType::Song)
{
SongGroupIndex& idx = m_songGroups[header.groupId];
bIdx = &idx;
if (absOffs) if (absOffs)
{ {
/* Normal pages */ /* Normal pages */
const SongGroupIndex::PageEntry* normEntries = r.seek(header.pageTableOff, athena::Begin);
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + header.pageTableOff); while (!AtEnd16(r))
while (normEntries->objId != 0xffff)
{ {
idx.m_normPages[normEntries->programNo] = normEntries; SongGroupIndex::PageEntryDNA<DNAE> entry;
++normEntries; entry.read(r);
idx.m_normPages[entry.programNo] = entry;
} }
/* Drum pages */ /* Drum pages */
const SongGroupIndex::PageEntry* drumEntries = r.seek(header.drumTableOff, athena::Begin);
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + header.drumTableOff); while (!AtEnd16(r))
while (drumEntries->objId != 0xffff)
{ {
idx.m_drumPages[drumEntries->programNo] = drumEntries; SongGroupIndex::PageEntryDNA<DNAE> entry;
++drumEntries; entry.read(r);
idx.m_drumPages[entry.programNo] = entry;
} }
/* MIDI setups */ /* MIDI setups */
const uint8_t* setupData = data + header.midiSetupsOff; r.seek(header.midiSetupsOff, athena::Begin);
const uint8_t* setupEnd = data + header.groupEndOff; while (r.position() < header.groupEndOff)
while (setupData < setupEnd)
{ {
uint16_t songId = SBig(*reinterpret_cast<const uint16_t*>(setupData)); uint16_t songId = r.readUint16Big();
idx.m_midiSetups[songId] = r.seek(2, athena::Current);
reinterpret_cast<const std::array<SongGroupIndex::MIDISetup, 16>*>(setupData + 4); std::array<SongGroupIndex::MIDISetup, 16>& setup = idx.m_midiSetups[songId];
setupData += 5 * 16 + 4; for (int i = 0; i < 16 ; ++i)
setup[i].read(r);
} }
} }
else else
{ {
/* Normal pages */ /* Normal pages */
const MusyX1PageEntry* normEntries = r.seek(subDataOff + header.pageTableOff, athena::Begin);
reinterpret_cast<const MusyX1PageEntry*>(subData + header.pageTableOff); while (!AtEnd16(r))
while (normEntries->objId != 0xffff)
{ {
normEntries->setIntoMusyX2(*normPagesBuf); SongGroupIndex::MusyX1PageEntryDNA<DNAE> entry;
idx.m_normPages[normEntries->programNo] = normPagesBuf; entry.read(r);
++normEntries; idx.m_normPages[entry.programNo] = entry;
++normPagesBuf;
} }
/* Drum pages */ /* Drum pages */
const MusyX1PageEntry* drumEntries = r.seek(subDataOff + header.drumTableOff, athena::Begin);
reinterpret_cast<const MusyX1PageEntry*>(subData + header.drumTableOff); while (!AtEnd16(r))
while (drumEntries->objId != 0xffff)
{ {
drumEntries->setIntoMusyX2(*drumPagesBuf); SongGroupIndex::MusyX1PageEntryDNA<DNAE> entry;
idx.m_drumPages[drumEntries->programNo] = drumPagesBuf; entry.read(r);
++drumEntries; idx.m_drumPages[entry.programNo] = entry;
++drumPagesBuf;
} }
/* MIDI setups */ /* MIDI setups */
const uint8_t* setupData = subData + header.midiSetupsOff; r.seek(subDataOff + header.midiSetupsOff, athena::Begin);
const uint8_t* setupEnd = subData + header.groupEndOff; while (r.position() < groupBegin + header.groupEndOff)
while (setupData < setupEnd)
{ {
uint16_t songId = SBig(*reinterpret_cast<const uint16_t*>(setupData)); uint16_t songId = r.readUint16Big();
const std::array<MusyX1MIDISetup, 16>* midiSetups = r.seek(2, athena::Current);
reinterpret_cast<const std::array<MusyX1MIDISetup, 16>*>(setupData + 4); std::array<SongGroupIndex::MIDISetup, 16>& setup = idx.m_midiSetups[songId];
for (int i = 0; i < 16 ; ++i)
for (int i = 0; i < 16; ++i) {
(*midiSetups)[i].setIntoMusyX2((*midiSetupsBuf)[i]); SongGroupIndex::MusyX1MIDISetup ent;
ent.read(r);
idx.m_midiSetups[songId] = midiSetupsBuf; setup[i] = ent;
setupData += 8 * 16 + 4; }
++midiSetupsBuf;
} }
} }
} }
else if (header.type == GroupType::SFX) else if (header.type == GroupType::SFX)
{ {
SFXGroupIndex& idx = m_sfxGroups[header.groupId]; SFXGroupIndex& idx = ret.m_sfxGroups[header.groupId];
bIdx = &idx;
/* SFX entries */ /* SFX entries */
uint16_t count = SBig(*reinterpret_cast<const uint16_t*>(subData + header.pageTableOff)); r.seek(subDataOff + header.pageTableOff, athena::Begin);
uint16_t count = r.readUint16Big();
r.seek(2, athena::Current);
idx.m_sfxEntries.reserve(count); idx.m_sfxEntries.reserve(count);
for (int i = 0; i < count; ++i) for (int i = 0; i < count; ++i)
{ {
const SFXGroupIndex::SFXEntry* entries = SFXGroupIndex::SFXEntryDNA<DNAE> entry;
reinterpret_cast<const SFXGroupIndex::SFXEntry*>(subData + header.pageTableOff + 4 + i * 12); entry.read(r);
idx.m_sfxEntries[SBig(entries->defineId)] = entries; r.seek(2, athena::Current);
idx.m_sfxEntries[entry.defineId.id] = entry;
} }
} }
if (bIdx)
{
bIdx->m_soundMacroIndex = reinterpret_cast<const uint16_t*>(subData + header.soundMacroIdsOff);
bIdx->m_tablesIndex = reinterpret_cast<const uint16_t*>(subData + header.tableIdsOff);
bIdx->m_keymapsIndex = reinterpret_cast<const uint16_t*>(subData + header.keymapIdsOff);
bIdx->m_layersIndex = reinterpret_cast<const uint16_t*>(subData + header.layerIdsOff);
}
if (absOffs) if (absOffs)
group = reinterpret_cast<const GroupHeader*>(data + header.groupEndOff); r.seek(header.groupEndOff, athena::Begin);
else else
{ r.seek(groupBegin + header.groupEndOff, athena::Begin);
data += header.groupEndOff;
group = reinterpret_cast<const GroupHeader*>(data);
}
}
}
void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, PCDataTag)
{
size_t normPageCount = 0;
size_t drumPageCount = 0;
size_t midiSetupCount = 0;
const GroupHeader* group = reinterpret_cast<const GroupHeader*>(data);
while (group->groupEndOff != 0xffffffff)
{
const unsigned char* subData = data + 8;
if (group->type == GroupType::Song)
{
/* Normal pages */
const MusyX1PageEntry* normEntries =
reinterpret_cast<const MusyX1PageEntry*>(subData + group->pageTableOff);
while (normEntries->objId != 0xffff)
{
++normPageCount;
++normEntries;
} }
/* Drum pages */ return ret;
const MusyX1PageEntry* drumEntries =
reinterpret_cast<const MusyX1PageEntry*>(subData + group->drumTableOff);
while (drumEntries->objId != 0xffff)
{
++drumPageCount;
++drumEntries;
}
/* MIDI setups */
const uint8_t* setupData = subData + group->midiSetupsOff;
const uint8_t* setupEnd = subData + group->groupEndOff;
while (setupData < setupEnd)
{
++midiSetupCount;
setupData += 8 * 16 + 4;
}
}
data += group->groupEndOff;
group = reinterpret_cast<const GroupHeader*>(data);
}
if (normPageCount)
m_convNormalPages.reset(new SongGroupIndex::PageEntry[normPageCount]);
if (drumPageCount)
m_convDrumPages.reset(new SongGroupIndex::PageEntry[drumPageCount]);
if (midiSetupCount)
m_convMidiSetups.reset(new std::array<SongGroupIndex::MIDISetup, 16>[ midiSetupCount ]);
}
AudioGroupProject::AudioGroupProject(const unsigned char* data, bool absOffs, PCDataTag)
{
if (!absOffs)
_allocateConvBuffers(data, PCDataTag{});
SongGroupIndex::PageEntry* normPagesBuf = m_convNormalPages.get();
SongGroupIndex::PageEntry* drumPagesBuf = m_convDrumPages.get();
std::array<SongGroupIndex::MIDISetup, 16>* midiSetupsBuf = m_convMidiSetups.get();
const GroupHeader* group = reinterpret_cast<const GroupHeader*>(data);
while (group->groupEndOff != 0xffffffff)
{
const unsigned char* subData = absOffs ? data : data + 8;
AudioGroupIndex* bIdx = nullptr;
if (group->type == GroupType::Song)
{
SongGroupIndex& idx = m_songGroups[group->groupId];
bIdx = &idx;
if (absOffs)
{
/* Normal pages */
const SongGroupIndex::PageEntry* normEntries =
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + group->pageTableOff);
while (normEntries->objId != 0xffff)
{
idx.m_normPages[normEntries->programNo] = normEntries;
++normEntries;
}
/* Drum pages */
const SongGroupIndex::PageEntry* drumEntries =
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + group->drumTableOff);
while (drumEntries->objId != 0xffff)
{
idx.m_drumPages[drumEntries->programNo] = drumEntries;
++drumEntries;
}
/* MIDI setups */
const uint8_t* setupData = data + group->midiSetupsOff;
const uint8_t* setupEnd = data + group->groupEndOff;
while (setupData < setupEnd)
{
uint16_t songId = *reinterpret_cast<const uint16_t*>(setupData);
idx.m_midiSetups[songId] =
reinterpret_cast<const std::array<SongGroupIndex::MIDISetup, 16>*>(setupData + 4);
setupData += 5 * 16 + 4;
}
}
else
{
/* Normal pages */
const MusyX1PageEntry* normEntries =
reinterpret_cast<const MusyX1PageEntry*>(subData + group->pageTableOff);
while (normEntries->objId != 0xffff)
{
normEntries->setIntoMusyX2(*normPagesBuf);
idx.m_normPages[normEntries->programNo] = normPagesBuf;
++normEntries;
++normPagesBuf;
}
/* Drum pages */
const MusyX1PageEntry* drumEntries =
reinterpret_cast<const MusyX1PageEntry*>(subData + group->drumTableOff);
while (drumEntries->objId != 0xffff)
{
drumEntries->setIntoMusyX2(*drumPagesBuf);
idx.m_drumPages[drumEntries->programNo] = drumPagesBuf;
++drumEntries;
++drumPagesBuf;
}
/* MIDI setups */
const uint8_t* setupData = subData + group->midiSetupsOff;
const uint8_t* setupEnd = subData + group->groupEndOff;
while (setupData < setupEnd)
{
uint16_t songId = *reinterpret_cast<const uint16_t*>(setupData);
const std::array<MusyX1MIDISetup, 16>* midiSetups =
reinterpret_cast<const std::array<MusyX1MIDISetup, 16>*>(setupData + 4);
for (int i = 0; i < 16; ++i)
(*midiSetups)[i].setIntoMusyX2((*midiSetupsBuf)[i]);
idx.m_midiSetups[songId] = midiSetupsBuf;
setupData += 8 * 16 + 4;
++midiSetupsBuf;
}
}
}
else if (group->type == GroupType::SFX)
{
SFXGroupIndex& idx = m_sfxGroups[group->groupId];
bIdx = &idx;
/* SFX entries */
uint16_t count = *reinterpret_cast<const uint16_t*>(subData + group->pageTableOff);
idx.m_sfxEntries.reserve(count);
for (int i = 0; i < count; ++i)
{
const SFXGroupIndex::SFXEntry* entries =
reinterpret_cast<const SFXGroupIndex::SFXEntry*>(subData + group->pageTableOff + 4 + i * 12);
idx.m_sfxEntries[entries->defineId] = entries;
}
}
if (bIdx)
{
bIdx->m_soundMacroIndex = reinterpret_cast<const uint16_t*>(subData + group->soundMacroIdsOff);
bIdx->m_tablesIndex = reinterpret_cast<const uint16_t*>(subData + group->tableIdsOff);
bIdx->m_keymapsIndex = reinterpret_cast<const uint16_t*>(subData + group->keymapIdsOff);
bIdx->m_layersIndex = reinterpret_cast<const uint16_t*>(subData + group->layerIdsOff);
}
if (absOffs)
group = reinterpret_cast<const GroupHeader*>(data + group->groupEndOff);
else
{
data += group->groupEndOff;
group = reinterpret_cast<const GroupHeader*>(data);
}
}
} }
AudioGroupProject AudioGroupProject::CreateAudioGroupProject(const AudioGroupData& data) AudioGroupProject AudioGroupProject::CreateAudioGroupProject(const AudioGroupData& data)
{ {
athena::io::MemoryReader r(data.getProj(), data.getProjSize());
switch (data.getDataFormat()) switch (data.getDataFormat())
{ {
case DataFormat::GCN: case DataFormat::GCN:
default: default:
return AudioGroupProject(data.getProj(), GCNDataTag{}); return AudioGroupProject(r, GCNDataTag{});
case DataFormat::N64: case DataFormat::N64:
return AudioGroupProject(data.getProj(), data.getAbsoluteProjOffsets(), N64DataTag{}); return _AudioGroupProject<athena::Big>(r, data.getAbsoluteProjOffsets());
case DataFormat::PC: case DataFormat::PC:
return AudioGroupProject(data.getProj(), data.getAbsoluteProjOffsets(), PCDataTag{}); return _AudioGroupProject<athena::Little>(r, data.getAbsoluteProjOffsets());
} }
} }
const SongGroupIndex* AudioGroupProject::getSongGroupIndex(int groupId) const const SongGroupIndex* AudioGroupProject::getSongGroupIndex(int groupId) const
{ {
auto search = m_songGroups.find(groupId); auto search = m_songGroups.find(groupId);
if (search == m_songGroups.cend()) if (search != m_songGroups.cend())
return nullptr;
return &search->second; return &search->second;
return nullptr;
} }
const SFXGroupIndex* AudioGroupProject::getSFXGroupIndex(int groupId) const const SFXGroupIndex* AudioGroupProject::getSFXGroupIndex(int groupId) const
{ {
auto search = m_sfxGroups.find(groupId); auto search = m_sfxGroups.find(groupId);
if (search == m_sfxGroups.cend()) if (search != m_sfxGroups.cend())
return nullptr;
return &search->second; return &search->second;
return nullptr;
} }
} }

View File

@ -1,5 +1,6 @@
#include "amuse/AudioGroupSampleDirectory.hpp" #include "amuse/AudioGroupSampleDirectory.hpp"
#include "amuse/Common.hpp" #include "amuse/Common.hpp"
#include "amuse/AudioGroupData.hpp"
#include <cstring> #include <cstring>
namespace amuse namespace amuse
@ -195,4 +196,18 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(const unsigned char* data,
} }
} }
} }
AudioGroupSampleDirectory AudioGroupSampleDirectory::CreateAudioGroupSampleDirectory(const AudioGroupData& data)
{
switch (data.getDataFormat())
{
case DataFormat::GCN:
default:
return AudioGroupSampleDirectory(data.getSdir(), GCNDataTag{});
case DataFormat::N64:
return AudioGroupSampleDirectory(data.getSdir(), data.getSamp(), data.getAbsoluteProjOffsets(), N64DataTag{});
case DataFormat::PC:
return AudioGroupSampleDirectory(data.getSdir(), data.getAbsoluteProjOffsets(), PCDataTag{});
}
}
} }

459
lib/Common.cpp Normal file
View File

@ -0,0 +1,459 @@
#include "amuse/Common.hpp"
#include "logvisor/logvisor.hpp"
namespace amuse
{
static logvisor::Module Log("amuse");
thread_local NameDB* ObjectId::CurNameDB = nullptr;
thread_local NameDB* SampleId::CurNameDB = nullptr;
thread_local NameDB* SongId::CurNameDB = nullptr;
thread_local NameDB* SFXId::CurNameDB = nullptr;
template<> template<>
void ObjectIdDNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
{
id = reader.readUint16Little();
}
template<> template<>
void ObjectIdDNA<athena::Little>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
{
writer.writeUint16Little(id);
}
template<> template<>
void ObjectIdDNA<athena::Little>::Enumerate<BigDNA::BinarySize>(size_t& sz)
{
sz += 2;
}
template<> template<>
void ObjectIdDNA<athena::Little>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
{
_read(reader);
}
template<> template<>
void ObjectIdDNA<athena::Little>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
{
_write(writer);
}
template<> template<>
void ObjectIdDNA<athena::Big>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
{
id = reader.readUint16Big();
}
template<> template<>
void ObjectIdDNA<athena::Big>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
{
writer.writeUint16Big(id);
}
template<> template<>
void ObjectIdDNA<athena::Big>::Enumerate<BigDNA::BinarySize>(size_t& sz)
{
sz += 2;
}
template<> template<>
void ObjectIdDNA<athena::Big>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
{
_read(reader);
}
template<> template<>
void ObjectIdDNA<athena::Big>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
{
_write(writer);
}
template <athena::Endian DNAE>
void ObjectIdDNA<DNAE>::_read(athena::io::YAMLDocReader& r)
{
std::string name = r.readString(nullptr);
if (!ObjectId::CurNameDB)
Log.report(logvisor::Fatal, "Unable to resolve object name %s, no database present", name.c_str());
id = ObjectId::CurNameDB->resolveIdFromName(name);
}
template <athena::Endian DNAE>
void ObjectIdDNA<DNAE>::_write(athena::io::YAMLDocWriter& w)
{
if (!ObjectId::CurNameDB)
Log.report(logvisor::Fatal, "Unable to resolve object ID %d, no database present", id.id);
std::string_view name = ObjectId::CurNameDB->resolveNameFromId(id);
w.writeString(nullptr, name);
}
template <athena::Endian DNAE>
const char* ObjectIdDNA<DNAE>::DNAType()
{
return "amuse::ObjectId";
}
template<> template<>
void SampleIdDNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
{
id = reader.readUint16Little();
}
template<> template<>
void SampleIdDNA<athena::Little>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
{
writer.writeUint16Little(id);
}
template<> template<>
void SampleIdDNA<athena::Little>::Enumerate<BigDNA::BinarySize>(size_t& sz)
{
sz += 2;
}
template<> template<>
void SampleIdDNA<athena::Little>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
{
_read(reader);
}
template<> template<>
void SampleIdDNA<athena::Little>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
{
_write(writer);
}
template<> template<>
void SampleIdDNA<athena::Big>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
{
id = reader.readUint16Big();
}
template<> template<>
void SampleIdDNA<athena::Big>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
{
writer.writeUint16Big(id);
}
template<> template<>
void SampleIdDNA<athena::Big>::Enumerate<BigDNA::BinarySize>(size_t& sz)
{
sz += 2;
}
template<> template<>
void SampleIdDNA<athena::Big>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
{
_read(reader);
}
template<> template<>
void SampleIdDNA<athena::Big>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
{
_write(writer);
}
template <athena::Endian DNAE>
void SampleIdDNA<DNAE>::_read(athena::io::YAMLDocReader& r)
{
std::string name = r.readString(nullptr);
if (!SampleId::CurNameDB)
Log.report(logvisor::Fatal, "Unable to resolve sample name %s, no database present", name.c_str());
id = SampleId::CurNameDB->resolveIdFromName(name);
}
template <athena::Endian DNAE>
void SampleIdDNA<DNAE>::_write(athena::io::YAMLDocWriter& w)
{
if (!SampleId::CurNameDB)
Log.report(logvisor::Fatal, "Unable to resolve sample ID %d, no database present", id.id);
std::string_view name = SampleId::CurNameDB->resolveNameFromId(id);
w.writeString(nullptr, name);
}
template <athena::Endian DNAE>
const char* SampleIdDNA<DNAE>::DNAType()
{
return "amuse::SampleId";
}
template<> template<>
void SongIdDNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
{
id = reader.readUint16Little();
}
template<> template<>
void SongIdDNA<athena::Little>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
{
writer.writeUint16Little(id);
}
template<> template<>
void SongIdDNA<athena::Little>::Enumerate<BigDNA::BinarySize>(size_t& sz)
{
sz += 2;
}
template<> template<>
void SongIdDNA<athena::Little>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
{
_read(reader);
}
template<> template<>
void SongIdDNA<athena::Little>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
{
_write(writer);
}
template<> template<>
void SongIdDNA<athena::Big>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
{
id = reader.readUint16Big();
}
template<> template<>
void SongIdDNA<athena::Big>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
{
writer.writeUint16Big(id);
}
template<> template<>
void SongIdDNA<athena::Big>::Enumerate<BigDNA::BinarySize>(size_t& sz)
{
sz += 2;
}
template<> template<>
void SongIdDNA<athena::Big>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
{
_read(reader);
}
template<> template<>
void SongIdDNA<athena::Big>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
{
_write(writer);
}
template <athena::Endian DNAE>
void SongIdDNA<DNAE>::_read(athena::io::YAMLDocReader& r)
{
std::string name = r.readString(nullptr);
if (!SongId::CurNameDB)
Log.report(logvisor::Fatal, "Unable to resolve song name %s, no database present", name.c_str());
id = SongId::CurNameDB->resolveIdFromName(name);
}
template <athena::Endian DNAE>
void SongIdDNA<DNAE>::_write(athena::io::YAMLDocWriter& w)
{
if (!SongId::CurNameDB)
Log.report(logvisor::Fatal, "Unable to resolve song ID %d, no database present", id.id);
std::string_view name = SongId::CurNameDB->resolveNameFromId(id);
w.writeString(nullptr, name);
}
template <athena::Endian DNAE>
const char* SongIdDNA<DNAE>::DNAType()
{
return "amuse::SongId";
}
template<> template<>
void SFXIdDNA<athena::Little>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
{
id = reader.readUint16Little();
}
template<> template<>
void SFXIdDNA<athena::Little>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
{
writer.writeUint16Little(id);
}
template<> template<>
void SFXIdDNA<athena::Little>::Enumerate<BigDNA::BinarySize>(size_t& sz)
{
sz += 2;
}
template<> template<>
void SFXIdDNA<athena::Little>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
{
_read(reader);
}
template<> template<>
void SFXIdDNA<athena::Little>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
{
_write(writer);
}
template<> template<>
void SFXIdDNA<athena::Big>::Enumerate<BigDNA::Read>(athena::io::IStreamReader& reader)
{
id = reader.readUint16Big();
}
template<> template<>
void SFXIdDNA<athena::Big>::Enumerate<BigDNA::Write>(athena::io::IStreamWriter& writer)
{
writer.writeUint16Big(id);
}
template<> template<>
void SFXIdDNA<athena::Big>::Enumerate<BigDNA::BinarySize>(size_t& sz)
{
sz += 2;
}
template<> template<>
void SFXIdDNA<athena::Big>::Enumerate<BigDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
{
_read(reader);
}
template<> template<>
void SFXIdDNA<athena::Big>::Enumerate<BigDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
{
_write(writer);
}
template <athena::Endian DNAE>
void SFXIdDNA<DNAE>::_read(athena::io::YAMLDocReader& r)
{
std::string name = r.readString(nullptr);
if (!SFXId::CurNameDB)
Log.report(logvisor::Fatal, "Unable to resolve song name %s, no database present", name.c_str());
id = SFXId::CurNameDB->resolveIdFromName(name);
}
template <athena::Endian DNAE>
void SFXIdDNA<DNAE>::_write(athena::io::YAMLDocWriter& w)
{
if (!SFXId::CurNameDB)
Log.report(logvisor::Fatal, "Unable to resolve song ID %d, no database present", id.id);
std::string_view name = SFXId::CurNameDB->resolveNameFromId(id);
w.writeString(nullptr, name);
}
template <athena::Endian DNAE>
const char* SFXIdDNA<DNAE>::DNAType()
{
return "amuse::SFXId";
}
ObjectId NameDB::generateId(Type tp)
{
uint16_t upperMatch = uint16_t(tp) << 8;
uint16_t maxMatch = 0;
for (const auto& p : m_idToString)
if ((p.first & 0xff00) == upperMatch && (p.first & 0xff) >= maxMatch)
maxMatch = (p.first & 0xff) + 1;
return upperMatch | maxMatch;
}
std::string NameDB::generateName(ObjectId id)
{
Type tp = Type(id.id >> 8);
char name[32];
switch (tp)
{
case Type::SoundMacro:
snprintf(name, 32, "macro%d", id.id & 0xff);
break;
case Type::Table:
snprintf(name, 32, "table%d", id.id & 0xff);
break;
case Type::Keymap:
snprintf(name, 32, "keymap%d", id.id & 0xff);
break;
case Type::Layer:
snprintf(name, 32, "layers%d", id.id & 0xff);
break;
default:
snprintf(name, 32, "obj%04X", id.id);
break;
}
return name;
}
std::string_view NameDB::registerPair(std::string_view str, ObjectId id)
{
m_stringToId[std::string(str)] = id;
return m_idToString.insert(std::make_pair(id, str)).first->second;
}
std::string_view NameDB::resolveNameFromId(ObjectId id) const
{
auto search = m_idToString.find(id);
if (search == m_idToString.cend())
Log.report(logvisor::Fatal, "Unable to resolve ID %d", id.id);
return search->second;
}
ObjectId NameDB::resolveIdFromName(std::string_view str) const
{
auto search = m_stringToId.find(std::string(str));
if (search == m_stringToId.cend())
Log.report(logvisor::Fatal, "Unable to resolve name %s", str.data());
return search->second;
}
template struct ObjectIdDNA<athena::Big>;
template struct ObjectIdDNA<athena::Little>;
template struct SampleIdDNA<athena::Big>;
template struct SampleIdDNA<athena::Little>;
template<>
void LittleUInt24::Enumerate<LittleDNA::Read>(athena::io::IStreamReader& reader)
{
union
{
atUint32 val;
char bytes[4];
} data = {};
reader.readBytesToBuf(data.bytes, 3);
val = SLittle(data.val);
}
template<>
void LittleUInt24::Enumerate<LittleDNA::Write>(athena::io::IStreamWriter& writer)
{
union
{
atUint32 val;
char bytes[4];
} data;
data.val = SLittle(val);
writer.writeBytes(data.bytes, 3);
}
template<>
void LittleUInt24::Enumerate<LittleDNA::BinarySize>(size_t& sz)
{
sz += 3;
}
template<>
void LittleUInt24::Enumerate<LittleDNA::ReadYaml>(athena::io::YAMLDocReader& reader)
{
val = reader.readUint32(nullptr);
}
template<>
void LittleUInt24::Enumerate<LittleDNA::WriteYaml>(athena::io::YAMLDocWriter& writer)
{
writer.writeUint32(nullptr, val);
}
const char* LittleUInt24::DNAType()
{
return "amuse::LittleUInt24";
}
}

View File

@ -1960,6 +1960,9 @@ std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> ContainerRegistry:
{ {
std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> ret; std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> ret;
const SystemChar* sep = std::max(StrRChr(path, _S('/')), StrRChr(path, _S('\\')));
SystemString baseName(sep + 1, dot - sep - 1);
/* Project */ /* Project */
SystemChar projPath[1024]; SystemChar projPath[1024];
SNPrintf(projPath, 1024, _S("%.*s.pro"), int(dot - path), path); SNPrintf(projPath, 1024, _S("%.*s.pro"), int(dot - path), path);
@ -2044,15 +2047,15 @@ std::vector<std::pair<SystemString, IntrusiveAudioGroupData>> ContainerRegistry:
/* SDIR-based format detection */ /* SDIR-based format detection */
if (*reinterpret_cast<uint32_t*>(sdir.get() + 8) == 0x0) if (*reinterpret_cast<uint32_t*>(sdir.get() + 8) == 0x0)
ret.emplace_back(_S("Group"), ret.emplace_back(baseName,
IntrusiveAudioGroupData{proj.release(), projLen, pool.release(), poolLen, sdir.release(), IntrusiveAudioGroupData{proj.release(), projLen, pool.release(), poolLen, sdir.release(),
sdirLen, samp.release(), sampLen, GCNDataTag{}}); sdirLen, samp.release(), sampLen, GCNDataTag{}});
else if (sdir[9] == 0x0) else if (sdir[9] == 0x0)
ret.emplace_back(_S("Group"), ret.emplace_back(baseName,
IntrusiveAudioGroupData{proj.release(), projLen, pool.release(), poolLen, sdir.release(), IntrusiveAudioGroupData{proj.release(), projLen, pool.release(), poolLen, sdir.release(),
sdirLen, samp.release(), sampLen, false, N64DataTag{}}); sdirLen, samp.release(), sampLen, false, N64DataTag{}});
else else
ret.emplace_back(_S("Group"), ret.emplace_back(baseName,
IntrusiveAudioGroupData{proj.release(), projLen, pool.release(), poolLen, sdir.release(), IntrusiveAudioGroupData{proj.release(), projLen, pool.release(), poolLen, sdir.release(),
sdirLen, samp.release(), sampLen, false, PCDataTag{}}); sdirLen, samp.release(), sampLen, false, PCDataTag{}});

View File

@ -203,7 +203,7 @@ AudioGroup* Engine::_addAudioGroup(const AudioGroupData& data, std::unique_ptr<A
const SFXGroupIndex& sfxGroup = grp.second; const SFXGroupIndex& sfxGroup = grp.second;
m_sfxLookup.reserve(m_sfxLookup.size() + sfxGroup.m_sfxEntries.size()); m_sfxLookup.reserve(m_sfxLookup.size() + sfxGroup.m_sfxEntries.size());
for (const auto& ent : sfxGroup.m_sfxEntries) for (const auto& ent : sfxGroup.m_sfxEntries)
m_sfxLookup[ent.first] = std::make_tuple(ret, grp.first, ent.second); m_sfxLookup[ent.first] = std::make_tuple(ret, grp.first, &ent.second);
} }
return ret; return ret;
@ -307,8 +307,7 @@ std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, std::wea
std::list<std::shared_ptr<Voice>>::iterator ret = std::list<std::shared_ptr<Voice>>::iterator ret =
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, false, smx); _allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, false, smx);
ObjectId oid = (grp->getDataFormat() == DataFormat::PC) ? entry->objId : SBig(entry->objId); if (!(*ret)->loadSoundObject(entry->objId, 0, 1000.f, entry->defKey, entry->defVel, 0))
if (!(*ret)->loadSoundObject(oid, 0, 1000.f, entry->defKey, entry->defVel, 0))
{ {
_destroyVoice(ret); _destroyVoice(ret);
return {}; return {};
@ -336,8 +335,7 @@ std::shared_ptr<Emitter> Engine::addEmitter(const float* pos, const float* dir,
std::list<std::shared_ptr<Voice>>::iterator vox = std::list<std::shared_ptr<Voice>>::iterator vox =
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, true, smx); _allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, true, smx);
ObjectId oid = (grp->getDataFormat() == DataFormat::PC) ? entry->objId : SBig(entry->objId); if (!(*vox)->loadSoundObject(entry->objId, 0, 1000.f, entry->defKey, entry->defVel, 0))
if (!(*vox)->loadSoundObject(oid, 0, 1000.f, entry->defKey, entry->defVel, 0))
{ {
_destroyVoice(vox); _destroyVoice(vox);
return {}; return {};

View File

@ -63,7 +63,7 @@ Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const
{ {
auto it = m_songGroup->m_midiSetups.find(setupId); auto it = m_songGroup->m_midiSetups.find(setupId);
if (it != m_songGroup->m_midiSetups.cend()) if (it != m_songGroup->m_midiSetups.cend())
m_midiSetup = it->second->data(); m_midiSetup = it->second.data();
} }
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SFXGroupIndex* sfxGroup, Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SFXGroupIndex* sfxGroup,
@ -72,7 +72,7 @@ Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const
{ {
std::map<uint16_t, const SFXGroupIndex::SFXEntry*> sortSFX; std::map<uint16_t, const SFXGroupIndex::SFXEntry*> sortSFX;
for (const auto& sfx : sfxGroup->m_sfxEntries) for (const auto& sfx : sfxGroup->m_sfxEntries)
sortSFX[sfx.first] = sfx.second; sortSFX[sfx.first] = &sfx.second;
m_sfxMappings.reserve(sortSFX.size()); m_sfxMappings.reserve(sortSFX.size());
for (const auto& sfx : sortSFX) for (const auto& sfx : sortSFX)
@ -95,7 +95,7 @@ Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId)
auto it = m_parent->m_songGroup->m_drumPages.find(m_setup->programNo); auto it = m_parent->m_songGroup->m_drumPages.find(m_setup->programNo);
if (it != m_parent->m_songGroup->m_drumPages.cend()) if (it != m_parent->m_songGroup->m_drumPages.cend())
{ {
m_page = it->second; m_page = &it->second;
m_curProgram = m_setup->programNo; m_curProgram = m_setup->programNo;
} }
} }
@ -104,7 +104,7 @@ Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId)
auto it = m_parent->m_songGroup->m_normPages.find(m_setup->programNo); auto it = m_parent->m_songGroup->m_normPages.find(m_setup->programNo);
if (it != m_parent->m_songGroup->m_normPages.cend()) if (it != m_parent->m_songGroup->m_normPages.cend())
{ {
m_page = it->second; m_page = &it->second;
m_curProgram = m_setup->programNo; m_curProgram = m_setup->programNo;
} }
} }
@ -120,13 +120,13 @@ Sequencer::ChannelState::ChannelState(Sequencer& parent, uint8_t chanId)
{ {
auto it = m_parent->m_songGroup->m_drumPages.find(0); auto it = m_parent->m_songGroup->m_drumPages.find(0);
if (it != m_parent->m_songGroup->m_drumPages.cend()) if (it != m_parent->m_songGroup->m_drumPages.cend())
m_page = it->second; m_page = &it->second;
} }
else else
{ {
auto it = m_parent->m_songGroup->m_normPages.find(0); auto it = m_parent->m_songGroup->m_normPages.find(0);
if (it != m_parent->m_songGroup->m_normPages.cend()) if (it != m_parent->m_songGroup->m_normPages.cend())
m_page = it->second; m_page = &it->second;
} }
m_curVol = 1.f; m_curVol = 1.f;
@ -248,12 +248,12 @@ std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo
ObjectId oid; ObjectId oid;
if (m_parent->m_songGroup) if (m_parent->m_songGroup)
oid = (m_parent->m_audioGroup.getDataFormat() == DataFormat::PC) ? m_page->objId : SBig(m_page->objId); oid = m_page->objId;
else if (m_parent->m_sfxMappings.size()) else if (m_parent->m_sfxMappings.size())
{ {
size_t lookupIdx = note % m_parent->m_sfxMappings.size(); size_t lookupIdx = note % m_parent->m_sfxMappings.size();
const SFXGroupIndex::SFXEntry* sfxEntry = m_parent->m_sfxMappings[lookupIdx]; const SFXGroupIndex::SFXEntry* sfxEntry = m_parent->m_sfxMappings[lookupIdx];
oid = (m_parent->m_audioGroup.getDataFormat() == DataFormat::PC) ? sfxEntry->objId : SBig(sfxEntry->objId); oid = sfxEntry->objId;
note = sfxEntry->defKey; note = sfxEntry->defKey;
} }
else else
@ -341,7 +341,7 @@ bool Sequencer::ChannelState::programChange(int8_t prog)
auto it = m_parent->m_songGroup->m_drumPages.find(prog); auto it = m_parent->m_songGroup->m_drumPages.find(prog);
if (it != m_parent->m_songGroup->m_drumPages.cend()) if (it != m_parent->m_songGroup->m_drumPages.cend())
{ {
m_page = it->second; m_page = &it->second;
m_curProgram = prog; m_curProgram = prog;
return true; return true;
} }
@ -351,7 +351,7 @@ bool Sequencer::ChannelState::programChange(int8_t prog)
auto it = m_parent->m_songGroup->m_normPages.find(prog); auto it = m_parent->m_songGroup->m_normPages.find(prog);
if (it != m_parent->m_songGroup->m_normPages.cend()) if (it != m_parent->m_songGroup->m_normPages.cend())
{ {
m_page = it->second; m_page = &it->second;
m_curProgram = prog; m_curProgram = prog;
return true; return true;
} }

File diff suppressed because it is too large Load Diff

View File

@ -43,10 +43,9 @@ void Voice::_macroSampleEnd()
{ {
if (m_sampleEndTrap.macroId != 0xffff) if (m_sampleEndTrap.macroId != 0xffff)
{ {
if (m_sampleEndTrap.macroId == m_state.m_header.m_macroId) if (m_sampleEndTrap.macroId == std::get<0>(m_state.m_pc.back()))
{ {
m_state.m_pc.back().second = SoundMacroState::_assertPC(m_sampleEndTrap.macroStep, std::get<2>(m_state.m_pc.back()) = std::get<1>(m_state.m_pc.back())->assertPC(m_sampleEndTrap.macroStep);
m_state.m_header.m_size);
m_state.m_inWait = false; m_state.m_inWait = false;
} }
else else
@ -238,7 +237,7 @@ void Voice::_procSamplePre(int16_t& samp)
float end = m_envelopeEnd; float end = m_envelopeEnd;
float t = clamp(0.f, float(m_envelopeTime / m_envelopeDur), 1.f); float t = clamp(0.f, float(m_envelopeTime / m_envelopeDur), 1.f);
if (m_envelopeCurve) if (m_envelopeCurve)
t = (*m_envelopeCurve)[int(t * 127.f)] / 127.f; t = m_envelopeCurve->data.at(t * 127.f) / 127.f;
m_curVol = clamp(0.f, (start * (1.0f - t)) + (end * t), 1.f); m_curVol = clamp(0.f, (start * (1.0f - t)) + (end * t), 1.f);
// printf("%d %f\n", m_vid, m_curVol); // printf("%d %f\n", m_vid, m_curVol);
@ -752,22 +751,16 @@ std::shared_ptr<Voice> Voice::startChildMacro(int8_t addNote, ObjectId macroId,
m_state.m_initMod); m_state.m_initMod);
} }
bool Voice::_loadSoundMacro(const unsigned char* macroData, int macroStep, double ticksPerSec, uint8_t midiKey, bool Voice::_loadSoundMacro(ObjectId id, const SoundMacro* macroData, int macroStep, double ticksPerSec,
uint8_t midiVel, uint8_t midiMod, bool pushPc) uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc)
{ {
if (m_state.m_pc.empty()) if (m_state.m_pc.empty())
m_state.initialize(macroData, macroStep, ticksPerSec, midiKey, midiVel, midiMod, m_state.initialize(id, macroData, macroStep, ticksPerSec, midiKey, midiVel, midiMod);
m_audioGroup.getDataFormat() != DataFormat::PC);
else else
{ {
if (!pushPc) if (!pushPc)
m_state.m_pc.clear(); m_state.m_pc.clear();
const SoundMacroState::Header& header = reinterpret_cast<const SoundMacroState::Header&>(macroData); m_state.m_pc.emplace_back(id, macroData, macroData->assertPC(macroStep));
const bool swapData = m_audioGroup.getDataFormat() != DataFormat::PC;
m_state.m_pc.push_back({macroData, SoundMacroState::_assertPC(macroStep, header.m_size, swapData)});
m_state.m_header = header;
if (swapData)
m_state.m_header.swapBig();
} }
m_voxState = VoiceState::Playing; m_voxState = VoiceState::Playing;
@ -775,46 +768,43 @@ bool Voice::_loadSoundMacro(const unsigned char* macroData, int macroStep, doubl
return true; return true;
} }
bool Voice::_loadKeymap(const Keymap* keymap, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, bool Voice::_loadKeymap(ObjectId id, const Keymap* keymap, int macroStep, double ticksPerSec,
uint8_t midiMod, bool pushPc) uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc)
{ {
const Keymap& km = keymap[midiKey]; const Keymap& km = keymap[midiKey];
ObjectId oid = (m_audioGroup.getDataFormat() == DataFormat::PC) ? km.objectId : SBig(km.objectId);
midiKey += km.transpose; midiKey += km.transpose;
bool ret = loadSoundObject(oid, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc); bool ret = loadSoundObject(id, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc);
m_curVol = 1.f; m_curVol = 1.f;
_setPan((km.pan - 64) / 64.f); _setPan((km.pan - 64) / 64.f);
_setSurroundPan(-1.f); _setSurroundPan(-1.f);
return ret; return ret;
} }
bool Voice::_loadLayer(const std::vector<const LayerMapping*>& layer, int macroStep, double ticksPerSec, bool Voice::_loadLayer(ObjectId id, const std::vector<LayerMapping>& layer, int macroStep, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc) uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc)
{ {
bool ret = false; bool ret = false;
for (const LayerMapping* mapping : layer) for (const LayerMapping& mapping : layer)
{ {
if (midiKey >= mapping->keyLo && midiKey <= mapping->keyHi) if (midiKey >= mapping.keyLo && midiKey <= mapping.keyHi)
{ {
ObjectId oid = uint8_t mappingKey = midiKey + mapping.transpose;
(m_audioGroup.getDataFormat() == DataFormat::PC) ? mapping->objectId : SBig(mapping->objectId);
uint8_t mappingKey = midiKey + mapping->transpose;
if (m_voxState != VoiceState::Playing) if (m_voxState != VoiceState::Playing)
{ {
ret |= loadSoundObject(oid, macroStep, ticksPerSec, mappingKey, midiVel, midiMod, pushPc); ret |= loadSoundObject(id, macroStep, ticksPerSec, mappingKey, midiVel, midiMod, pushPc);
m_curVol = mapping->volume / 127.f; m_curVol = mapping.volume / 127.f;
_setPan((mapping->pan - 64) / 64.f); _setPan((mapping.pan - 64) / 64.f);
_setSurroundPan((mapping->span - 64) / 64.f); _setSurroundPan((mapping.span - 64) / 64.f);
} }
else else
{ {
std::shared_ptr<Voice> vox = std::shared_ptr<Voice> vox =
_startChildMacro(oid, macroStep, ticksPerSec, mappingKey, midiVel, midiMod, pushPc); _startChildMacro(id, macroStep, ticksPerSec, mappingKey, midiVel, midiMod, pushPc);
if (vox) if (vox)
{ {
vox->m_curVol = mapping->volume / 127.f; vox->m_curVol = mapping.volume / 127.f;
vox->_setPan((mapping->pan - 64) / 64.f); vox->_setPan((mapping.pan - 64) / 64.f);
vox->_setSurroundPan((mapping->span - 64) / 64.f); vox->_setSurroundPan((mapping.span - 64) / 64.f);
ret = true; ret = true;
} }
} }
@ -829,25 +819,25 @@ bool Voice::loadSoundObject(ObjectId objectId, int macroStep, double ticksPerSec
if (m_destroyed) if (m_destroyed)
return false; return false;
const unsigned char* macroData = m_audioGroup.getPool().soundMacro(objectId); const SoundMacro* macroData = m_audioGroup.getPool().soundMacro(objectId);
if (macroData) if (macroData)
{ {
m_objectId = objectId; m_objectId = objectId;
return _loadSoundMacro(macroData, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc); return _loadSoundMacro(objectId, macroData, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc);
} }
const Keymap* keymap = m_audioGroup.getPool().keymap(objectId); const Keymap* keymap = m_audioGroup.getPool().keymap(objectId);
if (keymap) if (keymap)
{ {
m_objectId = objectId; m_objectId = objectId;
return _loadKeymap(keymap, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc); return _loadKeymap(objectId, keymap, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc);
} }
const std::vector<const LayerMapping*>* layer = m_audioGroup.getPool().layer(objectId); const std::vector<LayerMapping>* layer = m_audioGroup.getPool().layer(objectId);
if (layer) if (layer)
{ {
m_objectId = objectId; m_objectId = objectId;
return _loadLayer(*layer, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc); return _loadLayer(objectId, *layer, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc);
} }
return false; return false;
@ -872,10 +862,9 @@ void Voice::keyOff()
if (m_keyoffTrap.macroId != 0xffff) if (m_keyoffTrap.macroId != 0xffff)
{ {
if (m_keyoffTrap.macroId == m_state.m_header.m_macroId) if (m_keyoffTrap.macroId == std::get<0>(m_state.m_pc.back()))
{ {
m_state.m_pc.back().second = SoundMacroState::_assertPC(m_keyoffTrap.macroStep, std::get<2>(m_state.m_pc.back()) = std::get<1>(m_state.m_pc.back())->assertPC(m_keyoffTrap.macroStep);
m_state.m_header.m_size);
m_state.m_inWait = false; m_state.m_inWait = false;
} }
else else
@ -898,9 +887,8 @@ void Voice::message(int32_t val)
if (m_messageTrap.macroId != 0xffff) if (m_messageTrap.macroId != 0xffff)
{ {
if (m_messageTrap.macroId == m_state.m_header.m_macroId) if (m_messageTrap.macroId == std::get<0>(m_state.m_pc.back()))
m_state.m_pc.back().second = SoundMacroState::_assertPC(m_messageTrap.macroStep, std::get<2>(m_state.m_pc.back()) = std::get<1>(m_state.m_pc.back())->assertPC(m_messageTrap.macroStep);
m_state.m_header.m_size);
else else
loadSoundObject(m_messageTrap.macroId, m_messageTrap.macroStep, m_state.m_ticksPerSec, m_state.m_initKey, loadSoundObject(m_messageTrap.macroId, m_messageTrap.macroStep, m_state.m_ticksPerSec, m_state.m_initKey,
m_state.m_initVel, m_state.m_initMod); m_state.m_initVel, m_state.m_initMod);