mirror of https://github.com/AxioDL/amuse.git
Lots of foundational work for Amuse editor
This commit is contained in:
parent
1e8b6e599c
commit
4c884d019d
|
@ -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
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
#include "AudioGroupModel.hpp"
|
|
@ -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
|
|
@ -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})
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,7 @@
|
|||
#include "EditorWidget.hpp"
|
||||
|
||||
EditorWidget::EditorWidget(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
|
||||
}
|
|
@ -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
|
|
@ -1,7 +1,7 @@
|
|||
#include "KeymapEditor.hpp"
|
||||
|
||||
KeymapEditor::KeymapEditor(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
: EditorWidget(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "LayersEditor.hpp"
|
||||
|
||||
LayersEditor::LayersEditor(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
: EditorWidget(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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&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&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&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>&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>&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>&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>&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>&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>&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&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>&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>&Delete</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionImport_Project">
|
||||
<action name="actionImport">
|
||||
<property name="text">
|
||||
<string>Import Project</string>
|
||||
<string>&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>&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 &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 &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 &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 &Layers</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionAuto_Play">
|
||||
|
@ -347,7 +385,7 @@
|
|||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Auto-Play</string>
|
||||
<string>&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>&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>&Input Device:</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionExport_GameCube_Groups">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>&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/>
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
#include "SFXGroupEditor.hpp"
|
||||
|
||||
SFXGroupEditor::SFXGroupEditor(QWidget* parent)
|
||||
: EditorWidget(parent)
|
||||
{
|
||||
|
||||
}
|
|
@ -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
|
|
@ -1,7 +1,7 @@
|
|||
#include "SampleEditor.hpp"
|
||||
|
||||
SampleEditor::SampleEditor(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
: EditorWidget(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "SongGroupEditor.hpp"
|
||||
|
||||
SongGroupEditor::SongGroupEditor(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
: EditorWidget(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
#include "SoundGroupEditor.hpp"
|
||||
|
||||
SoundGroupEditor::SoundGroupEditor(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
|
||||
}
|
|
@ -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
|
|
@ -1,7 +1,7 @@
|
|||
#include "SoundMacroEditor.hpp"
|
||||
|
||||
SoundMacroEditor::SoundMacroEditor(QWidget* parent)
|
||||
: QWidget(parent)
|
||||
: EditorWidget(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 |
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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__
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
|
||||
}
|
|
@ -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{}});
|
||||
|
||||
|
|
|
@ -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 {};
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue