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)
endif()
atdna(atdna_AudioGroupPool.cpp include/amuse/AudioGroupPool.hpp)
atdna(atdna_AudioGroupProject.cpp include/amuse/AudioGroupProject.hpp)
set(SOURCES
lib/AudioGroup.cpp
lib/AudioGroupData.cpp
@ -38,8 +41,11 @@ set(SOURCES
lib/EffectChorus.cpp
lib/EffectDelay.cpp
lib/ContainerRegistry.cpp
lib/Common.cpp
lib/DSPCodec.cpp
lib/N64MusyXCodec.cpp)
lib/N64MusyXCodec.cpp
atdna_AudioGroupPool.cpp
atdna_AudioGroupProject.cpp)
set(HEADERS
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(Qt5Network)
find_package(Qt5Xml)
find_package(Qt5Svg)
if(WIN32)
list(APPEND PLAT_SRCS platforms/win/amuse-gui.rc platforms/win/amuse-gui.manifest)
elseif(APPLE)
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
MACOSX_PACKAGE_LOCATION Resources)
endif()
@ -26,20 +25,26 @@ list(APPEND PLAT_SRCS mainicon_qt.cpp)
QT5_ADD_RESOURCES(qrc_resources.cpp resources/resources.qrc)
add_executable(amuse-gui WIN32 MACOSX_BUNDLE
Common.hpp Common.cpp
MainWindow.ui MainWindow.hpp MainWindow.cpp
KeyboardWidget.hpp KeyboardWidget.cpp
StatusBarWidget.hpp StatusBarWidget.cpp
ProjectModel.hpp ProjectModel.cpp
ProjectStatistics.hpp ProjectStatistics.cpp
EditorWidget.hpp EditorWidget.cpp
SoundMacroEditor.hpp SoundMacroEditor.cpp
KeymapEditor.hpp KeymapEditor.cpp
LayersEditor.hpp LayersEditor.cpp
SampleEditor.hpp SampleEditor.cpp
SoundGroupEditor.hpp SoundGroupEditor.cpp
SFXGroupEditor.hpp SFXGroupEditor.cpp
SongGroupEditor.hpp SongGroupEditor.cpp
AudioGroupModel.hpp AudioGroupModel.cpp
resources/resources.qrc qrc_resources.cpp
${PLAT_SRCS}
main.cpp)
if(COMMAND add_sanitizers)
add_sanitizers(amuse-gui)
endif()
set_target_properties(amuse-gui PROPERTIES
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}
${Qt5Network_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"
KeymapEditor::KeymapEditor(QWidget* parent)
: QWidget(parent)
: EditorWidget(parent)
{
}

View File

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

View File

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

View File

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

View File

@ -1,10 +1,398 @@
#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)
: 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
#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 {
class MainWindow;
}
class AudioGroupModel;
class MainWindow : public QMainWindow
{
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:
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>
</size>
</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">
<bool>true</bool>
</property>
@ -99,10 +111,28 @@
<rect>
<x>0</x>
<y>0</y>
<width>538</width>
<height>450</height>
<width>540</width>
<height>442</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
<widget class="QSvgWidget" name="editorSvg" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>256</width>
<height>256</height>
</size>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<widget class="QScrollArea" name="keyboardScrollArea">
@ -124,6 +154,15 @@
<height>100</height>
</size>
</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">
<bool>true</bool>
</property>
@ -132,8 +171,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>538</width>
<height>98</height>
<width>540</width>
<height>100</height>
</rect>
</property>
<property name="maximumSize">
@ -155,23 +194,23 @@
<x>0</x>
<y>0</y>
<width>800</width>
<height>22</height>
<height>27</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
<string>Fi&amp;le</string>
</property>
<addaction name="actionNew_Project"/>
<addaction name="actionOpen_Project"/>
<addaction name="actionImport_Project"/>
<addaction name="separator"/>
<addaction name="actionImport"/>
<addaction name="actionExport_GameCube_Groups"/>
</widget>
<widget class="QMenu" name="menuProject">
<property name="title">
<string>Project</string>
<string>P&amp;roject</string>
</property>
<addaction name="actionNew_Sound_Group"/>
<addaction name="actionNew_SFX_Group"/>
<addaction name="actionNew_Song_Group"/>
<addaction name="separator"/>
<addaction name="actionNew_Sound_Macro"/>
@ -180,7 +219,7 @@
</widget>
<widget class="QMenu" name="menuAudio">
<property name="title">
<string>Audio</string>
<string>A&amp;udio</string>
</property>
<addaction name="actionAuto_Play"/>
<addaction name="separator"/>
@ -188,7 +227,7 @@
</widget>
<widget class="QMenu" name="menuMIDI">
<property name="title">
<string>MIDI</string>
<string>&amp;MIDI</string>
</property>
<addaction name="separator"/>
<addaction name="actionInput_Device"/>
@ -214,129 +253,128 @@
<widget class="StatusBarWidget" name="statusbar"/>
<action name="actionNew_Project">
<property name="text">
<string>New Project</string>
</property>
<property name="shortcut">
<string>Ctrl+N</string>
<string>&amp;New Project</string>
</property>
</action>
<action name="actionOpen_Project">
<property name="text">
<string>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>
<string>&amp;Open Project</string>
</property>
</action>
<action name="actionUndo">
<property name="text">
<string>Undo</string>
<property name="enabled">
<bool>false</bool>
</property>
<property name="shortcut">
<string>Ctrl+Z</string>
<property name="text">
<string>&amp;Undo</string>
</property>
</action>
<action name="actionRedo">
<property name="text">
<string>Redo</string>
<property name="enabled">
<bool>false</bool>
</property>
<property name="shortcut">
<string>Ctrl+Shift+Z</string>
<property name="text">
<string>&amp;Redo</string>
</property>
</action>
<action name="actionCut">
<property name="text">
<string>Cut</string>
<property name="enabled">
<bool>false</bool>
</property>
<property name="shortcut">
<string>Ctrl+X</string>
<property name="text">
<string>&amp;Cut</string>
</property>
</action>
<action name="actionCopy">
<property name="text">
<string>Copy</string>
<property name="enabled">
<bool>false</bool>
</property>
<property name="shortcut">
<string>Ctrl+C</string>
<property name="text">
<string>C&amp;opy</string>
</property>
</action>
<action name="actionPaste">
<property name="text">
<string>Paste</string>
<property name="enabled">
<bool>false</bool>
</property>
<property name="shortcut">
<string>Ctrl+V</string>
<property name="text">
<string>&amp;Paste</string>
</property>
</action>
<action name="actionDelete">
<property name="text">
<string>Delete</string>
<property name="enabled">
<bool>false</bool>
</property>
<property name="shortcut">
<string>Del</string>
<property name="text">
<string>&amp;Delete</string>
</property>
</action>
<action name="actionImport_Project">
<action name="actionImport">
<property name="text">
<string>Import Project</string>
<string>&amp;Import</string>
</property>
<property name="shortcut">
<string>Ctrl+I</string>
</property>
</action>
<action name="actionNew_Sound_Group">
<action name="actionNew_SFX_Group">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset>
<normaloff>:/icons/IconNewSoundGroup.svg</normaloff>:/icons/IconNewSoundGroup.svg</iconset>
</property>
<property name="text">
<string>New Sound Group</string>
<string>&amp;New SFX Group</string>
</property>
</action>
<action name="actionNew_Song_Group">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset>
<normaloff>:/icons/IconNewSongGroup.svg</normaloff>:/icons/IconNewSongGroup.svg</iconset>
</property>
<property name="text">
<string>New Song Group</string>
<string>New &amp;Song Group</string>
</property>
</action>
<action name="actionNew_Sound_Macro">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset>
<normaloff>:/icons/IconNewSoundMacro.svg</normaloff>:/icons/IconNewSoundMacro.svg</iconset>
</property>
<property name="text">
<string>New Sound Macro</string>
<string>New Sound &amp;Macro</string>
</property>
</action>
<action name="actionNew_Keymap">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset>
<normaloff>:/icons/IconNewKeymap.svg</normaloff>:/icons/IconNewKeymap.svg</iconset>
</property>
<property name="text">
<string>New Keymap</string>
<string>New &amp;Keymap</string>
</property>
</action>
<action name="actionNew_Layers">
<property name="enabled">
<bool>false</bool>
</property>
<property name="icon">
<iconset>
<normaloff>:/icons/IconNewLayers.svg</normaloff>:/icons/IconNewLayers.svg</iconset>
</property>
<property name="text">
<string>New Layers</string>
<string>New &amp;Layers</string>
</property>
</action>
<action name="actionAuto_Play">
@ -347,7 +385,7 @@
<bool>true</bool>
</property>
<property name="text">
<string>Auto-Play</string>
<string>&amp;Auto-Play</string>
</property>
</action>
<action name="actionSelect_Output_Device">
@ -355,7 +393,7 @@
<bool>false</bool>
</property>
<property name="text">
<string>Output Device:</string>
<string>&amp;Output Device:</string>
</property>
</action>
<action name="actionInput_Device">
@ -363,7 +401,18 @@
<bool>false</bool>
</property>
<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>
</action>
</widget>
@ -379,6 +428,12 @@
<extends>QStatusBar</extends>
<header>StatusBarWidget.hpp</header>
</customwidget>
<customwidget>
<class>QSvgWidget</class>
<extends>QWidget</extends>
<header location="global">QSvgWidget</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources/>
<connections/>

View File

@ -1,7 +1,163 @@
#include <athena/FileWriter.hpp>
#include <athena/FileReader.hpp>
#include "ProjectModel.hpp"
#include "Common.hpp"
#include "athena/YAMLDocWriter.hpp"
ProjectModel::ProjectModel(QObject* parent)
: QAbstractItemModel(parent)
ProjectModel::ProjectModel(const QString& path, QObject* 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
#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
{
Q_OBJECT
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"
SampleEditor::SampleEditor(QWidget* parent)
: QWidget(parent)
: EditorWidget(parent)
{
}

View File

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

View File

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

View File

@ -1,9 +1,9 @@
#ifndef 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
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"
SoundMacroEditor::SoundMacroEditor(QWidget* parent)
: QWidget(parent)
: EditorWidget(parent)
{
}

View File

@ -1,9 +1,9 @@
#ifndef 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
public:

View File

@ -2,6 +2,9 @@
#include <QApplication>
#include <QStyleFactory>
#include "MainWindow.hpp"
#include "boo/IApplication.hpp"
using namespace std::literals;
#ifdef __APPLE__
void MacOSSetDarkAppearance();
@ -28,6 +31,24 @@ static QIcon MakeAppIcon()
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[])
{
#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::Text, Qt::white);
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::Disabled, QPalette::Button, QColor(53,53,53,53));
darkPalette.setColor(QPalette::ButtonText, Qt::white);
@ -64,6 +86,9 @@ int main(int argc, char* argv[])
MacOSSetDarkAppearance();
#endif
BooInterface booApp;
boo::APP = &booApp;
MainWindow w;
w.show();
return a.exec();

View File

@ -10,13 +10,13 @@
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="1024"
height="1024"
viewBox="0 0 270.93 270.93333"
width="247.54231"
height="247.54233"
viewBox="0 0 261.98227 261.98229"
id="svg2"
version="1.1"
inkscape:version="0.92.2 5c3e80d, 2017-08-06"
sodipodi:docname="faceGrey.svg"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="FaceGrey.svg"
inkscape:export-filename="/Users/jacko/Desktop/amuse-logo/face1024.png"
inkscape:export-xdpi="96.000008"
inkscape:export-ydpi="96.000008">
@ -27,19 +27,19 @@
<stop
id="stop4353"
offset="0"
style="stop-color:#5f5f5f;stop-opacity:1;" />
style="stop-color:#858585;stop-opacity:1;" />
<stop
style="stop-color:#424242;stop-opacity:1;"
style="stop-color:#4f4f4f;stop-opacity:1;"
offset="0.27903292"
id="stop4351" />
<stop
id="stop4349"
offset="0.56446373"
style="stop-color:#4e4e4e;stop-opacity:1;" />
style="stop-color:#606060;stop-opacity:1;" />
<stop
id="stop4347"
offset="1"
style="stop-color:#212121;stop-opacity:1;" />
style="stop-color:#484848;stop-opacity:1;" />
</linearGradient>
<linearGradient
id="linearGradient4315">
@ -334,23 +334,28 @@
</defs>
<sodipodi:namedview
id="base"
pagecolor="#2a2a2a"
pagecolor="#3a3a3a"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageopacity="0.00392157"
inkscape:pageshadow="2"
inkscape:zoom="0.63234634"
inkscape:cx="247.50409"
inkscape:cy="571.18657"
inkscape:zoom="2.5293854"
inkscape:cx="57.159723"
inkscape:cy="115.45029"
inkscape:document-units="mm"
inkscape:current-layer="layer3"
showgrid="false"
units="px"
inkscape:window-width="1680"
inkscape:window-height="1005"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="0" />
inkscape:window-width="1555"
inkscape:window-height="1280"
inkscape:window-x="1890"
inkscape:window-y="286"
inkscape:window-maximized="0"
scale-x="4"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<metadata
id="metadata7">
<rdf:RDF>
@ -367,7 +372,7 @@
inkscape:label="source"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-26.06665)"
transform="translate(-4.3711946,-30.440026)"
style="display:none"
sodipodi:insensitive="true">
<text
@ -412,7 +417,8 @@
id="layer2"
inkscape:label="path"
style="display:inline;opacity:0.76700003"
sodipodi:insensitive="true">
sodipodi:insensitive="true"
transform="translate(-4.3711946,-4.3733763)">
<g
id="g4360"
style="fill:url(#linearGradient4375);fill-opacity:1"
@ -453,7 +459,8 @@
id="layer5"
inkscape:label="path 1"
style="display:inline;opacity:0.24734982"
sodipodi:insensitive="true">
sodipodi:insensitive="true"
transform="translate(-4.3711946,-4.3733763)">
<path
inkscape:connector-curvature="0"
id="path4439"
@ -466,7 +473,8 @@
inkscape:label="path 2"
id="g4512"
inkscape:groupmode="layer"
sodipodi:insensitive="true">
sodipodi:insensitive="true"
transform="translate(-4.3711946,-4.3733763)">
<g
transform="matrix(1.0065636,0,0,1.0065636,-0.88913164,-0.88914604)"
style="fill:url(#linearGradient4530);fill-opacity:1"
@ -507,7 +515,8 @@
inkscape:label="path 3"
id="g4538"
inkscape:groupmode="layer"
sodipodi:insensitive="true">
sodipodi:insensitive="true"
transform="translate(-4.3711946,-4.3733763)">
<g
transform="matrix(1.0065636,0,0,1.0065636,-0.88913164,-0.88914604)"
style="fill:url(#linearGradient4556);fill-opacity:1"
@ -547,19 +556,20 @@
inkscape:groupmode="layer"
id="layer3"
inkscape:label="light"
style="display:inline;opacity:1">
style="display:inline;opacity:1"
transform="translate(-4.3711946,-4.3733763)">
<g
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)">
<g
id="text4141-2-9"
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)">
<path
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"
transform="matrix(0,-0.26458333,0.26458333,0,0,0.001665)"
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>IconSoundMacro.svg</file>
</qresource>
<qresource prefix="/bg">
<file>FaceGrey.svg</file>
</qresource>
</RCC>

File diff suppressed because it is too large Load Diff

View File

@ -12,58 +12,167 @@ namespace amuse
{
class AudioGroupData;
/** Common index members of SongGroups and SFXGroups */
struct AudioGroupIndex
enum class GroupType : atUint16
{
const uint16_t* m_soundMacroIndex;
const uint16_t* m_tablesIndex;
const uint16_t* m_keymapsIndex;
const uint16_t* m_layersIndex;
Song,
SFX
};
/** 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 */
struct SongGroupIndex : AudioGroupIndex
{
/** 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
{
ObjectId objId;
uint8_t priority;
uint8_t maxVoices;
uint8_t programNo;
uint8_t pad;
atUint8 priority;
atUint8 maxVoices;
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, const PageEntry*> m_drumPages;
std::unordered_map<uint8_t, PageEntry> m_normPages;
std::unordered_map<uint8_t, PageEntry> m_drumPages;
/** Maps SongID to 16 MIDI channel numbers to GM program numbers and settings */
struct MIDISetup
struct MusyX1MIDISetup : BigDNA
{
uint8_t programNo;
uint8_t volume;
uint8_t panning;
uint8_t reverb;
uint8_t chorus;
AT_DECL_DNA_YAML
Value<atUint8> programNo;
Value<atUint8> volume;
Value<atUint8> panning;
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 */
struct SFXGroupIndex : AudioGroupIndex
{
/** 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
{
uint16_t defineId;
ObjectId objId;
uint8_t priority;
uint8_t maxVoices;
uint8_t defVel;
uint8_t panning;
uint8_t defKey;
uint8_t pad;
atUint8 priority;
atUint8 maxVoices;
atUint8 defVel;
atUint8 panning;
atUint8 defKey;
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 */
@ -72,17 +181,11 @@ class AudioGroupProject
std::unordered_map<int, SongGroupIndex> m_songGroups;
std::unordered_map<int, SFXGroupIndex> m_sfxGroups;
/* MusyX 1.0 structures converted to MusyX 2.0 structures for pointer-compatible access */
std::unique_ptr<SongGroupIndex::PageEntry[]> m_convNormalPages;
std::unique_ptr<SongGroupIndex::PageEntry[]> m_convDrumPages;
std::unique_ptr<std::array<SongGroupIndex::MIDISetup, 16>[]> m_convMidiSetups;
void _allocateConvBuffers(const unsigned char* data, N64DataTag);
void _allocateConvBuffers(const unsigned char* data, PCDataTag);
AudioGroupProject() = default;
AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag);
template <athena::Endian DNAE>
static AudioGroupProject _AudioGroupProject(athena::io::IStreamReader& r, bool absOffs);
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);
const SongGroupIndex* getSongGroupIndex(int groupId) const;

View File

@ -7,6 +7,7 @@
namespace amuse
{
class AudioGroupData;
/** Indexes individual samples in SAMP chunk */
class AudioGroupSampleDirectory
@ -52,6 +53,7 @@ public:
AudioGroupSampleDirectory(const unsigned char* data, GCNDataTag);
AudioGroupSampleDirectory(const unsigned char* data, const unsigned char* sampData, bool absOffs, N64DataTag);
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; }
};

View File

@ -9,6 +9,7 @@
#include <string>
#include <string_view>
#include <cstring>
#include "athena/DNA.hpp"
#ifndef _WIN32
#include <strings.h>
@ -28,6 +29,95 @@ constexpr float NativeSampleRate = 32000.0f;
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
#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__

View File

@ -4,23 +4,20 @@
#include <cstdint>
#include <functional>
#include <cassert>
#include "Common.hpp"
namespace amuse
{
class Engine;
class AudioGroup;
/** Common ID structure statically tagging
* SoundMacros, Tables, Keymaps, Layers */
using ObjectId = uint16_t;
/** Common 'engine child' class */
class Entity
{
/* Only the Engine will manage Entity lifetimes,
* but shared_ptrs are issued to the client so it can safely track state */
friend class Engine;
friend class SoundMacroState;
friend struct SoundMacroState;
protected:
bool m_destroyed = false;
@ -32,7 +29,7 @@ protected:
Engine& m_engine;
const AudioGroup& m_audioGroup;
int m_groupId;
ObjectId m_objectId = 0xffff; /* if applicable */
ObjectId m_objectId; /* if applicable */
public:
Entity(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid = ObjectId())
: m_engine(engine), m_audioGroup(group), m_groupId(groupId), m_objectId(oid)
@ -50,9 +47,6 @@ public:
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__

View File

@ -6,6 +6,7 @@
#include <list>
#include "Entity.hpp"
#include "Common.hpp"
#include "AudioGroupPool.hpp"
/* Squelch Win32 macro pollution >.< */
#undef SendMessage
@ -16,123 +17,13 @@ namespace amuse
class Voice;
/** 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 */
std::vector<std::pair<const unsigned char*, 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);
}
std::vector<std::tuple<ObjectId, const SoundMacro*, int>> m_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 */
@ -233,9 +124,9 @@ class SoundMacroState
public:
/** initialize state for SoundMacro data at `ptr` */
void initialize(const unsigned char* ptr, int step, bool swapData);
void initialize(const unsigned char* ptr, int step, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
uint8_t midiMod, bool swapData);
void initialize(ObjectId id, const SoundMacro* macro, int step);
void initialize(ObjectId id, const SoundMacro* macro, int step, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod);
/** advances `dt` seconds worth of commands in the SoundMacro
* @return `true` if END reached

View File

@ -31,9 +31,20 @@ class Voice : public Entity
{
friend class Engine;
friend class Sequencer;
friend class SoundMacroState;
friend struct SoundMacroState;
friend class Envelope;
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 */
bool m_emitter; /**< Voice is part of an Emitter */
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 _destroyVoice(std::list<std::shared_ptr<Voice>>::iterator it);
bool _loadSoundMacro(const unsigned char* macroData, int macroStep, double ticksPerSec, 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,
uint8_t midiMod, bool pushPc = false);
bool _loadLayer(const std::vector<const LayerMapping*>& layer, int macroStep, double ticksPerSec, uint8_t midiKey,
bool _loadSoundMacro(ObjectId id, const SoundMacro* macroData, int macroStep, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
bool _loadKeymap(ObjectId id, const Keymap* keymap, int macroStep, double ticksPerSec, uint8_t midiKey,
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,
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);

View File

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

View File

@ -1,152 +1,326 @@
#include "amuse/AudioGroupPool.hpp"
#include "amuse/Common.hpp"
#include "amuse/Entity.hpp"
#include "amuse/AudioGroupData.hpp"
#include "athena/MemoryReader.hpp"
#include "logvisor/logvisor.hpp"
namespace amuse
{
static logvisor::Module Log("amuse::AudioGroupPool");
struct Header
static bool AtEnd(athena::io::IStreamReader& r)
{
uint32_t soundMacrosOffset;
uint32_t tablesOffset;
uint32_t keymapsOffset;
uint32_t layersOffset;
void swapBig()
{
soundMacrosOffset = SBig(soundMacrosOffset);
tablesOffset = SBig(tablesOffset);
keymapsOffset = SBig(keymapsOffset);
layersOffset = SBig(layersOffset);
uint32_t v = r.readUint32Big();
r.seek(-4, athena::Current);
return v == 0xffffffff;
}
};
AudioGroupPool::AudioGroupPool(const unsigned char* data)
template <athena::Endian DNAE>
AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r)
{
Header head = *reinterpret_cast<const Header*>(data);
head.swapBig();
AudioGroupPool ret;
PoolHeader<DNAE> head;
head.read(r);
if (head.soundMacrosOffset)
{
const unsigned char* cur = data + head.soundMacrosOffset;
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff)
r.seek(head.soundMacrosOffset, athena::Begin);
while (!AtEnd(r))
{
uint32_t size = SBig(*reinterpret_cast<const uint32_t*>(cur));
ObjectId id = SBig(*reinterpret_cast<const ObjectId*>(cur + 4));
m_soundMacros[id] = cur;
cur += size;
ObjectHeader<DNAE> objHead;
atInt64 startPos = r.position();
objHead.read(r);
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)
{
const unsigned char* cur = data + head.tablesOffset;
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff)
r.seek(head.tablesOffset, athena::Begin);
while (!AtEnd(r))
{
uint32_t size = SBig(*reinterpret_cast<const uint32_t*>(cur));
ObjectId id = SBig(*reinterpret_cast<const ObjectId*>(cur + 4));
m_tables[id] = cur + 8;
cur += size;
ObjectHeader<DNAE> objHead;
atInt64 startPos = r.position();
objHead.read(r);
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)
{
const unsigned char* cur = data + head.keymapsOffset;
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff)
r.seek(head.keymapsOffset, athena::Begin);
while (!AtEnd(r))
{
uint32_t size = SBig(*reinterpret_cast<const uint32_t*>(cur));
ObjectId id = SBig(*reinterpret_cast<const ObjectId*>(cur + 4));
m_keymaps[id] = reinterpret_cast<const Keymap*>(cur + 8);
cur += size;
ObjectHeader<DNAE> objHead;
atInt64 startPos = r.position();
objHead.read(r);
Keymap& km = ret.m_keymaps[objHead.objectId.id];
km.read(r);
r.seek(startPos + objHead.size, athena::Begin);
}
}
if (head.layersOffset)
{
const unsigned char* cur = data + head.layersOffset;
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff)
r.seek(head.layersOffset, athena::Begin);
while (!AtEnd(r))
{
uint32_t size = SBig(*reinterpret_cast<const uint32_t*>(cur));
ObjectId id = SBig(*reinterpret_cast<const ObjectId*>(cur + 4));
std::vector<const LayerMapping*>& mappingsOut = m_layers[id];
uint32_t count = SBig(*reinterpret_cast<const uint32_t*>(cur + 8));
mappingsOut.reserve(count);
const unsigned char* subcur = cur + 12;
ObjectHeader<DNAE> objHead;
atInt64 startPos = r.position();
objHead.read(r);
std::vector<LayerMapping>& lm = ret.m_layers[objHead.objectId.id];
uint32_t count;
athena::io::Read<athena::io::PropType::None>::Do<decltype(count), DNAE>({}, count, r);
lm.reserve(count);
for (uint32_t i = 0; i < count; ++i)
mappingsOut.push_back(reinterpret_cast<const LayerMapping*>(subcur + i * 12));
cur += size;
{
lm.emplace_back();
lm.back().read(r);
}
r.seek(startPos + objHead.size, athena::Begin);
}
}
AudioGroupPool::AudioGroupPool(const unsigned char* data, PCDataTag)
{
const Header* head = reinterpret_cast<const Header*>(data);
return ret;
}
template AudioGroupPool AudioGroupPool::_AudioGroupPool<athena::Big>(athena::io::IStreamReader& r);
template AudioGroupPool AudioGroupPool::_AudioGroupPool<athena::Little>(athena::io::IStreamReader& r);
if (head->soundMacrosOffset)
AudioGroupPool AudioGroupPool::CreateAudioGroupPool(const AudioGroupData& data)
{
const unsigned char* cur = data + head->soundMacrosOffset;
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff)
athena::io::MemoryReader r(data.getPool(), data.getPoolSize());
switch (data.getDataFormat())
{
uint32_t size = *reinterpret_cast<const uint32_t*>(cur);
ObjectId id = *reinterpret_cast<const ObjectId*>(cur + 4);
m_soundMacros[id] = cur;
cur += size;
case DataFormat::PC:
return _AudioGroupPool<athena::Little>(r);
default:
return _AudioGroupPool<athena::Big>(r);
}
}
if (head->tablesOffset)
template <class Tp>
static std::unique_ptr<SoundMacro::ICmd> MakeCmd(athena::io::MemoryReader& r)
{
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;
}
std::unique_ptr<SoundMacro::ICmd> ret = std::make_unique<Tp>();
static_cast<Tp&>(*ret).read(r);
return ret;
}
if (head->keymapsOffset)
int SoundMacro::assertPC(int pc) const
{
const unsigned char* cur = data + head->keymapsOffset;
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff)
if (pc == -1)
return -1;
if (pc >= m_cmds.size())
{
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;
fprintf(stderr, "SoundMacro PC bounds exceeded [%d/%d]\n", pc, int(m_cmds.size()));
abort();
}
return pc;
}
if (head->layersOffset)
template <athena::Endian DNAE>
void SoundMacro::readCmds(athena::io::IStreamReader& r, uint32_t size)
{
const unsigned char* cur = data + head->layersOffset;
while (*reinterpret_cast<const uint32_t*>(cur) != 0xffffffff)
uint32_t numCmds = size / 8;
m_cmds.reserve(numCmds);
for (int i = 0; i < numCmds; ++i)
{
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;
}
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 unsigned char* AudioGroupPool::soundMacro(ObjectId id) const
const SoundMacro* AudioGroupPool::soundMacro(ObjectId id) const
{
auto search = m_soundMacros.find(id);
if (search == m_soundMacros.cend())
return nullptr;
return search->second;
return &search->second;
}
const Keymap* AudioGroupPool::keymap(ObjectId id) const
@ -154,10 +328,10 @@ const Keymap* AudioGroupPool::keymap(ObjectId id) const
auto search = m_keymaps.find(id);
if (search == m_keymaps.cend())
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);
if (search == m_layers.cend())
@ -170,6 +344,41 @@ const ADSR* AudioGroupPool::tableAsAdsr(ObjectId id) const
auto search = m_tables.find(id);
if (search == m_tables.cend())
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/AudioGroupData.hpp"
#include "amuse/Common.hpp"
#include <stdint.h>
#include "athena/MemoryReader.hpp"
namespace amuse
{
enum class GroupType : uint16_t
static bool AtEnd32(athena::io::IStreamReader& r)
{
Song,
SFX
};
struct GroupHeader
{
uint32_t groupEndOff;
uint16_t groupId;
GroupType type;
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()
{
groupEndOff = SBig(groupEndOff);
groupId = SBig(groupId);
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);
uint32_t v = r.readUint32Big();
r.seek(-4, athena::Current);
return v == 0xffffffff;
}
};
AudioGroupProject::AudioGroupProject(const unsigned char* data, GCNDataTag)
static bool AtEnd16(athena::io::IStreamReader& r)
{
const GroupHeader* group = reinterpret_cast<const GroupHeader*>(data);
while (group->groupEndOff != 0xffffffff)
{
GroupHeader header = *group;
header.swapBig();
uint16_t v = r.readUint16Big();
r.seek(-2, athena::Current);
return v == 0xffff;
}
AudioGroupIndex* bIdx = nullptr;
AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag)
{
while (!AtEnd32(r))
{
GroupHeader<athena::Big> header;
header.read(r);
if (header.type == GroupType::Song)
{
SongGroupIndex& idx = m_songGroups[header.groupId];
bIdx = &idx;
/* Normal pages */
const SongGroupIndex::PageEntry* normEntries =
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + header.pageTableOff);
while (normEntries->objId != 0xffff)
r.seek(header.pageTableOff, athena::Begin);
while (!AtEnd16(r))
{
idx.m_normPages[normEntries->programNo] = normEntries;
++normEntries;
SongGroupIndex::PageEntryDNA<athena::Big> entry;
entry.read(r);
idx.m_normPages[entry.programNo] = entry;
}
/* Drum pages */
const SongGroupIndex::PageEntry* drumEntries =
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + header.drumTableOff);
while (drumEntries->objId != 0xffff)
r.seek(header.drumTableOff, athena::Begin);
while (!AtEnd16(r))
{
idx.m_drumPages[drumEntries->programNo] = drumEntries;
++drumEntries;
SongGroupIndex::PageEntryDNA<athena::Big> entry;
entry.read(r);
idx.m_drumPages[entry.programNo] = entry;
}
/* MIDI setups */
const uint8_t* setupData = data + header.midiSetupsOff;
const uint8_t* setupEnd = data + header.groupEndOff;
while (setupData < setupEnd)
r.seek(header.midiSetupsOff, athena::Begin);
while (r.position() < header.groupEndOff)
{
uint16_t songId = SBig(*reinterpret_cast<const uint16_t*>(setupData));
idx.m_midiSetups[songId] =
reinterpret_cast<const std::array<SongGroupIndex::MIDISetup, 16>*>(setupData + 4);
setupData += 5 * 16 + 4;
uint16_t songId = r.readUint16Big();
r.seek(2, athena::Current);
std::array<SongGroupIndex::MIDISetup, 16>& setup = idx.m_midiSetups[songId];
for (int i = 0; i < 16 ; ++i)
setup[i].read(r);
}
}
else if (header.type == GroupType::SFX)
{
SFXGroupIndex& idx = m_sfxGroups[header.groupId];
bIdx = &idx;
/* 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);
const SFXGroupIndex::SFXEntry* entries =
reinterpret_cast<const SFXGroupIndex::SFXEntry*>(data + header.pageTableOff + 4);
for (int i = 0; i < count; ++i)
{
idx.m_sfxEntries[SBig(entries->defineId)] = entries;
++entries;
SFXGroupIndex::SFXEntryDNA<athena::Big> entry;
entry.read(r);
idx.m_sfxEntries[entry.defineId.id] = entry;
}
}
if (bIdx)
{
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);
r.seek(header.groupEndOff, athena::Begin);
}
}
struct MusyX1PageEntry
template <athena::Endian DNAE>
AudioGroupProject AudioGroupProject::_AudioGroupProject(athena::io::IStreamReader& r, bool absOffs)
{
ObjectId objId;
uint8_t priority;
uint8_t maxVoices;
uint8_t unk;
uint8_t programNo;
uint8_t pad[2];
AudioGroupProject ret;
void setIntoMusyX2(SongGroupIndex::PageEntry& ent) const
while (!AtEnd32(r))
{
ent.objId = objId;
ent.priority = priority;
ent.maxVoices = maxVoices;
ent.programNo = programNo;
}
};
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();
atInt64 groupBegin = r.position();
atInt64 subDataOff = absOffs ? 0 : groupBegin + 8;
GroupHeader<DNAE> header;
header.read(r);
if (header.type == GroupType::Song)
{
/* Normal pages */
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;
SongGroupIndex& idx = ret.m_songGroups[header.groupId];
if (absOffs)
{
/* Normal pages */
const SongGroupIndex::PageEntry* normEntries =
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + header.pageTableOff);
while (normEntries->objId != 0xffff)
r.seek(header.pageTableOff, athena::Begin);
while (!AtEnd16(r))
{
idx.m_normPages[normEntries->programNo] = normEntries;
++normEntries;
SongGroupIndex::PageEntryDNA<DNAE> entry;
entry.read(r);
idx.m_normPages[entry.programNo] = entry;
}
/* Drum pages */
const SongGroupIndex::PageEntry* drumEntries =
reinterpret_cast<const SongGroupIndex::PageEntry*>(data + header.drumTableOff);
while (drumEntries->objId != 0xffff)
r.seek(header.drumTableOff, athena::Begin);
while (!AtEnd16(r))
{
idx.m_drumPages[drumEntries->programNo] = drumEntries;
++drumEntries;
SongGroupIndex::PageEntryDNA<DNAE> entry;
entry.read(r);
idx.m_drumPages[entry.programNo] = entry;
}
/* MIDI setups */
const uint8_t* setupData = data + header.midiSetupsOff;
const uint8_t* setupEnd = data + header.groupEndOff;
while (setupData < setupEnd)
r.seek(header.midiSetupsOff, athena::Begin);
while (r.position() < header.groupEndOff)
{
uint16_t songId = SBig(*reinterpret_cast<const uint16_t*>(setupData));
idx.m_midiSetups[songId] =
reinterpret_cast<const std::array<SongGroupIndex::MIDISetup, 16>*>(setupData + 4);
setupData += 5 * 16 + 4;
uint16_t songId = r.readUint16Big();
r.seek(2, athena::Current);
std::array<SongGroupIndex::MIDISetup, 16>& setup = idx.m_midiSetups[songId];
for (int i = 0; i < 16 ; ++i)
setup[i].read(r);
}
}
else
{
/* Normal pages */
const MusyX1PageEntry* normEntries =
reinterpret_cast<const MusyX1PageEntry*>(subData + header.pageTableOff);
while (normEntries->objId != 0xffff)
r.seek(subDataOff + header.pageTableOff, athena::Begin);
while (!AtEnd16(r))
{
normEntries->setIntoMusyX2(*normPagesBuf);
idx.m_normPages[normEntries->programNo] = normPagesBuf;
++normEntries;
++normPagesBuf;
SongGroupIndex::MusyX1PageEntryDNA<DNAE> entry;
entry.read(r);
idx.m_normPages[entry.programNo] = entry;
}
/* Drum pages */
const MusyX1PageEntry* drumEntries =
reinterpret_cast<const MusyX1PageEntry*>(subData + header.drumTableOff);
while (drumEntries->objId != 0xffff)
r.seek(subDataOff + header.drumTableOff, athena::Begin);
while (!AtEnd16(r))
{
drumEntries->setIntoMusyX2(*drumPagesBuf);
idx.m_drumPages[drumEntries->programNo] = drumPagesBuf;
++drumEntries;
++drumPagesBuf;
SongGroupIndex::MusyX1PageEntryDNA<DNAE> entry;
entry.read(r);
idx.m_drumPages[entry.programNo] = entry;
}
/* MIDI setups */
const uint8_t* setupData = subData + header.midiSetupsOff;
const uint8_t* setupEnd = subData + header.groupEndOff;
while (setupData < setupEnd)
r.seek(subDataOff + header.midiSetupsOff, athena::Begin);
while (r.position() < groupBegin + header.groupEndOff)
{
uint16_t songId = SBig(*reinterpret_cast<const uint16_t*>(setupData));
const std::array<MusyX1MIDISetup, 16>* midiSetups =
reinterpret_cast<const std::array<MusyX1MIDISetup, 16>*>(setupData + 4);
uint16_t songId = r.readUint16Big();
r.seek(2, athena::Current);
std::array<SongGroupIndex::MIDISetup, 16>& setup = idx.m_midiSetups[songId];
for (int i = 0; i < 16 ; ++i)
(*midiSetups)[i].setIntoMusyX2((*midiSetupsBuf)[i]);
idx.m_midiSetups[songId] = midiSetupsBuf;
setupData += 8 * 16 + 4;
++midiSetupsBuf;
{
SongGroupIndex::MusyX1MIDISetup ent;
ent.read(r);
setup[i] = ent;
}
}
}
}
else if (header.type == GroupType::SFX)
{
SFXGroupIndex& idx = m_sfxGroups[header.groupId];
bIdx = &idx;
SFXGroupIndex& idx = ret.m_sfxGroups[header.groupId];
/* 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);
for (int i = 0; i < count; ++i)
{
const SFXGroupIndex::SFXEntry* entries =
reinterpret_cast<const SFXGroupIndex::SFXEntry*>(subData + header.pageTableOff + 4 + i * 12);
idx.m_sfxEntries[SBig(entries->defineId)] = entries;
SFXGroupIndex::SFXEntryDNA<DNAE> entry;
entry.read(r);
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)
group = reinterpret_cast<const GroupHeader*>(data + header.groupEndOff);
r.seek(header.groupEndOff, athena::Begin);
else
{
data += header.groupEndOff;
group = reinterpret_cast<const GroupHeader*>(data);
}
}
r.seek(groupBegin + header.groupEndOff, athena::Begin);
}
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 */
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);
}
}
return ret;
}
AudioGroupProject AudioGroupProject::CreateAudioGroupProject(const AudioGroupData& data)
{
athena::io::MemoryReader r(data.getProj(), data.getProjSize());
switch (data.getDataFormat())
{
case DataFormat::GCN:
default:
return AudioGroupProject(data.getProj(), GCNDataTag{});
return AudioGroupProject(r, GCNDataTag{});
case DataFormat::N64:
return AudioGroupProject(data.getProj(), data.getAbsoluteProjOffsets(), N64DataTag{});
return _AudioGroupProject<athena::Big>(r, data.getAbsoluteProjOffsets());
case DataFormat::PC:
return AudioGroupProject(data.getProj(), data.getAbsoluteProjOffsets(), PCDataTag{});
return _AudioGroupProject<athena::Little>(r, data.getAbsoluteProjOffsets());
}
}
const SongGroupIndex* AudioGroupProject::getSongGroupIndex(int groupId) const
{
auto search = m_songGroups.find(groupId);
if (search == m_songGroups.cend())
return nullptr;
if (search != m_songGroups.cend())
return &search->second;
return nullptr;
}
const SFXGroupIndex* AudioGroupProject::getSFXGroupIndex(int groupId) const
{
auto search = m_sfxGroups.find(groupId);
if (search == m_sfxGroups.cend())
return nullptr;
if (search != m_sfxGroups.cend())
return &search->second;
return nullptr;
}
}

View File

@ -1,5 +1,6 @@
#include "amuse/AudioGroupSampleDirectory.hpp"
#include "amuse/Common.hpp"
#include "amuse/AudioGroupData.hpp"
#include <cstring>
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;
const SystemChar* sep = std::max(StrRChr(path, _S('/')), StrRChr(path, _S('\\')));
SystemString baseName(sep + 1, dot - sep - 1);
/* Project */
SystemChar projPath[1024];
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 */
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(),
sdirLen, samp.release(), sampLen, GCNDataTag{}});
else if (sdir[9] == 0x0)
ret.emplace_back(_S("Group"),
ret.emplace_back(baseName,
IntrusiveAudioGroupData{proj.release(), projLen, pool.release(), poolLen, sdir.release(),
sdirLen, samp.release(), sampLen, false, N64DataTag{}});
else
ret.emplace_back(_S("Group"),
ret.emplace_back(baseName,
IntrusiveAudioGroupData{proj.release(), projLen, pool.release(), poolLen, sdir.release(),
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;
m_sfxLookup.reserve(m_sfxLookup.size() + sfxGroup.m_sfxEntries.size());
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;
@ -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 =
_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(oid, 0, 1000.f, entry->defKey, entry->defVel, 0))
if (!(*ret)->loadSoundObject(entry->objId, 0, 1000.f, entry->defKey, entry->defVel, 0))
{
_destroyVoice(ret);
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 =
_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(oid, 0, 1000.f, entry->defKey, entry->defVel, 0))
if (!(*vox)->loadSoundObject(entry->objId, 0, 1000.f, entry->defKey, entry->defVel, 0))
{
_destroyVoice(vox);
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);
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,
@ -72,7 +72,7 @@ Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const
{
std::map<uint16_t, const SFXGroupIndex::SFXEntry*> sortSFX;
for (const auto& sfx : sfxGroup->m_sfxEntries)
sortSFX[sfx.first] = sfx.second;
sortSFX[sfx.first] = &sfx.second;
m_sfxMappings.reserve(sortSFX.size());
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);
if (it != m_parent->m_songGroup->m_drumPages.cend())
{
m_page = it->second;
m_page = &it->second;
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);
if (it != m_parent->m_songGroup->m_normPages.cend())
{
m_page = it->second;
m_page = &it->second;
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);
if (it != m_parent->m_songGroup->m_drumPages.cend())
m_page = it->second;
m_page = &it->second;
}
else
{
auto it = m_parent->m_songGroup->m_normPages.find(0);
if (it != m_parent->m_songGroup->m_normPages.cend())
m_page = it->second;
m_page = &it->second;
}
m_curVol = 1.f;
@ -248,12 +248,12 @@ std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo
ObjectId oid;
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())
{
size_t lookupIdx = note % m_parent->m_sfxMappings.size();
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;
}
else
@ -341,7 +341,7 @@ bool Sequencer::ChannelState::programChange(int8_t prog)
auto it = m_parent->m_songGroup->m_drumPages.find(prog);
if (it != m_parent->m_songGroup->m_drumPages.cend())
{
m_page = it->second;
m_page = &it->second;
m_curProgram = prog;
return true;
}
@ -351,7 +351,7 @@ bool Sequencer::ChannelState::programChange(int8_t prog)
auto it = m_parent->m_songGroup->m_normPages.find(prog);
if (it != m_parent->m_songGroup->m_normPages.cend())
{
m_page = it->second;
m_page = &it->second;
m_curProgram = prog;
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 == 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,
m_state.m_header.m_size);
std::get<2>(m_state.m_pc.back()) = std::get<1>(m_state.m_pc.back())->assertPC(m_sampleEndTrap.macroStep);
m_state.m_inWait = false;
}
else
@ -238,7 +237,7 @@ void Voice::_procSamplePre(int16_t& samp)
float end = m_envelopeEnd;
float t = clamp(0.f, float(m_envelopeTime / m_envelopeDur), 1.f);
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);
// 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);
}
bool Voice::_loadSoundMacro(const unsigned char* macroData, int macroStep, double ticksPerSec, uint8_t midiKey,
uint8_t midiVel, uint8_t midiMod, bool pushPc)
bool Voice::_loadSoundMacro(ObjectId id, const SoundMacro* macroData, int macroStep, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc)
{
if (m_state.m_pc.empty())
m_state.initialize(macroData, macroStep, ticksPerSec, midiKey, midiVel, midiMod,
m_audioGroup.getDataFormat() != DataFormat::PC);
m_state.initialize(id, macroData, macroStep, ticksPerSec, midiKey, midiVel, midiMod);
else
{
if (!pushPc)
m_state.m_pc.clear();
const SoundMacroState::Header& header = reinterpret_cast<const SoundMacroState::Header&>(macroData);
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_state.m_pc.emplace_back(id, macroData, macroData->assertPC(macroStep));
}
m_voxState = VoiceState::Playing;
@ -775,46 +768,43 @@ bool Voice::_loadSoundMacro(const unsigned char* macroData, int macroStep, doubl
return true;
}
bool Voice::_loadKeymap(const Keymap* keymap, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,
uint8_t midiMod, bool pushPc)
bool Voice::_loadKeymap(ObjectId id, const Keymap* keymap, int macroStep, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc)
{
const Keymap& km = keymap[midiKey];
ObjectId oid = (m_audioGroup.getDataFormat() == DataFormat::PC) ? km.objectId : SBig(km.objectId);
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;
_setPan((km.pan - 64) / 64.f);
_setSurroundPan(-1.f);
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)
{
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 =
(m_audioGroup.getDataFormat() == DataFormat::PC) ? mapping->objectId : SBig(mapping->objectId);
uint8_t mappingKey = midiKey + mapping->transpose;
uint8_t mappingKey = midiKey + mapping.transpose;
if (m_voxState != VoiceState::Playing)
{
ret |= loadSoundObject(oid, macroStep, ticksPerSec, mappingKey, midiVel, midiMod, pushPc);
m_curVol = mapping->volume / 127.f;
_setPan((mapping->pan - 64) / 64.f);
_setSurroundPan((mapping->span - 64) / 64.f);
ret |= loadSoundObject(id, macroStep, ticksPerSec, mappingKey, midiVel, midiMod, pushPc);
m_curVol = mapping.volume / 127.f;
_setPan((mapping.pan - 64) / 64.f);
_setSurroundPan((mapping.span - 64) / 64.f);
}
else
{
std::shared_ptr<Voice> vox =
_startChildMacro(oid, macroStep, ticksPerSec, mappingKey, midiVel, midiMod, pushPc);
_startChildMacro(id, macroStep, ticksPerSec, mappingKey, midiVel, midiMod, pushPc);
if (vox)
{
vox->m_curVol = mapping->volume / 127.f;
vox->_setPan((mapping->pan - 64) / 64.f);
vox->_setSurroundPan((mapping->span - 64) / 64.f);
vox->m_curVol = mapping.volume / 127.f;
vox->_setPan((mapping.pan - 64) / 64.f);
vox->_setSurroundPan((mapping.span - 64) / 64.f);
ret = true;
}
}
@ -829,25 +819,25 @@ bool Voice::loadSoundObject(ObjectId objectId, int macroStep, double ticksPerSec
if (m_destroyed)
return false;
const unsigned char* macroData = m_audioGroup.getPool().soundMacro(objectId);
const SoundMacro* macroData = m_audioGroup.getPool().soundMacro(objectId);
if (macroData)
{
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);
if (keymap)
{
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)
{
m_objectId = objectId;
return _loadLayer(*layer, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc);
return _loadLayer(objectId, *layer, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc);
}
return false;
@ -872,10 +862,9 @@ void Voice::keyOff()
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,
m_state.m_header.m_size);
std::get<2>(m_state.m_pc.back()) = std::get<1>(m_state.m_pc.back())->assertPC(m_keyoffTrap.macroStep);
m_state.m_inWait = false;
}
else
@ -898,9 +887,8 @@ void Voice::message(int32_t val)
if (m_messageTrap.macroId != 0xffff)
{
if (m_messageTrap.macroId == m_state.m_header.m_macroId)
m_state.m_pc.back().second = SoundMacroState::_assertPC(m_messageTrap.macroStep,
m_state.m_header.m_size);
if (m_messageTrap.macroId == std::get<0>(m_state.m_pc.back()))
std::get<2>(m_state.m_pc.back()) = std::get<1>(m_state.m_pc.back())->assertPC(m_messageTrap.macroStep);
else
loadSoundObject(m_messageTrap.macroId, m_messageTrap.macroStep, m_state.m_ticksPerSec, m_state.m_initKey,
m_state.m_initVel, m_state.m_initMod);