From 4c884d019dd5d5239af6a79c3decc4b2895a9767 Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Fri, 13 Jul 2018 20:06:33 -1000 Subject: [PATCH] Lots of foundational work for Amuse editor --- CMakeLists.txt | 8 +- Editor/AudioGroupModel.cpp | 1 + Editor/AudioGroupModel.hpp | 21 + Editor/CMakeLists.txt | 14 +- Editor/Common.cpp | 38 + Editor/Common.hpp | 14 + Editor/EditorWidget.cpp | 7 + Editor/EditorWidget.hpp | 14 + Editor/KeymapEditor.cpp | 2 +- Editor/KeymapEditor.hpp | 4 +- Editor/LayersEditor.cpp | 2 +- Editor/LayersEditor.hpp | 4 +- Editor/MainWindow.cpp | 394 +++- Editor/MainWindow.hpp | 60 +- Editor/MainWindow.ui | 185 +- Editor/ProjectModel.cpp | 160 +- Editor/ProjectModel.hpp | 51 +- Editor/SFXGroupEditor.cpp | 7 + Editor/SFXGroupEditor.hpp | 14 + Editor/SampleEditor.cpp | 2 +- Editor/SampleEditor.hpp | 4 +- Editor/SongGroupEditor.cpp | 2 +- Editor/SongGroupEditor.hpp | 4 +- Editor/SoundGroupEditor.cpp | 7 - Editor/SoundGroupEditor.hpp | 14 - Editor/SoundMacroEditor.cpp | 2 +- Editor/SoundMacroEditor.hpp | 4 +- Editor/main.cpp | 25 + Editor/resources/FaceGrey.svg | 66 +- Editor/resources/resources.qrc | 3 + include/amuse/AudioGroupPool.hpp | 1043 +++++++++- include/amuse/AudioGroupProject.hpp | 177 +- include/amuse/AudioGroupSampleDirectory.hpp | 2 + include/amuse/Common.hpp | 137 ++ include/amuse/Entity.hpp | 12 +- include/amuse/SoundMacroState.hpp | 123 +- include/amuse/Voice.hpp | 25 +- lib/AudioGroup.cpp | 12 +- lib/AudioGroupPool.cpp | 423 +++- lib/AudioGroupProject.cpp | 542 +---- lib/AudioGroupSampleDirectory.cpp | 15 + lib/Common.cpp | 459 +++++ lib/ContainerRegistry.cpp | 9 +- lib/Engine.cpp | 8 +- lib/Sequencer.cpp | 20 +- lib/SoundMacroState.cpp | 2032 +++++++++---------- lib/Voice.cpp | 74 +- 47 files changed, 4202 insertions(+), 2044 deletions(-) create mode 100644 Editor/AudioGroupModel.cpp create mode 100644 Editor/AudioGroupModel.hpp create mode 100644 Editor/Common.cpp create mode 100644 Editor/Common.hpp create mode 100644 Editor/EditorWidget.cpp create mode 100644 Editor/EditorWidget.hpp create mode 100644 Editor/SFXGroupEditor.cpp create mode 100644 Editor/SFXGroupEditor.hpp delete mode 100644 Editor/SoundGroupEditor.cpp delete mode 100644 Editor/SoundGroupEditor.hpp create mode 100644 lib/Common.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6016cc5..60c97fd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/Editor/AudioGroupModel.cpp b/Editor/AudioGroupModel.cpp new file mode 100644 index 0000000..725e0db --- /dev/null +++ b/Editor/AudioGroupModel.cpp @@ -0,0 +1 @@ +#include "AudioGroupModel.hpp" \ No newline at end of file diff --git a/Editor/AudioGroupModel.hpp b/Editor/AudioGroupModel.hpp new file mode 100644 index 0000000..d6435aa --- /dev/null +++ b/Editor/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 diff --git a/Editor/CMakeLists.txt b/Editor/CMakeLists.txt index 23d5aac..2410f47 100644 --- a/Editor/CMakeLists.txt +++ b/Editor/CMakeLists.txt @@ -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}) diff --git a/Editor/Common.cpp b/Editor/Common.cpp new file mode 100644 index 0000000..dede7b9 --- /dev/null +++ b/Editor/Common.cpp @@ -0,0 +1,38 @@ +#include "Common.hpp" +#include +#include + +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; +} diff --git a/Editor/Common.hpp b/Editor/Common.hpp new file mode 100644 index 0000000..c731b87 --- /dev/null +++ b/Editor/Common.hpp @@ -0,0 +1,14 @@ +#ifndef AMUSE_COMMON_HPP +#define AMUSE_COMMON_HPP + +#include "boo/System.hpp" +#include +#include + +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 diff --git a/Editor/EditorWidget.cpp b/Editor/EditorWidget.cpp new file mode 100644 index 0000000..1442833 --- /dev/null +++ b/Editor/EditorWidget.cpp @@ -0,0 +1,7 @@ +#include "EditorWidget.hpp" + +EditorWidget::EditorWidget(QWidget* parent) +: QWidget(parent) +{ + +} diff --git a/Editor/EditorWidget.hpp b/Editor/EditorWidget.hpp new file mode 100644 index 0000000..a356ea4 --- /dev/null +++ b/Editor/EditorWidget.hpp @@ -0,0 +1,14 @@ +#ifndef AMUSE_EDITOR_WIDGET_HPP +#define AMUSE_EDITOR_WIDGET_HPP + +#include + +class EditorWidget : public QWidget +{ +Q_OBJECT +public: + explicit EditorWidget(QWidget* parent = Q_NULLPTR); +}; + + +#endif //AMUSE_EDITOR_WIDGET_HPP diff --git a/Editor/KeymapEditor.cpp b/Editor/KeymapEditor.cpp index 5e8c7bb..67f9765 100644 --- a/Editor/KeymapEditor.cpp +++ b/Editor/KeymapEditor.cpp @@ -1,7 +1,7 @@ #include "KeymapEditor.hpp" KeymapEditor::KeymapEditor(QWidget* parent) -: QWidget(parent) +: EditorWidget(parent) { } diff --git a/Editor/KeymapEditor.hpp b/Editor/KeymapEditor.hpp index 3c18540..f7170e7 100644 --- a/Editor/KeymapEditor.hpp +++ b/Editor/KeymapEditor.hpp @@ -1,9 +1,9 @@ #ifndef AMUSE_KEYMAP_EDITOR_HPP #define AMUSE_KEYMAP_EDITOR_HPP -#include +#include "EditorWidget.hpp" -class KeymapEditor : public QWidget +class KeymapEditor : public EditorWidget { Q_OBJECT public: diff --git a/Editor/LayersEditor.cpp b/Editor/LayersEditor.cpp index 8b5a3b2..a89999d 100644 --- a/Editor/LayersEditor.cpp +++ b/Editor/LayersEditor.cpp @@ -1,7 +1,7 @@ #include "LayersEditor.hpp" LayersEditor::LayersEditor(QWidget* parent) -: QWidget(parent) +: EditorWidget(parent) { } diff --git a/Editor/LayersEditor.hpp b/Editor/LayersEditor.hpp index 1638342..89b9f9e 100644 --- a/Editor/LayersEditor.hpp +++ b/Editor/LayersEditor.hpp @@ -1,9 +1,9 @@ #ifndef AMUSE_LAYERS_EDITOR_HPP #define AMUSE_LAYERS_EDITOR_HPP -#include +#include "EditorWidget.hpp" -class LayersEditor : public QWidget +class LayersEditor : public EditorWidget { Q_OBJECT public: diff --git a/Editor/MainWindow.cpp b/Editor/MainWindow.cpp index aacacd5..52a097b 100644 --- a/Editor/MainWindow.cpp +++ b/Editor/MainWindow.cpp @@ -1,10 +1,398 @@ #include "MainWindow.hpp" -#include "ui_MainWindow.h" +#include +#include +#include +#include +#include +#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(*m_voxEngine); + m_engine = std::make_unique(*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 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 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(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(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(sender())) + { + m_ui.actionUndo->setEnabled(le->isUndoAvailable()); + m_ui.actionRedo->setEnabled(le->isRedoAvailable()); + } +} + +void MainWindow::onTextSelect() +{ + if (QLineEdit* le = qobject_cast(sender())) + { + m_ui.actionCut->setEnabled(le->hasSelectedText()); + m_ui.actionCopy->setEnabled(le->hasSelectedText()); + } +} + +void MainWindow::onTextDelete() +{ + if (QLineEdit* le = qobject_cast(focusWidget())) + { + le->del(); + } +} diff --git a/Editor/MainWindow.hpp b/Editor/MainWindow.hpp index 8139c1f..6fc5ad6 100644 --- a/Editor/MainWindow.hpp +++ b/Editor/MainWindow.hpp @@ -2,16 +2,74 @@ #define AMUSE_MAINWINDOW_HPP #include +#include +#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 m_voxEngine; + std::unique_ptr m_voxAllocator; + std::unique_ptr 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(); + }; diff --git a/Editor/MainWindow.ui b/Editor/MainWindow.ui index fe59316..0346d52 100644 --- a/Editor/MainWindow.ui +++ b/Editor/MainWindow.ui @@ -91,6 +91,18 @@ 400 + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + + + -2 + true @@ -99,10 +111,28 @@ 0 0 - 538 - 450 + 540 + 442 + + + + + + 0 + 0 + + + + + 256 + 256 + + + + + @@ -124,6 +154,15 @@ 100 + + QFrame::NoFrame + + + QFrame::Plain + + + 0 + true @@ -132,8 +171,8 @@ 0 0 - 538 - 98 + 540 + 100 @@ -155,23 +194,23 @@ 0 0 800 - 22 + 27 - File + Fi&le - - + + - Project + P&roject - + @@ -180,7 +219,7 @@ - Audio + A&udio @@ -188,7 +227,7 @@ - MIDI + &MIDI @@ -214,129 +253,128 @@ - New Project - - - Ctrl+N + &New Project - Open Project - - - Ctrl+O - - - - - Duplicate Project - - - - - Quit + &Open Project - - Undo + + false - - Ctrl+Z + + &Undo - - Redo + + false - - Ctrl+Shift+Z + + &Redo - - Cut + + false - - Ctrl+X + + &Cut - - Copy + + false - - Ctrl+C + + C&opy - - Paste + + false - - Ctrl+V + + &Paste - - Delete + + false - - Del + + &Delete - + - Import Project + &Import Ctrl+I - + + + false + :/icons/IconNewSoundGroup.svg:/icons/IconNewSoundGroup.svg - New Sound Group + &New SFX Group + + false + :/icons/IconNewSongGroup.svg:/icons/IconNewSongGroup.svg - New Song Group + New &Song Group + + false + :/icons/IconNewSoundMacro.svg:/icons/IconNewSoundMacro.svg - New Sound Macro + New Sound &Macro + + false + :/icons/IconNewKeymap.svg:/icons/IconNewKeymap.svg - New Keymap + New &Keymap + + false + :/icons/IconNewLayers.svg:/icons/IconNewLayers.svg - New Layers + New &Layers @@ -347,7 +385,7 @@ true - Auto-Play + &Auto-Play @@ -355,7 +393,7 @@ false - Output Device: + &Output Device: @@ -363,7 +401,18 @@ false - Input Device: + &Input Device: + + + + + false + + + &Export GameCube Groups + + + Ctrl+E @@ -379,6 +428,12 @@ QStatusBar
StatusBarWidget.hpp
+ + QSvgWidget + QWidget +
QSvgWidget
+ 1 +
diff --git a/Editor/ProjectModel.cpp b/Editor/ProjectModel.cpp index d122ce3..7add2ff 100644 --- a/Editor/ProjectModel.cpp +++ b/Editor/ProjectModel.cpp @@ -1,7 +1,163 @@ +#include +#include #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(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(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(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() { } diff --git a/Editor/ProjectModel.hpp b/Editor/ProjectModel.hpp index 5fd7623..abbb5e0 100644 --- a/Editor/ProjectModel.hpp +++ b/Editor/ProjectModel.hpp @@ -2,12 +2,61 @@ #define AMUSE_PROJECT_MODEL_HPP #include +#include +#include +#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 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); }; diff --git a/Editor/SFXGroupEditor.cpp b/Editor/SFXGroupEditor.cpp new file mode 100644 index 0000000..db897c9 --- /dev/null +++ b/Editor/SFXGroupEditor.cpp @@ -0,0 +1,7 @@ +#include "SFXGroupEditor.hpp" + +SFXGroupEditor::SFXGroupEditor(QWidget* parent) +: EditorWidget(parent) +{ + +} diff --git a/Editor/SFXGroupEditor.hpp b/Editor/SFXGroupEditor.hpp new file mode 100644 index 0000000..246087f --- /dev/null +++ b/Editor/SFXGroupEditor.hpp @@ -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 diff --git a/Editor/SampleEditor.cpp b/Editor/SampleEditor.cpp index e95d44e..8a86a72 100644 --- a/Editor/SampleEditor.cpp +++ b/Editor/SampleEditor.cpp @@ -1,7 +1,7 @@ #include "SampleEditor.hpp" SampleEditor::SampleEditor(QWidget* parent) -: QWidget(parent) +: EditorWidget(parent) { } diff --git a/Editor/SampleEditor.hpp b/Editor/SampleEditor.hpp index d796e1b..4730c08 100644 --- a/Editor/SampleEditor.hpp +++ b/Editor/SampleEditor.hpp @@ -1,9 +1,9 @@ #ifndef AMUSE_SAMPLE_EDITOR_HPP #define AMUSE_SAMPLE_EDITOR_HPP -#include +#include "EditorWidget.hpp" -class SampleEditor : public QWidget +class SampleEditor : public EditorWidget { Q_OBJECT public: diff --git a/Editor/SongGroupEditor.cpp b/Editor/SongGroupEditor.cpp index 6499e48..31c5124 100644 --- a/Editor/SongGroupEditor.cpp +++ b/Editor/SongGroupEditor.cpp @@ -1,7 +1,7 @@ #include "SongGroupEditor.hpp" SongGroupEditor::SongGroupEditor(QWidget* parent) -: QWidget(parent) +: EditorWidget(parent) { } diff --git a/Editor/SongGroupEditor.hpp b/Editor/SongGroupEditor.hpp index 5e0245e..5206a16 100644 --- a/Editor/SongGroupEditor.hpp +++ b/Editor/SongGroupEditor.hpp @@ -1,9 +1,9 @@ #ifndef AMUSE_SONG_GROUP_EDITOR_HPP #define AMUSE_SONG_GROUP_EDITOR_HPP -#include +#include "EditorWidget.hpp" -class SongGroupEditor : public QWidget +class SongGroupEditor : public EditorWidget { Q_OBJECT public: diff --git a/Editor/SoundGroupEditor.cpp b/Editor/SoundGroupEditor.cpp deleted file mode 100644 index bf39856..0000000 --- a/Editor/SoundGroupEditor.cpp +++ /dev/null @@ -1,7 +0,0 @@ -#include "SoundGroupEditor.hpp" - -SoundGroupEditor::SoundGroupEditor(QWidget* parent) -: QWidget(parent) -{ - -} diff --git a/Editor/SoundGroupEditor.hpp b/Editor/SoundGroupEditor.hpp deleted file mode 100644 index f680f79..0000000 --- a/Editor/SoundGroupEditor.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef AMUSE_SOUND_GROUP_EDITOR_HPP -#define AMUSE_SOUND_GROUP_EDITOR_HPP - -#include - -class SoundGroupEditor : public QWidget -{ - Q_OBJECT -public: - explicit SoundGroupEditor(QWidget* parent = Q_NULLPTR); -}; - - -#endif //AMUSE_SOUND_GROUP_EDITOR_HPP diff --git a/Editor/SoundMacroEditor.cpp b/Editor/SoundMacroEditor.cpp index 6771ca8..7e3fd12 100644 --- a/Editor/SoundMacroEditor.cpp +++ b/Editor/SoundMacroEditor.cpp @@ -1,7 +1,7 @@ #include "SoundMacroEditor.hpp" SoundMacroEditor::SoundMacroEditor(QWidget* parent) -: QWidget(parent) +: EditorWidget(parent) { } diff --git a/Editor/SoundMacroEditor.hpp b/Editor/SoundMacroEditor.hpp index abbe9d3..0388e8c 100644 --- a/Editor/SoundMacroEditor.hpp +++ b/Editor/SoundMacroEditor.hpp @@ -1,9 +1,9 @@ #ifndef AMUSE_SOUND_MACRO_EDITOR_HPP #define AMUSE_SOUND_MACRO_EDITOR_HPP -#include +#include "EditorWidget.hpp" -class SoundMacroEditor : public QWidget +class SoundMacroEditor : public EditorWidget { Q_OBJECT public: diff --git a/Editor/main.cpp b/Editor/main.cpp index 0f02ade..6f10f5f 100644 --- a/Editor/main.cpp +++ b/Editor/main.cpp @@ -2,6 +2,9 @@ #include #include #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 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& getArgs() const { return m_args; } + + /* Constructors/initializers for sub-objects */ + std::shared_ptr 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(); diff --git a/Editor/resources/FaceGrey.svg b/Editor/resources/FaceGrey.svg index 4f9d678..c5ca118 100644 --- a/Editor/resources/FaceGrey.svg +++ b/Editor/resources/FaceGrey.svg @@ -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 @@ + style="stop-color:#858585;stop-opacity:1;" /> + style="stop-color:#606060;stop-opacity:1;" /> + style="stop-color:#484848;stop-opacity:1;" /> @@ -334,23 +334,28 @@ + 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" /> @@ -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"> + sodipodi:insensitive="true" + transform="translate(-4.3711946,-4.3733763)"> + sodipodi:insensitive="true" + transform="translate(-4.3711946,-4.3733763)"> + sodipodi:insensitive="true" + transform="translate(-4.3711946,-4.3733763)"> + sodipodi:insensitive="true" + transform="translate(-4.3711946,-4.3733763)"> + style="display:inline;opacity:1" + transform="translate(-4.3711946,-4.3733763)"> diff --git a/Editor/resources/resources.qrc b/Editor/resources/resources.qrc index 3adf384..1dc301d 100644 --- a/Editor/resources/resources.qrc +++ b/Editor/resources/resources.qrc @@ -14,4 +14,7 @@ IconSoundGroup.svg IconSoundMacro.svg + + FaceGrey.svg + diff --git a/include/amuse/AudioGroupPool.hpp b/include/amuse/AudioGroupPool.hpp index 6c22af4..1a9c3c1 100644 --- a/include/amuse/AudioGroupPool.hpp +++ b/include/amuse/AudioGroupPool.hpp @@ -10,6 +10,955 @@ namespace amuse { +class AudioGroupData; +struct SoundMacroState; +class Voice; + +/** Header at the top of the pool file */ +template +struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) +PoolHeader : BigDNA +{ + AT_DECL_DNA + Value soundMacrosOffset; + Value tablesOffset; + Value keymapsOffset; + Value layersOffset; +}; + +/** Header present at the top of each pool object */ +template +struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) +ObjectHeader : BigDNA +{ + AT_DECL_DNA + Value size; + ObjectIdDNA objectId; + Seek<2, athena::Current> pad; +}; + +struct SoundMacro +{ + /** SoundMacro command operations */ + enum class CmdOp : 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, + }; + + /** Base command interface. All versions of MusyX encode little-endian parameters */ + struct ICmd : LittleDNAV + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + virtual bool Do(SoundMacroState& st, Voice& vox) const = 0; + virtual CmdOp Isa() const = 0; + }; + struct CmdEnd : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::End; } + }; + struct CmdStop : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::Stop; } + }; + struct CmdSplitKey : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value key; + ObjectIdDNA macro; + Value macroStep; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SplitKey; } + }; + struct CmdSplitVel : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value velocity; + ObjectIdDNA macro; + Value macroStep; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SplitVel; } + }; + struct CmdWaitTicks : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value keyOff; + Value random; + Value sampleEnd; + Value absolute; + Value msSwitch; + Value ticksPerMs; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::WaitTicks; } + }; + struct CmdLoop : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value keyOff; + Value random; + Value sampleEnd; + Value macroStep; + Value times; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::Loop; } + }; + struct CmdGoto : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Seek<1, athena::SeekOrigin::Current> dummy; + ObjectIdDNA macro; + Value macroStep; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::Goto; } + }; + struct CmdWaitMs : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value keyOff; + Value random; + Value sampleEnd; + Value absolute; + Seek<1, athena::SeekOrigin::Current> dummy; + Value ms; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::WaitMs; } + }; + struct CmdPlayMacro : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value addNote; + ObjectIdDNA macro; + Value macroStep; + Value priority; + Value maxVoices; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::PlayMacro; } + }; + struct CmdSendKeyOff : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value variable; + Value lastStarted; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SendKeyOff; } + }; + struct CmdSplitMod : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value modValue; + ObjectIdDNA macro; + Value macroStep; + Value priority; + Value maxVoices; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SplitMod; } + }; + struct CmdPianoPan : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value scale; + Value centerKey; + Value centerPan; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::PianoPan; } + }; + struct CmdSetAdsr : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + ObjectIdDNA table; + Value dlsMode; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SetAdsr; } + }; + struct CmdScaleVolume : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value scale; + Value add; + ObjectIdDNA table; + Value originalVol; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::ScaleVolume; } + }; + struct CmdPanning : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value panPosition; + Value timeMs; + Value width; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::Panning; } + }; + struct CmdEnvelope : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value scale; + Value add; + ObjectIdDNA table; + Value msSwitch; + Value fadeTime; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::Envelope; } + }; + struct CmdStartSample : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + SampleIdDNA sample; + Value mode; + Value offset; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::StartSample; } + }; + struct CmdStopSample : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::StopSample; } + }; + struct CmdKeyOff : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::KeyOff; } + }; + struct CmdSplitRnd : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value rnd; + ObjectIdDNA macro; + Value macroStep; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SplitRnd; } + }; + struct CmdFadeIn : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value scale; + Value add; + ObjectIdDNA table; + Value msSwitch; + Value ticksPerMs; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::FadeIn; } + }; + struct CmdSpanning : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value spanPosition; + Value timeMs; + Value width; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::Spanning; } + }; + struct CmdSetAdsrCtrl : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value attack; + Value decay; + Value sustain; + Value release; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SetAdsrCtrl; } + }; + struct CmdRndNote : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value noteLo; + Value detune; + Value noteHi; + Value fixedFree; + Value absRel; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::RndNote; } + }; + struct CmdAddNote : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value add; + Value detune; + Value originalKey; + Seek<1, athena::SeekOrigin::Current> seek; + Value msSwitch; + Value ticksPerMs; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::AddNote; } + }; + struct CmdSetNote : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value key; + Value detune; + Seek<2, athena::SeekOrigin::Current> seek; + Value msSwitch; + Value ticksPerMs; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SetNote; } + }; + struct CmdLastNote : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value add; + Value detune; + Seek<2, athena::SeekOrigin::Current> seek; + Value msSwitch; + Value ticksPerMs; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::LastNote; } + }; + struct CmdPortamento : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value portState; + Value portType; + Seek<2, athena::SeekOrigin::Current> seek; + Value msSwitch; + Value ticksPerMs; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::Portamento; } + }; + struct CmdVibrato : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value levelNote; + Value levelFine; + Value modwheelFlag; + Seek<1, athena::SeekOrigin::Current> seek; + Value msSwitch; + Value ticksPerMs; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::Vibrato; } + }; + struct CmdPitchSweep1 : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value times; + Value add; + Seek<1, athena::SeekOrigin::Current> seek; + Value msSwitch; + Value ticksPerMs; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::PitchSweep1; } + }; + struct CmdPitchSweep2 : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value times; + Value add; + Seek<1, athena::SeekOrigin::Current> seek; + Value msSwitch; + Value ticksPerMs; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::PitchSweep2; } + }; + struct CmdSetPitch : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + LittleUInt24 hz; + Value fine; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SetPitch; } + }; + struct CmdSetPitchAdsr : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + ObjectIdDNA table; + Seek<1, athena::SeekOrigin::Current> seek; + Value keys; + Value cents; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SetPitchAdsr; } + }; + struct CmdScaleVolumeDLS : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value scale; + Value originalVol; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::ScaleVolumeDLS; } + }; + struct CmdMod2Vibrange : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value keys; + Value cents; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::Mod2Vibrange; } + }; + struct CmdSetupTremolo : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value scale; + Seek<1, athena::SeekOrigin::Current> seek; + Value modwAddScale; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SetupTremolo; } + }; + struct CmdReturn : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::Return; } + }; + struct CmdGoSub : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Seek<1, athena::SeekOrigin::Current> seek; + ObjectIdDNA macro; + Value macroStep; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::GoSub; } + }; + struct CmdTrapEvent : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value event; + ObjectIdDNA macro; + Value macroStep; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::TrapEvent; } + }; + struct CmdUntrapEvent : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value event; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::UntrapEvent; } + }; + struct CmdSendMessage : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value isVar; + ObjectIdDNA macro; + Value vid; + Value variable; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SendMessage; } + }; + struct CmdGetMessage : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value variable; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::GetMessage; } + }; + struct CmdGetVid : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value variable; + Value playMacro; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::GetVid; } + }; + struct CmdAddAgeCount : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Seek<1, athena::SeekOrigin::Current> seek; + Value add; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::AddAgeCount; } + }; + struct CmdSetAgeCount : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Seek<1, athena::SeekOrigin::Current> seek; + Value counter; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SetAgeCount; } + }; + struct CmdSendFlag : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value flagId; + Value value; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SendFlag; } + }; + struct CmdPitchWheelR : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value rangeUp; + Value rangeDown; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::PitchWheelR; } + }; + struct CmdSetPriority : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value prio; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SetPriority; } + }; + struct CmdAddPriority : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Seek<1, athena::SeekOrigin::Current> seek; + Value prio; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::AddPriority; } + }; + struct CmdAgeCntSpeed : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Seek<3, athena::SeekOrigin::Current> seek; + Value time; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::AgeCntSpeed; } + }; + struct CmdAgeCntVel : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Seek<1, athena::SeekOrigin::Current> seek; + Value ageBase; + Value ageScale; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::AgeCntVel; } + }; + struct CmdVolSelect : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value midiControl; + Value scalingPercentage; + Value combine; + Value isVar; + Value fineScaling; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::VolSelect; } + }; + struct CmdPanSelect : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value midiControl; + Value scalingPercentage; + Value combine; + Value isVar; + Value fineScaling; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::PanSelect; } + }; + struct CmdPitchWheelSelect : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value midiControl; + Value scalingPercentage; + Value combine; + Value isVar; + Value fineScaling; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::PitchWheelSelect; } + }; + struct CmdModWheelSelect : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value midiControl; + Value scalingPercentage; + Value combine; + Value isVar; + Value fineScaling; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::ModWheelSelect; } + }; + struct CmdPedalSelect : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value midiControl; + Value scalingPercentage; + Value combine; + Value isVar; + Value fineScaling; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::PedalSelect; } + }; + struct CmdPortamentoSelect : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value midiControl; + Value scalingPercentage; + Value combine; + Value isVar; + Value fineScaling; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::PortamentoSelect; } + }; + struct CmdReverbSelect : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value midiControl; + Value scalingPercentage; + Value combine; + Value isVar; + Value fineScaling; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::ReverbSelect; } + }; + struct CmdSpanSelect : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value midiControl; + Value scalingPercentage; + Value combine; + Value isVar; + Value fineScaling; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SpanSelect; } + }; + struct CmdDopplerSelect : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value midiControl; + Value scalingPercentage; + Value combine; + Value isVar; + Value fineScaling; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::DopplerSelect; } + }; + struct CmdTremoloSelect : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value midiControl; + Value scalingPercentage; + Value combine; + Value isVar; + Value fineScaling; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::TremoloSelect; } + }; + struct CmdPreASelect : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value midiControl; + Value scalingPercentage; + Value combine; + Value isVar; + Value fineScaling; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::PreASelect; } + }; + struct CmdPreBSelect : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value midiControl; + Value scalingPercentage; + Value combine; + Value isVar; + Value fineScaling; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::PreBSelect; } + }; + struct CmdPostBSelect : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value midiControl; + Value scalingPercentage; + Value combine; + Value isVar; + Value fineScaling; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::PostBSelect; } + }; + struct CmdAuxAFXSelect : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value midiControl; + Value scalingPercentage; + Value combine; + Value isVar; + Value fineScaling; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::AuxAFXSelect; } + }; + struct CmdAuxBFXSelect : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value midiControl; + Value scalingPercentage; + Value combine; + Value isVar; + Value fineScaling; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::AuxBFXSelect; } + }; + struct CmdSetupLFO : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value lfoNumber; + Value periodInMs; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SetupLFO; } + }; + struct CmdModeSelect : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value dlsVol; + Value itd; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::ModeSelect; } + }; + struct CmdSetKeygroup : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value group; + Value killNow; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SetKeygroup; } + }; + struct CmdSRCmodeSelect : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value srcType; + Value type0SrcFilter; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SRCmodeSelect; } + }; + struct CmdAddVars : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value varCtrlA; + Value a; + Value varCtrlB; + Value b; + Value varCtrlC; + Value c; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::AddVars; } + }; + struct CmdSubVars : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value varCtrlA; + Value a; + Value varCtrlB; + Value b; + Value varCtrlC; + Value c; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::SubVars; } + }; + struct CmdMulVars : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value varCtrlA; + Value a; + Value varCtrlB; + Value b; + Value varCtrlC; + Value c; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::MulVars; } + }; + struct CmdDivVars : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value varCtrlA; + Value a; + Value varCtrlB; + Value b; + Value varCtrlC; + Value c; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::DivVars; } + }; + struct CmdAddIVars : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value varCtrlA; + Value a; + Value varCtrlB; + Value b; + Value imm; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::AddIVars; } + }; + struct CmdIfEqual : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value varCtrlA; + Value a; + Value varCtrlB; + Value b; + Value notEq; + Value macroStep; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::IfEqual; } + }; + struct CmdIfLess : ICmd + { + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value varCtrlA; + Value a; + Value varCtrlB; + Value b; + Value notLt; + Value macroStep; + bool Do(SoundMacroState& st, Voice& vox) const; + CmdOp Isa() const { return CmdOp::IfLess; } + }; + + std::vector> m_cmds; + int assertPC(int pc) const; + + const ICmd& getCmd(int i) const { return *m_cmds[assertPC(i)]; } + + template + void readCmds(athena::io::IStreamReader& r, uint32_t size); +}; /** Converts time-cents representation to seconds */ static inline double TimeCentsToSeconds(int32_t tc) @@ -19,13 +968,22 @@ static inline double TimeCentsToSeconds(int32_t tc) return std::exp2(tc / (1200.0 * 65536.0)); } -/** Defines phase-based volume curve for macro volume control */ -struct ADSR +/** Polymorphic interface for representing table data */ +struct ITable : LittleDNAV { - uint16_t attack; - uint16_t decay; - uint16_t sustain; /* 0x1000 == 100% */ - uint16_t release; /* milliseconds */ + AT_DECL_DNA_YAML + AT_DECL_DNAV +}; + +/** Defines phase-based volume curve for macro volume control */ +struct ADSR : ITable +{ + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value attack; + Value decay; + Value sustain; /* 0x1000 == 100% */ + Value release; /* milliseconds */ double getAttack() const { return attack / 1000.0; } double getDecay() const { return (decay == 0x8000) ? 0.0 : (decay / 1000.0); } @@ -34,14 +992,16 @@ struct ADSR }; /** Defines phase-based volume curve for macro volume control (modified DLS standard) */ -struct ADSRDLS +struct ADSRDLS : ITable { - uint32_t attack; /* 16.16 Time-cents */ - uint32_t decay; /* 16.16 Time-cents */ - uint16_t sustain; /* 0x1000 == 100% */ - uint16_t release; /* milliseconds */ - uint32_t velToAttack; /* 16.16, 1000.0 == 100%; attack = + (vel/128) * */ - uint32_t keyToDecay; /* 16.16, 1000.0 == 100%; decay = + (note/128) * */ + AT_DECL_DNA_YAML + AT_DECL_DNAV + Value attack; /* 16.16 Time-cents */ + Value decay; /* 16.16 Time-cents */ + Value sustain; /* 0x1000 == 100% */ + Value release; /* milliseconds */ + Value velToAttack; /* 16.16, 1000.0 == 100%; attack = + (vel/128) * */ + Value keyToDecay; /* 16.16, 1000.0 == 100%; decay = + (note/128) * */ double getAttack() const { return TimeCentsToSeconds(attack); } double getDecay() const { return TimeCentsToSeconds(decay); } @@ -61,44 +1021,55 @@ struct ADSRDLS } }; +/** Defines arbitrary data for use as volume curve */ +struct Curve : ITable +{ + AT_DECL_EXPLICIT_DNA_YAML + AT_DECL_DNAV + std::vector data; +}; + /** Maps individual MIDI keys to sound-entity as indexed in table * (macro-voice, keymap, layer) */ -struct Keymap +struct Keymap : LittleDNA { - int16_t objectId; - int8_t transpose; - int8_t pan; /* -128 for surround-channel only */ - int8_t prioOffset; - int8_t pad[3]; + AT_DECL_DNA_YAML + Value transpose; + Value pan; /* -128 for surround-channel only */ + Value prioOffset; + Seek<3, athena::Current> pad; }; /** Maps ranges of MIDI keys to sound-entity (macro-voice, keymap, layer) */ -struct LayerMapping +struct LayerMapping : LittleDNA { - int16_t objectId; - int8_t keyLo; - int8_t keyHi; - int8_t transpose; - int8_t volume; - int8_t prioOffset; - int8_t span; - int8_t pan; + AT_DECL_DNA_YAML + Value keyLo; + Value keyHi; + Value transpose; + Value volume; + Value prioOffset; + Value span; + Value pan; }; /** Database of functional objects within Audio Group */ class AudioGroupPool { - std::unordered_map m_soundMacros; - std::unordered_map m_tables; - std::unordered_map m_keymaps; - std::unordered_map> m_layers; + std::unordered_map m_soundMacros; + std::unordered_map> m_tables; + std::unordered_map m_keymaps; + std::unordered_map> m_layers; + AudioGroupPool() = default; + template + static AudioGroupPool _AudioGroupPool(athena::io::IStreamReader& r); public: - AudioGroupPool(const unsigned char* data); - AudioGroupPool(const unsigned char* data, PCDataTag); - const unsigned char* soundMacro(ObjectId id) const; + static AudioGroupPool CreateAudioGroupPool(const AudioGroupData& data); + + const SoundMacro* soundMacro(ObjectId id) const; const Keymap* keymap(ObjectId id) const; - const std::vector* layer(ObjectId id) const; + const std::vector* layer(ObjectId id) const; const ADSR* tableAsAdsr(ObjectId id) const; const ADSRDLS* tableAsAdsrDLS(ObjectId id) const { return reinterpret_cast(tableAsAdsr(id)); } const Curve* tableAsCurves(ObjectId id) const { return reinterpret_cast(tableAsAdsr(id)); } diff --git a/include/amuse/AudioGroupProject.hpp b/include/amuse/AudioGroupProject.hpp index 10b0c52..621dfea 100644 --- a/include/amuse/AudioGroupProject.hpp +++ b/include/amuse/AudioGroupProject.hpp @@ -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 +struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) +GroupHeader : BigDNA +{ + AT_DECL_DNA + Value groupEndOff; + Value groupId; + Value type; + Value soundMacroIdsOff; + Value samplIdsOff; + Value tableIdsOff; + Value keymapIdsOff; + Value layerIdsOff; + Value pageTableOff; + Value drumTableOff; + Value 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 + struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) + PageEntryDNA : BigDNA + { + AT_DECL_DNA_YAML + ObjectIdDNA objId; + Value priority; + Value maxVoices; + Value programNo; + Seek<1, athena::Current> pad; + }; + template + struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) + MusyX1PageEntryDNA : BigDNA + { + AT_DECL_DNA_YAML + ObjectIdDNA objId; + Value priority; + Value maxVoices; + Value unk; + Value 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 + PageEntry(const PageEntryDNA& in) + : objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices) {} + + template + PageEntry(const MusyX1PageEntryDNA& in) + : objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices) {} + + template + PageEntryDNA toDNA(uint8_t programNo) const + { + PageEntryDNA ret; + ret.objId.id = objId; + ret.priority = priority; + ret.maxVoices = maxVoices; + ret.programNo = programNo; + return ret; + } }; - std::unordered_map m_normPages; - std::unordered_map m_drumPages; + std::unordered_map m_normPages; + std::unordered_map 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 programNo; + Value volume; + Value panning; + Value reverb; + Value chorus; + Seek<3, athena::Current> pad; }; - std::unordered_map*> m_midiSetups; + struct MIDISetup : BigDNA + { + AT_DECL_DNA_YAML + Value programNo; + Value volume; + Value panning; + Value reverb; + Value 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> m_midiSetups; }; /** Root index of SFXGroup */ struct SFXGroupIndex : AudioGroupIndex { /** Maps game-side SFX define IDs to sound entities */ + template + struct AT_SPECIALIZE_PARMS(athena::Endian::Big, athena::Endian::Little) + SFXEntryDNA : BigDNA + { + AT_DECL_DNA_YAML + SFXIdDNA defineId; + ObjectIdDNA objId; + Value priority; + Value maxVoices; + Value defVel; + Value panning; + Value 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 + SFXEntry(const SFXEntryDNA& in) + : objId(in.objId.id), priority(in.priority), maxVoices(in.maxVoices), + defVel(in.defVel), panning(in.panning), defKey(in.defKey) {} + + template + SFXEntryDNA toDNA(SFXId defineId) const + { + SFXEntryDNA 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 m_sfxEntries; + std::unordered_map m_sfxEntries; }; /** Collection of SongGroup and SFXGroup indexes */ @@ -72,17 +181,11 @@ class AudioGroupProject std::unordered_map m_songGroups; std::unordered_map m_sfxGroups; - /* MusyX 1.0 structures converted to MusyX 2.0 structures for pointer-compatible access */ - std::unique_ptr m_convNormalPages; - std::unique_ptr m_convDrumPages; - std::unique_ptr[]> 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 + 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; diff --git a/include/amuse/AudioGroupSampleDirectory.hpp b/include/amuse/AudioGroupSampleDirectory.hpp index 3eb073d..9279743 100644 --- a/include/amuse/AudioGroupSampleDirectory.hpp +++ b/include/amuse/AudioGroupSampleDirectory.hpp @@ -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>& sampleEntries() const { return m_entries; } }; diff --git a/include/amuse/Common.hpp b/include/amuse/Common.hpp index 73446db..9c400eb 100644 --- a/include/amuse/Common.hpp +++ b/include/amuse/Common.hpp @@ -9,6 +9,7 @@ #include #include #include +#include "athena/DNA.hpp" #ifndef _WIN32 #include @@ -28,6 +29,95 @@ constexpr float NativeSampleRate = 32000.0f; namespace amuse { +struct NameDB; + +using BigDNA = athena::io::DNA; +using LittleDNA = athena::io::DNA; +using BigDNAV = athena::io::DNAVYaml; +using LittleDNAV = athena::io::DNAVYaml; + +/** 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 +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 +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 +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 +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 +{ + size_t operator()(const amuse::ObjectId& val) const noexcept { return val.id; } +}; +template<> +struct hash +{ + size_t operator()(const amuse::SampleId& val) const noexcept { return val.id; } +}; +template<> +struct hash +{ + size_t operator()(const amuse::SongId& val) const noexcept { return val.id; } +}; +template<> +struct hash +{ + 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 m_stringToId; + std::unordered_map 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__ diff --git a/include/amuse/Entity.hpp b/include/amuse/Entity.hpp index 5e124dd..f3e69d6 100644 --- a/include/amuse/Entity.hpp +++ b/include/amuse/Entity.hpp @@ -4,23 +4,20 @@ #include #include #include +#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__ diff --git a/include/amuse/SoundMacroState.hpp b/include/amuse/SoundMacroState.hpp index d614789..993301b 100644 --- a/include/amuse/SoundMacroState.hpp +++ b/include/amuse/SoundMacroState.hpp @@ -6,6 +6,7 @@ #include #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> 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> 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 diff --git a/include/amuse/Voice.hpp b/include/amuse/Voice.hpp index 7989264..30fd2e2 100644 --- a/include/amuse/Voice.hpp +++ b/include/amuse/Voice.hpp @@ -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 m_studio; /**< Studio this voice outputs to */ @@ -160,12 +171,12 @@ class Voice : public Entity std::list>::iterator _allocateVoice(double sampleRate, bool dynamicPitch); std::list>::iterator _destroyVoice(std::list>::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& layer, int macroStep, double ticksPerSec, uint8_t midiKey, - uint8_t midiVel, uint8_t midiMod, bool pushPc = false); + 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& layer, int macroStep, double ticksPerSec, + uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false); std::shared_ptr _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false); diff --git a/lib/AudioGroup.cpp b/lib/AudioGroup.cpp index 5d42d81..8ef6d97 100644 --- a/lib/AudioGroup.cpp +++ b/lib/AudioGroup.cpp @@ -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) diff --git a/lib/AudioGroupPool.cpp b/lib/AudioGroupPool.cpp index 917461f..bcd0613 100644 --- a/lib/AudioGroupPool.cpp +++ b/lib/AudioGroupPool.cpp @@ -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 +AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r) { - Header head = *reinterpret_cast(data); - head.swapBig(); + AudioGroupPool ret; + + PoolHeader head; + head.read(r); if (head.soundMacrosOffset) { - const unsigned char* cur = data + head.soundMacrosOffset; - while (*reinterpret_cast(cur) != 0xffffffff) + r.seek(head.soundMacrosOffset, athena::Begin); + while (!AtEnd(r)) { - uint32_t size = SBig(*reinterpret_cast(cur)); - ObjectId id = SBig(*reinterpret_cast(cur + 4)); - m_soundMacros[id] = cur; - cur += size; + ObjectHeader objHead; + atInt64 startPos = r.position(); + objHead.read(r); + SoundMacro& macro = ret.m_soundMacros[objHead.objectId.id]; + macro.readCmds(r, objHead.size - 8); + r.seek(startPos + objHead.size, athena::Begin); } } if (head.tablesOffset) { - const unsigned char* cur = data + head.tablesOffset; - while (*reinterpret_cast(cur) != 0xffffffff) + r.seek(head.tablesOffset, athena::Begin); + while (!AtEnd(r)) { - uint32_t size = SBig(*reinterpret_cast(cur)); - ObjectId id = SBig(*reinterpret_cast(cur + 4)); - m_tables[id] = cur + 8; - cur += size; + ObjectHeader 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(); + static_cast(*ptr).read(r); + break; + case 0x14: + ptr = std::make_unique(); + static_cast(*ptr).read(r); + break; + default: + ptr = std::make_unique(); + static_cast(*ptr).data.resize(objHead.size - 8); + r.readUBytesToBuf(&static_cast(*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(cur) != 0xffffffff) + r.seek(head.keymapsOffset, athena::Begin); + while (!AtEnd(r)) { - uint32_t size = SBig(*reinterpret_cast(cur)); - ObjectId id = SBig(*reinterpret_cast(cur + 4)); - m_keymaps[id] = reinterpret_cast(cur + 8); - cur += size; + ObjectHeader 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(cur) != 0xffffffff) + r.seek(head.layersOffset, athena::Begin); + while (!AtEnd(r)) { - uint32_t size = SBig(*reinterpret_cast(cur)); - ObjectId id = SBig(*reinterpret_cast(cur + 4)); - std::vector& mappingsOut = m_layers[id]; - - uint32_t count = SBig(*reinterpret_cast(cur + 8)); - mappingsOut.reserve(count); - const unsigned char* subcur = cur + 12; + ObjectHeader objHead; + atInt64 startPos = r.position(); + objHead.read(r); + std::vector& lm = ret.m_layers[objHead.objectId.id]; + uint32_t count; + athena::io::Read::Do({}, count, r); + lm.reserve(count); for (uint32_t i = 0; i < count; ++i) - mappingsOut.push_back(reinterpret_cast(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) + return ret; +} +template AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r); +template AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r); + +AudioGroupPool AudioGroupPool::CreateAudioGroupPool(const AudioGroupData& data) { - const Header* head = reinterpret_cast(data); - - if (head->soundMacrosOffset) + athena::io::MemoryReader r(data.getPool(), data.getPoolSize()); + switch (data.getDataFormat()) { - const unsigned char* cur = data + head->soundMacrosOffset; - while (*reinterpret_cast(cur) != 0xffffffff) - { - uint32_t size = *reinterpret_cast(cur); - ObjectId id = *reinterpret_cast(cur + 4); - m_soundMacros[id] = cur; - cur += size; - } - } - - if (head->tablesOffset) - { - const unsigned char* cur = data + head->tablesOffset; - while (*reinterpret_cast(cur) != 0xffffffff) - { - uint32_t size = *reinterpret_cast(cur); - ObjectId id = *reinterpret_cast(cur + 4); - m_tables[id] = cur + 8; - cur += size; - } - } - - if (head->keymapsOffset) - { - const unsigned char* cur = data + head->keymapsOffset; - while (*reinterpret_cast(cur) != 0xffffffff) - { - uint32_t size = *reinterpret_cast(cur); - ObjectId id = *reinterpret_cast(cur + 4); - m_keymaps[id] = reinterpret_cast(cur + 8); - cur += size; - } - } - - if (head->layersOffset) - { - const unsigned char* cur = data + head->layersOffset; - while (*reinterpret_cast(cur) != 0xffffffff) - { - uint32_t size = *reinterpret_cast(cur); - ObjectId id = *reinterpret_cast(cur + 4); - std::vector& mappingsOut = m_layers[id]; - - uint32_t count = *reinterpret_cast(cur + 8); - mappingsOut.reserve(count); - const unsigned char* subcur = cur + 12; - for (uint32_t i = 0; i < count; ++i) - mappingsOut.push_back(reinterpret_cast(subcur + i * 12)); - - cur += size; - } + case DataFormat::PC: + return _AudioGroupPool(r); + default: + return _AudioGroupPool(r); } } -const unsigned char* AudioGroupPool::soundMacro(ObjectId id) const +template +static std::unique_ptr MakeCmd(athena::io::MemoryReader& r) +{ + std::unique_ptr ret = std::make_unique(); + static_cast(*ret).read(r); + return ret; +} + +int SoundMacro::assertPC(int pc) const +{ + if (pc == -1) + return -1; + if (pc >= m_cmds.size()) + { + fprintf(stderr, "SoundMacro PC bounds exceeded [%d/%d]\n", pc, int(m_cmds.size())); + abort(); + } + return pc; +} + +template +void SoundMacro::readCmds(athena::io::IStreamReader& r, uint32_t size) +{ + uint32_t numCmds = size / 8; + m_cmds.reserve(numCmds); + for (int i = 0; i < numCmds; ++i) + { + uint32_t data[2]; + athena::io::Read::Do({}, data, r); + athena::io::MemoryReader r(data, 8); + std::unique_ptr cmd; + switch (CmdOp(r.readUByte())) + { + case CmdOp::End: + cmd = MakeCmd(r); break; + case CmdOp::Stop: + cmd = MakeCmd(r); break; + case CmdOp::SplitKey: + cmd = MakeCmd(r); break; + case CmdOp::SplitVel: + cmd = MakeCmd(r); break; + case CmdOp::WaitTicks: + cmd = MakeCmd(r); break; + case CmdOp::Loop: + cmd = MakeCmd(r); break; + case CmdOp::Goto: + cmd = MakeCmd(r); break; + case CmdOp::WaitMs: + cmd = MakeCmd(r); break; + case CmdOp::PlayMacro: + cmd = MakeCmd(r); break; + case CmdOp::SendKeyOff: + cmd = MakeCmd(r); break; + case CmdOp::SplitMod: + cmd = MakeCmd(r); break; + case CmdOp::PianoPan: + cmd = MakeCmd(r); break; + case CmdOp::SetAdsr: + cmd = MakeCmd(r); break; + case CmdOp::ScaleVolume: + cmd = MakeCmd(r); break; + case CmdOp::Panning: + cmd = MakeCmd(r); break; + case CmdOp::Envelope: + cmd = MakeCmd(r); break; + case CmdOp::StartSample: + cmd = MakeCmd(r); break; + case CmdOp::StopSample: + cmd = MakeCmd(r); break; + case CmdOp::KeyOff: + cmd = MakeCmd(r); break; + case CmdOp::SplitRnd: + cmd = MakeCmd(r); break; + case CmdOp::FadeIn: + cmd = MakeCmd(r); break; + case CmdOp::Spanning: + cmd = MakeCmd(r); break; + case CmdOp::SetAdsrCtrl: + cmd = MakeCmd(r); break; + case CmdOp::RndNote: + cmd = MakeCmd(r); break; + case CmdOp::AddNote: + cmd = MakeCmd(r); break; + case CmdOp::SetNote: + cmd = MakeCmd(r); break; + case CmdOp::LastNote: + cmd = MakeCmd(r); break; + case CmdOp::Portamento: + cmd = MakeCmd(r); break; + case CmdOp::Vibrato: + cmd = MakeCmd(r); break; + case CmdOp::PitchSweep1: + cmd = MakeCmd(r); break; + case CmdOp::PitchSweep2: + cmd = MakeCmd(r); break; + case CmdOp::SetPitch: + cmd = MakeCmd(r); break; + case CmdOp::SetPitchAdsr: + cmd = MakeCmd(r); break; + case CmdOp::ScaleVolumeDLS: + cmd = MakeCmd(r); break; + case CmdOp::Mod2Vibrange: + cmd = MakeCmd(r); break; + case CmdOp::SetupTremolo: + cmd = MakeCmd(r); break; + case CmdOp::Return: + cmd = MakeCmd(r); break; + case CmdOp::GoSub: + cmd = MakeCmd(r); break; + case CmdOp::TrapEvent: + cmd = MakeCmd(r); break; + case CmdOp::UntrapEvent: + cmd = MakeCmd(r); break; + case CmdOp::SendMessage: + cmd = MakeCmd(r); break; + case CmdOp::GetMessage: + cmd = MakeCmd(r); break; + case CmdOp::GetVid: + cmd = MakeCmd(r); break; + case CmdOp::AddAgeCount: + cmd = MakeCmd(r); break; + case CmdOp::SetAgeCount: + cmd = MakeCmd(r); break; + case CmdOp::SendFlag: + cmd = MakeCmd(r); break; + case CmdOp::PitchWheelR: + cmd = MakeCmd(r); break; + case CmdOp::SetPriority: + cmd = MakeCmd(r); break; + case CmdOp::AddPriority: + cmd = MakeCmd(r); break; + case CmdOp::AgeCntSpeed: + cmd = MakeCmd(r); break; + case CmdOp::AgeCntVel: + cmd = MakeCmd(r); break; + case CmdOp::VolSelect: + cmd = MakeCmd(r); break; + case CmdOp::PanSelect: + cmd = MakeCmd(r); break; + case CmdOp::PitchWheelSelect: + cmd = MakeCmd(r); break; + case CmdOp::ModWheelSelect: + cmd = MakeCmd(r); break; + case CmdOp::PedalSelect: + cmd = MakeCmd(r); break; + case CmdOp::PortamentoSelect: + cmd = MakeCmd(r); break; + case CmdOp::ReverbSelect: + cmd = MakeCmd(r); break; + case CmdOp::SpanSelect: + cmd = MakeCmd(r); break; + case CmdOp::DopplerSelect: + cmd = MakeCmd(r); break; + case CmdOp::TremoloSelect: + cmd = MakeCmd(r); break; + case CmdOp::PreASelect: + cmd = MakeCmd(r); break; + case CmdOp::PreBSelect: + cmd = MakeCmd(r); break; + case CmdOp::PostBSelect: + cmd = MakeCmd(r); break; + case CmdOp::AuxAFXSelect: + cmd = MakeCmd(r); break; + case CmdOp::AuxBFXSelect: + cmd = MakeCmd(r); break; + case CmdOp::SetupLFO: + cmd = MakeCmd(r); break; + case CmdOp::ModeSelect: + cmd = MakeCmd(r); break; + case CmdOp::SetKeygroup: + cmd = MakeCmd(r); break; + case CmdOp::SRCmodeSelect: + cmd = MakeCmd(r); break; + case CmdOp::AddVars: + cmd = MakeCmd(r); break; + case CmdOp::SubVars: + cmd = MakeCmd(r); break; + case CmdOp::MulVars: + cmd = MakeCmd(r); break; + case CmdOp::DivVars: + cmd = MakeCmd(r); break; + case CmdOp::AddIVars: + cmd = MakeCmd(r); break; + case CmdOp::IfEqual: + cmd = MakeCmd(r); break; + case CmdOp::IfLess: + cmd = MakeCmd(r); break; + default: + break; + } + m_cmds.push_back(std::move(cmd)); + } +} +template void SoundMacro::readCmds(athena::io::IStreamReader& r, uint32_t size); +template void SoundMacro::readCmds(athena::io::IStreamReader& r, uint32_t size); + +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* AudioGroupPool::layer(ObjectId id) const +const std::vector* 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(search->second); + return static_cast(search->second.get()); +} + +template <> +void amuse::Curve::Enumerate(athena::io::IStreamReader& r) +{ + Log.report(logvisor::Fatal, "Curve binary DNA read not supported"); +} + +template <> +void amuse::Curve::Enumerate(athena::io::IStreamWriter& w) +{ + Log.report(logvisor::Fatal, "Curve binary DNA write not supported"); +} + +template <> +void amuse::Curve::Enumerate(size_t& sz) +{ + Log.report(logvisor::Fatal, "Curve binary DNA size not supported"); +} + +template <> +void amuse::Curve::Enumerate(athena::io::YAMLDocReader& r) +{ + r.enumerate(nullptr, data); +} + +template <> +void amuse::Curve::Enumerate(athena::io::YAMLDocWriter& w) +{ + w.enumerate(nullptr, data); +} + +const char* amuse::Curve::DNAType() +{ + return "amuse::ADSR"; } } diff --git a/lib/AudioGroupProject.cpp b/lib/AudioGroupProject.cpp index 59d5476..d82d686 100644 --- a/lib/AudioGroupProject.cpp +++ b/lib/AudioGroupProject.cpp @@ -1,548 +1,224 @@ #include "amuse/AudioGroupProject.hpp" #include "amuse/AudioGroupData.hpp" -#include "amuse/Common.hpp" -#include +#include "athena/MemoryReader.hpp" namespace amuse { -enum class GroupType : uint16_t +static bool AtEnd32(athena::io::IStreamReader& r) { - Song, - SFX -}; + uint32_t v = r.readUint32Big(); + r.seek(-4, athena::Current); + return v == 0xffffffff; +} -struct GroupHeader +static bool AtEnd16(athena::io::IStreamReader& r) { - 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; + uint16_t v = r.readUint16Big(); + r.seek(-2, athena::Current); + return v == 0xffff; +} - void swapBig() +AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag) +{ + while (!AtEnd32(r)) { - 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); - } -}; - -AudioGroupProject::AudioGroupProject(const unsigned char* data, GCNDataTag) -{ - const GroupHeader* group = reinterpret_cast(data); - while (group->groupEndOff != 0xffffffff) - { - GroupHeader header = *group; - header.swapBig(); - - AudioGroupIndex* bIdx = nullptr; + GroupHeader 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(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 entry; + entry.read(r); + idx.m_normPages[entry.programNo] = entry; } /* Drum pages */ - const SongGroupIndex::PageEntry* drumEntries = - reinterpret_cast(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 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(setupData)); - idx.m_midiSetups[songId] = - reinterpret_cast*>(setupData + 4); - setupData += 5 * 16 + 4; + uint16_t songId = r.readUint16Big(); + r.seek(2, athena::Current); + std::array& 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(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(data + header.pageTableOff + 4); for (int i = 0; i < count; ++i) { - idx.m_sfxEntries[SBig(entries->defineId)] = entries; - ++entries; + SFXGroupIndex::SFXEntryDNA entry; + entry.read(r); + idx.m_sfxEntries[entry.defineId.id] = entry; } } - if (bIdx) - { - bIdx->m_soundMacroIndex = reinterpret_cast(data + header.soundMacroIdsOff); - bIdx->m_tablesIndex = reinterpret_cast(data + header.tableIdsOff); - bIdx->m_keymapsIndex = reinterpret_cast(data + header.keymapIdsOff); - bIdx->m_layersIndex = reinterpret_cast(data + header.layerIdsOff); - } - - group = reinterpret_cast(data + header.groupEndOff); + r.seek(header.groupEndOff, athena::Begin); } } -struct MusyX1PageEntry +template +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(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 header; + header.read(r); if (header.type == GroupType::Song) { - /* Normal pages */ - const MusyX1PageEntry* normEntries = - reinterpret_cast(subData + header.pageTableOff); - while (normEntries->objId != 0xffff) - { - ++normPageCount; - ++normEntries; - } - - /* Drum pages */ - const MusyX1PageEntry* drumEntries = - reinterpret_cast(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(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[ 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* midiSetupsBuf = m_convMidiSetups.get(); - - const GroupHeader* group = reinterpret_cast(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(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 entry; + entry.read(r); + idx.m_normPages[entry.programNo] = entry; } /* Drum pages */ - const SongGroupIndex::PageEntry* drumEntries = - reinterpret_cast(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 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(setupData)); - idx.m_midiSetups[songId] = - reinterpret_cast*>(setupData + 4); - setupData += 5 * 16 + 4; + uint16_t songId = r.readUint16Big(); + r.seek(2, athena::Current); + std::array& setup = idx.m_midiSetups[songId]; + for (int i = 0; i < 16 ; ++i) + setup[i].read(r); } } else { /* Normal pages */ - const MusyX1PageEntry* normEntries = - reinterpret_cast(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 entry; + entry.read(r); + idx.m_normPages[entry.programNo] = entry; } /* Drum pages */ - const MusyX1PageEntry* drumEntries = - reinterpret_cast(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 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(setupData)); - const std::array* midiSetups = - reinterpret_cast*>(setupData + 4); - - for (int i = 0; i < 16; ++i) - (*midiSetups)[i].setIntoMusyX2((*midiSetupsBuf)[i]); - - idx.m_midiSetups[songId] = midiSetupsBuf; - setupData += 8 * 16 + 4; - ++midiSetupsBuf; + uint16_t songId = r.readUint16Big(); + r.seek(2, athena::Current); + std::array& setup = idx.m_midiSetups[songId]; + for (int i = 0; i < 16 ; ++i) + { + 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(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(subData + header.pageTableOff + 4 + i * 12); - idx.m_sfxEntries[SBig(entries->defineId)] = entries; + SFXGroupIndex::SFXEntryDNA entry; + entry.read(r); + r.seek(2, athena::Current); + idx.m_sfxEntries[entry.defineId.id] = entry; } } - if (bIdx) - { - bIdx->m_soundMacroIndex = reinterpret_cast(subData + header.soundMacroIdsOff); - bIdx->m_tablesIndex = reinterpret_cast(subData + header.tableIdsOff); - bIdx->m_keymapsIndex = reinterpret_cast(subData + header.keymapIdsOff); - bIdx->m_layersIndex = reinterpret_cast(subData + header.layerIdsOff); - } - if (absOffs) - group = reinterpret_cast(data + header.groupEndOff); + r.seek(header.groupEndOff, athena::Begin); else - { - data += header.groupEndOff; - group = reinterpret_cast(data); - } - } -} - -void AudioGroupProject::_allocateConvBuffers(const unsigned char* data, PCDataTag) -{ - size_t normPageCount = 0; - size_t drumPageCount = 0; - size_t midiSetupCount = 0; - - const GroupHeader* group = reinterpret_cast(data); - while (group->groupEndOff != 0xffffffff) - { - const unsigned char* subData = data + 8; - - if (group->type == GroupType::Song) - { - /* Normal pages */ - const MusyX1PageEntry* normEntries = - reinterpret_cast(subData + group->pageTableOff); - while (normEntries->objId != 0xffff) - { - ++normPageCount; - ++normEntries; - } - - /* Drum pages */ - const MusyX1PageEntry* drumEntries = - reinterpret_cast(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(data); + r.seek(groupBegin + header.groupEndOff, athena::Begin); } - 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[ 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* midiSetupsBuf = m_convMidiSetups.get(); - - const GroupHeader* group = reinterpret_cast(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(data + group->pageTableOff); - while (normEntries->objId != 0xffff) - { - idx.m_normPages[normEntries->programNo] = normEntries; - ++normEntries; - } - - /* Drum pages */ - const SongGroupIndex::PageEntry* drumEntries = - reinterpret_cast(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(setupData); - idx.m_midiSetups[songId] = - reinterpret_cast*>(setupData + 4); - setupData += 5 * 16 + 4; - } - } - else - { - /* Normal pages */ - const MusyX1PageEntry* normEntries = - reinterpret_cast(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(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(setupData); - const std::array* midiSetups = - reinterpret_cast*>(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(subData + group->pageTableOff); - idx.m_sfxEntries.reserve(count); - for (int i = 0; i < count; ++i) - { - const SFXGroupIndex::SFXEntry* entries = - reinterpret_cast(subData + group->pageTableOff + 4 + i * 12); - idx.m_sfxEntries[entries->defineId] = entries; - } - } - - if (bIdx) - { - bIdx->m_soundMacroIndex = reinterpret_cast(subData + group->soundMacroIdsOff); - bIdx->m_tablesIndex = reinterpret_cast(subData + group->tableIdsOff); - bIdx->m_keymapsIndex = reinterpret_cast(subData + group->keymapIdsOff); - bIdx->m_layersIndex = reinterpret_cast(subData + group->layerIdsOff); - } - - if (absOffs) - group = reinterpret_cast(data + group->groupEndOff); - else - { - data += group->groupEndOff; - group = reinterpret_cast(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(r, data.getAbsoluteProjOffsets()); case DataFormat::PC: - return AudioGroupProject(data.getProj(), data.getAbsoluteProjOffsets(), PCDataTag{}); + return _AudioGroupProject(r, data.getAbsoluteProjOffsets()); } } const SongGroupIndex* AudioGroupProject::getSongGroupIndex(int groupId) const { auto search = m_songGroups.find(groupId); - if (search == m_songGroups.cend()) - return nullptr; - return &search->second; + 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; - return &search->second; + if (search != m_sfxGroups.cend()) + return &search->second; + return nullptr; } + } diff --git a/lib/AudioGroupSampleDirectory.cpp b/lib/AudioGroupSampleDirectory.cpp index ff92f2c..107dd0e 100644 --- a/lib/AudioGroupSampleDirectory.cpp +++ b/lib/AudioGroupSampleDirectory.cpp @@ -1,5 +1,6 @@ #include "amuse/AudioGroupSampleDirectory.hpp" #include "amuse/Common.hpp" +#include "amuse/AudioGroupData.hpp" #include 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{}); + } +} } diff --git a/lib/Common.cpp b/lib/Common.cpp new file mode 100644 index 0000000..8408cfd --- /dev/null +++ b/lib/Common.cpp @@ -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::Enumerate(athena::io::IStreamReader& reader) +{ + id = reader.readUint16Little(); +} + +template<> template<> +void ObjectIdDNA::Enumerate(athena::io::IStreamWriter& writer) +{ + writer.writeUint16Little(id); +} + +template<> template<> +void ObjectIdDNA::Enumerate(size_t& sz) +{ + sz += 2; +} + +template<> template<> +void ObjectIdDNA::Enumerate(athena::io::YAMLDocReader& reader) +{ + _read(reader); +} + +template<> template<> +void ObjectIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) +{ + _write(writer); +} + +template<> template<> +void ObjectIdDNA::Enumerate(athena::io::IStreamReader& reader) +{ + id = reader.readUint16Big(); +} + +template<> template<> +void ObjectIdDNA::Enumerate(athena::io::IStreamWriter& writer) +{ + writer.writeUint16Big(id); +} + +template<> template<> +void ObjectIdDNA::Enumerate(size_t& sz) +{ + sz += 2; +} + +template<> template<> +void ObjectIdDNA::Enumerate(athena::io::YAMLDocReader& reader) +{ + _read(reader); +} + +template<> template<> +void ObjectIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) +{ + _write(writer); +} + +template +void ObjectIdDNA::_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 +void ObjectIdDNA::_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 +const char* ObjectIdDNA::DNAType() +{ + return "amuse::ObjectId"; +} + +template<> template<> +void SampleIdDNA::Enumerate(athena::io::IStreamReader& reader) +{ + id = reader.readUint16Little(); +} + +template<> template<> +void SampleIdDNA::Enumerate(athena::io::IStreamWriter& writer) +{ + writer.writeUint16Little(id); +} + +template<> template<> +void SampleIdDNA::Enumerate(size_t& sz) +{ + sz += 2; +} + +template<> template<> +void SampleIdDNA::Enumerate(athena::io::YAMLDocReader& reader) +{ + _read(reader); +} + +template<> template<> +void SampleIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) +{ + _write(writer); +} + +template<> template<> +void SampleIdDNA::Enumerate(athena::io::IStreamReader& reader) +{ + id = reader.readUint16Big(); +} + +template<> template<> +void SampleIdDNA::Enumerate(athena::io::IStreamWriter& writer) +{ + writer.writeUint16Big(id); +} + +template<> template<> +void SampleIdDNA::Enumerate(size_t& sz) +{ + sz += 2; +} + +template<> template<> +void SampleIdDNA::Enumerate(athena::io::YAMLDocReader& reader) +{ + _read(reader); +} + +template<> template<> +void SampleIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) +{ + _write(writer); +} + +template +void SampleIdDNA::_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 +void SampleIdDNA::_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 +const char* SampleIdDNA::DNAType() +{ + return "amuse::SampleId"; +} + +template<> template<> +void SongIdDNA::Enumerate(athena::io::IStreamReader& reader) +{ + id = reader.readUint16Little(); +} + +template<> template<> +void SongIdDNA::Enumerate(athena::io::IStreamWriter& writer) +{ + writer.writeUint16Little(id); +} + +template<> template<> +void SongIdDNA::Enumerate(size_t& sz) +{ + sz += 2; +} + +template<> template<> +void SongIdDNA::Enumerate(athena::io::YAMLDocReader& reader) +{ + _read(reader); +} + +template<> template<> +void SongIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) +{ + _write(writer); +} + +template<> template<> +void SongIdDNA::Enumerate(athena::io::IStreamReader& reader) +{ + id = reader.readUint16Big(); +} + +template<> template<> +void SongIdDNA::Enumerate(athena::io::IStreamWriter& writer) +{ + writer.writeUint16Big(id); +} + +template<> template<> +void SongIdDNA::Enumerate(size_t& sz) +{ + sz += 2; +} + +template<> template<> +void SongIdDNA::Enumerate(athena::io::YAMLDocReader& reader) +{ + _read(reader); +} + +template<> template<> +void SongIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) +{ + _write(writer); +} + +template +void SongIdDNA::_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 +void SongIdDNA::_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 +const char* SongIdDNA::DNAType() +{ + return "amuse::SongId"; +} + +template<> template<> +void SFXIdDNA::Enumerate(athena::io::IStreamReader& reader) +{ + id = reader.readUint16Little(); +} + +template<> template<> +void SFXIdDNA::Enumerate(athena::io::IStreamWriter& writer) +{ + writer.writeUint16Little(id); +} + +template<> template<> +void SFXIdDNA::Enumerate(size_t& sz) +{ + sz += 2; +} + +template<> template<> +void SFXIdDNA::Enumerate(athena::io::YAMLDocReader& reader) +{ + _read(reader); +} + +template<> template<> +void SFXIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) +{ + _write(writer); +} + +template<> template<> +void SFXIdDNA::Enumerate(athena::io::IStreamReader& reader) +{ + id = reader.readUint16Big(); +} + +template<> template<> +void SFXIdDNA::Enumerate(athena::io::IStreamWriter& writer) +{ + writer.writeUint16Big(id); +} + +template<> template<> +void SFXIdDNA::Enumerate(size_t& sz) +{ + sz += 2; +} + +template<> template<> +void SFXIdDNA::Enumerate(athena::io::YAMLDocReader& reader) +{ + _read(reader); +} + +template<> template<> +void SFXIdDNA::Enumerate(athena::io::YAMLDocWriter& writer) +{ + _write(writer); +} + +template +void SFXIdDNA::_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 +void SFXIdDNA::_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 +const char* SFXIdDNA::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; +template struct ObjectIdDNA; + +template struct SampleIdDNA; +template struct SampleIdDNA; + +template<> +void LittleUInt24::Enumerate(athena::io::IStreamReader& reader) +{ + union + { + atUint32 val; + char bytes[4]; + } data = {}; + reader.readBytesToBuf(data.bytes, 3); + val = SLittle(data.val); +} + +template<> +void LittleUInt24::Enumerate(athena::io::IStreamWriter& writer) +{ + union + { + atUint32 val; + char bytes[4]; + } data; + data.val = SLittle(val); + writer.writeBytes(data.bytes, 3); +} + +template<> +void LittleUInt24::Enumerate(size_t& sz) +{ + sz += 3; +} + +template<> +void LittleUInt24::Enumerate(athena::io::YAMLDocReader& reader) +{ + val = reader.readUint32(nullptr); +} + +template<> +void LittleUInt24::Enumerate(athena::io::YAMLDocWriter& writer) +{ + writer.writeUint32(nullptr, val); +} + +const char* LittleUInt24::DNAType() +{ + return "amuse::LittleUInt24"; +} + +} diff --git a/lib/ContainerRegistry.cpp b/lib/ContainerRegistry.cpp index df0c8ff..5114fce 100644 --- a/lib/ContainerRegistry.cpp +++ b/lib/ContainerRegistry.cpp @@ -1960,6 +1960,9 @@ std::vector> ContainerRegistry: { std::vector> 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> ContainerRegistry: /* SDIR-based format detection */ if (*reinterpret_cast(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{}}); diff --git a/lib/Engine.cpp b/lib/Engine.cpp index e7145f7..d253483 100644 --- a/lib/Engine.cpp +++ b/lib/Engine.cpp @@ -203,7 +203,7 @@ AudioGroup* Engine::_addAudioGroup(const AudioGroupData& data, std::unique_ptr Engine::fxStart(int sfxId, float vol, float pan, std::wea std::list>::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 Engine::addEmitter(const float* pos, const float* dir, std::list>::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 {}; diff --git a/lib/Sequencer.cpp b/lib/Sequencer.cpp index f5f6c44..5462582 100644 --- a/lib/Sequencer.cpp +++ b/lib/Sequencer.cpp @@ -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 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 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; } diff --git a/lib/SoundMacroState.cpp b/lib/SoundMacroState.cpp index 3ba2961..a556d4e 100644 --- a/lib/SoundMacroState.cpp +++ b/lib/SoundMacroState.cpp @@ -13,32 +13,6 @@ namespace amuse { -int SoundMacroState::_assertPC(int pc, uint32_t size) -{ - if (pc == -1) - return -1; - int cmdCount = (size - sizeof(Header)) / sizeof(Command); - if (pc >= cmdCount) - { - fprintf(stderr, "SoundMacro PC bounds exceeded [%d/%d]\n", pc, cmdCount); - abort(); - } - return pc; -} - -void SoundMacroState::Header::swapBig() -{ - m_size = SBig(m_size); - m_macroId = SBig(m_macroId); -} - -void SoundMacroState::Command::swapBig() -{ - uint32_t* words = reinterpret_cast(this); - words[0] = SBig(words[0]); - words[1] = SBig(words[1]); -} - void SoundMacroState::Evaluator::addComponent(uint8_t midiCtrl, float scale, Combine combine, VarType varType) { m_comps.push_back({midiCtrl, scale, combine, varType}); @@ -130,13 +104,13 @@ float SoundMacroState::Evaluator::evaluate(double time, const Voice& vox, const return value; } -void SoundMacroState::initialize(const unsigned char* ptr, int step, bool swapData) +void SoundMacroState::initialize(ObjectId id, const SoundMacro* macro, int step) { - initialize(ptr, step, 1000.f, 0, 0, 0, swapData); + initialize(id, macro, step, 1000.f, 0, 0, 0); } -void SoundMacroState::initialize(const unsigned char* ptr, int step, double ticksPerSec, uint8_t midiKey, - uint8_t midiVel, uint8_t midiMod, bool swapData) +void SoundMacroState::initialize(ObjectId id, const SoundMacro* macro, int step, double ticksPerSec, + uint8_t midiKey, uint8_t midiVel, uint8_t midiMod) { m_ticksPerSec = ticksPerSec; m_initKey = midiKey; @@ -146,8 +120,7 @@ void SoundMacroState::initialize(const unsigned char* ptr, int step, double tick m_curMod = midiMod; m_curPitch = midiKey * 100; m_pc.clear(); - const Header& header = reinterpret_cast(ptr); - m_pc.push_back({ptr, _assertPC(step, header.m_size, swapData)}); + m_pc.emplace_back(id, macro, macro->assertPC(step)); m_inWait = false; m_execTime = 0.f; m_keyoff = false; @@ -157,15 +130,945 @@ void SoundMacroState::initialize(const unsigned char* ptr, int step, double tick m_useAdsrControllers = false; m_portamentoMode = 2; m_portamentoTime = 0.5f; - m_header = header; - if (swapData) - m_header.swapBig(); +} + +bool SoundMacro::CmdEnd::Do(SoundMacroState& st, Voice& vox) const +{ + st._setPC(-1); + return true; +} + +bool SoundMacro::CmdStop::Do(SoundMacroState& st, Voice& vox) const +{ + st._setPC(-1); + return true; +} + +bool SoundMacro::CmdSplitKey::Do(SoundMacroState& st, Voice& vox) const +{ + if (st.m_initKey >= key) + { + /* Do Branch */ + if (macro.id == std::get<0>(st.m_pc.back())) + st._setPC(macroStep); + else + vox.loadSoundObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod); + } + + return false; +} + +bool SoundMacro::CmdSplitVel::Do(SoundMacroState& st, Voice& vox) const +{ + if (st.m_curVel >= velocity) + { + /* Do Branch */ + if (macro.id == std::get<0>(st.m_pc.back())) + st._setPC(macroStep); + else + vox.loadSoundObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod); + } + + return false; +} + +bool SoundMacro::CmdWaitTicks::Do(SoundMacroState& st, Voice& vox) const +{ + /* Set wait state */ + if (ticksPerMs >= 0) + { + float q = msSwitch ? 1000.f : st.m_ticksPerSec; + float secTime = ticksPerMs / q; + /* Randomize at the proper resolution */ + if (random) + secTime = std::fmod(vox.getEngine().nextRandom() / q, secTime); + + if (absolute) + { + if (secTime <= st.m_execTime) + return false; + st.m_waitCountdown = secTime - st.m_execTime; + } + else + st.m_waitCountdown = secTime; + + st.m_indefiniteWait = false; + } + else + st.m_indefiniteWait = true; + + st.m_inWait = true; + st.m_keyoffWait = keyOff; + st.m_sampleEndWait = sampleEnd; + + return false; +} + +bool SoundMacro::CmdLoop::Do(SoundMacroState& st, Voice& vox) const +{ + if ((keyOff && st.m_keyoff) || (sampleEnd && st.m_sampleEnd)) + { + /* Break out of loop */ + st.m_loopCountdown = -1; + return false; + } + + uint32_t useTimes = times; + if (random) + useTimes = vox.getEngine().nextRandom() % times; + + if (st.m_loopCountdown == -1 && useTimes != -1) + st.m_loopCountdown = useTimes; + + if (st.m_loopCountdown > 0) + { + /* Loop back to step */ + --st.m_loopCountdown; + st._setPC(macroStep); + } + else /* Break out of loop */ + st.m_loopCountdown = -1; + + return false; +} + +bool SoundMacro::CmdGoto::Do(SoundMacroState& st, Voice& vox) const +{ + /* Do Branch */ + if (macro.id == std::get<0>(st.m_pc.back())) + st._setPC(macroStep); + else + vox.loadSoundObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod); + + return false; +} + +bool SoundMacro::CmdWaitMs::Do(SoundMacroState& st, Voice& vox) const +{ + /* Set wait state */ + if (ms >= 0) + { + float secTime = ms / 1000.f; + /* Randomize at the proper resolution */ + if (random) + secTime = std::fmod(vox.getEngine().nextRandom() / 1000.f, secTime); + + if (absolute) + { + if (secTime <= st.m_execTime) + return false; + st.m_waitCountdown = secTime - st.m_execTime; + } + else + st.m_waitCountdown = secTime; + + st.m_indefiniteWait = false; + } + else + st.m_indefiniteWait = true; + + st.m_inWait = true; + st.m_keyoffWait = keyOff; + st.m_sampleEndWait = sampleEnd; + + return false; +} + +bool SoundMacro::CmdPlayMacro::Do(SoundMacroState& st, Voice& vox) const +{ + std::shared_ptr sibVox = vox.startChildMacro(addNote, macro.id, macroStep); + if (sibVox) + st.m_lastPlayMacroVid = sibVox->vid(); + + return false; +} + +bool SoundMacro::CmdSendKeyOff::Do(SoundMacroState& st, Voice& vox) const +{ + if (lastStarted) + { + if (st.m_lastPlayMacroVid != -1) + { + std::shared_ptr otherVox = vox.getEngine().findVoice(st.m_lastPlayMacroVid); + if (otherVox) + otherVox->keyOff(); + } + } + else + { + std::shared_ptr otherVox = vox.getEngine().findVoice(st.m_variables[variable]); + if (otherVox) + otherVox->keyOff(); + } + + return false; +} + +bool SoundMacro::CmdSplitMod::Do(SoundMacroState& st, Voice& vox) const +{ + if (st.m_curMod >= modValue) + { + /* Do Branch */ + if (macro.id == std::get<0>(st.m_pc.back())) + st._setPC(macroStep); + else + vox.loadSoundObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod); + } + + return false; +} + +bool SoundMacro::CmdPianoPan::Do(SoundMacroState& st, Voice& vox) const +{ + int32_t pan = int32_t(st.m_initKey - centerKey) * scale / 127 + centerPan; + pan = std::max(-127, std::min(127, pan)); + vox.setPan(pan / 127.f); + + return false; +} + +bool SoundMacro::CmdSetAdsr::Do(SoundMacroState& st, Voice& vox) const +{ + vox.setAdsr(table.id, dlsMode); + return false; +} + +bool SoundMacro::CmdScaleVolume::Do(SoundMacroState& st, Voice& vox) const +{ + int32_t eval = int32_t(originalVol ? st.m_initVel : st.m_curVel) * scale / 127 + add; + eval = clamp(0, eval, 127); + + if (table.id != 0) + { + const Curve* curveData = vox.getAudioGroup().getPool().tableAsCurves(table.id); + if (curveData) + { + vox.m_curVol = curveData->data.at(eval) / 127.f; + return false; + } + } + + vox.m_curVol = eval / 127.f; + + return false; +} + +bool SoundMacro::CmdPanning::Do(SoundMacroState& st, Voice& vox) const +{ + vox.startPanning(timeMs / 1000.0, panPosition, width); + return false; +} + +bool SoundMacro::CmdEnvelope::Do(SoundMacroState& st, Voice& vox) const +{ + double q = msSwitch ? 1000.0 : st.m_ticksPerSec; + double secTime = fadeTime / q; + + int32_t eval = int32_t(st.m_curVel) * scale / 127 + add; + eval = clamp(0, eval, 127); + + const Curve* curveData; + if (table.id != 0) + curveData = vox.getAudioGroup().getPool().tableAsCurves(table.id); + else + curveData = nullptr; + + vox.startEnvelope(secTime, eval, curveData); + + return false; +} + +bool SoundMacro::CmdStartSample::Do(SoundMacroState& st, Voice& vox) const +{ + uint32_t useOffset = offset; + + switch (mode) + { + case 1: + useOffset = offset * (127 - st.m_curVel) / 127; + break; + case 2: + useOffset = offset * st.m_curVel / 127; + break; + default: + break; + } + + vox.startSample(sample.id, useOffset); + vox.setPitchKey(st.m_curPitch); + + return false; +} + +bool SoundMacro::CmdStopSample::Do(SoundMacroState& st, Voice& vox) const +{ + vox.stopSample(); + return false; +} + +bool SoundMacro::CmdKeyOff::Do(SoundMacroState& st, Voice& vox) const +{ + vox._macroKeyOff(); + return false; +} + +bool SoundMacro::CmdSplitRnd::Do(SoundMacroState& st, Voice& vox) const +{ + if (rnd <= vox.getEngine().nextRandom() % 256) + { + /* Do branch */ + if (macro.id == std::get<0>(st.m_pc.back())) + st._setPC(macroStep); + else + vox.loadSoundObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod); + } + + return false; +} + +bool SoundMacro::CmdFadeIn::Do(SoundMacroState& st, Voice& vox) const +{ + float q = msSwitch ? 1000.f : st.m_ticksPerSec; + float secTime = ticksPerMs / q; + + int32_t eval = int32_t(st.m_curVel) * scale / 127 + add; + eval = clamp(0, eval, 127); + + const Curve* curveData; + if (table.id != 0) + curveData = vox.getAudioGroup().getPool().tableAsCurves(table.id); + else + curveData = nullptr; + + vox.startFadeIn(secTime, eval, curveData); + + return false; +} + +bool SoundMacro::CmdSpanning::Do(SoundMacroState& st, Voice& vox) const +{ + vox.startSpanning(timeMs / 1000.0, spanPosition, width); + return false; +} + +bool SoundMacro::CmdSetAdsrCtrl::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_useAdsrControllers = true; + st.m_midiAttack = attack; + st.m_midiDecay = decay; + st.m_midiSustain = sustain; + st.m_midiRelease = release; + + /* Bootstrap ADSR defaults here */ + if (!vox.getCtrlValue(st.m_midiSustain)) + { + vox.setCtrlValue(st.m_midiAttack, 10); + vox.setCtrlValue(st.m_midiSustain, 127); + vox.setCtrlValue(st.m_midiRelease, 10); + } + + return false; +} + +bool SoundMacro::CmdRndNote::Do(SoundMacroState& st, Voice& vox) const +{ + int32_t useNoteLo = noteLo; + int32_t useNoteHi = noteHi; + + if (absRel) + { + useNoteLo = st.m_initKey - noteLo; + useNoteHi = noteLo + noteHi; + } + + useNoteLo *= 100; + useNoteHi *= 100; + + if (useNoteHi == useNoteLo) + st.m_curPitch = useNoteHi; + else + st.m_curPitch = (vox.getEngine().nextRandom() % (useNoteHi - useNoteLo)) + useNoteLo; + + if (!fixedFree) + st.m_curPitch = st.m_curPitch / 100 * 100 + detune; + + vox.setPitchKey(st.m_curPitch); + + return false; +} + +bool SoundMacro::CmdAddNote::Do(SoundMacroState& st, Voice& vox) const +{ + /* Set wait state */ + if (msSwitch) + { + float q = msSwitch ? 1000.f : st.m_ticksPerSec; + float secTime = ticksPerMs / q; + st.m_waitCountdown = secTime; + st.m_inWait = true; + } + + vox.setPitchKey(st.m_curPitch); + + return false; +} + +bool SoundMacro::CmdSetNote::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_curPitch = key * 100 + detune; + + /* Set wait state */ + if (ticksPerMs) + { + float q = msSwitch ? 1000.f : st.m_ticksPerSec; + float secTime = ticksPerMs / q; + st.m_waitCountdown = secTime; + st.m_inWait = true; + } + + vox.setPitchKey(st.m_curPitch); + + return false; +} + +bool SoundMacro::CmdLastNote::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_curPitch = (add + vox.getLastNote()) * 100 + detune; + + /* Set wait state */ + if (msSwitch) + { + float q = msSwitch ? 1000.f : st.m_ticksPerSec; + float secTime = ticksPerMs / q; + st.m_waitCountdown = secTime; + st.m_inWait = true; + } + + vox.setPitchKey(st.m_curPitch); + + return false; +} + +bool SoundMacro::CmdPortamento::Do(SoundMacroState& st, Voice& vox) const +{ + float q = msSwitch ? 1000.f : st.m_ticksPerSec; + st.m_portamentoTime = ticksPerMs / q; + return false; +} + +bool SoundMacro::CmdVibrato::Do(SoundMacroState& st, Voice& vox) const +{ + float q = msSwitch ? 1000.f : st.m_ticksPerSec; + vox.setVibrato(levelNote * 100 + levelFine, modwheelFlag, ticksPerMs / q); + return false; +} + +bool SoundMacro::CmdPitchSweep1::Do(SoundMacroState& st, Voice& vox) const +{ + /* Set wait state */ + if (msSwitch) + { + float q = msSwitch ? 1000.f : st.m_ticksPerSec; + float secTime = ticksPerMs / q; + st.m_waitCountdown = secTime; + st.m_inWait = true; + } + + vox.setPitchSweep1(times, add); + + return false; +} + +bool SoundMacro::CmdPitchSweep2::Do(SoundMacroState& st, Voice& vox) const +{ + /* Set wait state */ + if (msSwitch) + { + float q = msSwitch ? 1000.f : st.m_ticksPerSec; + float secTime = ticksPerMs / q; + st.m_waitCountdown = secTime; + st.m_inWait = true; + } + + vox.setPitchSweep2(times, add); + + return false; +} + +bool SoundMacro::CmdSetPitch::Do(SoundMacroState& st, Voice& vox) const +{ + vox.setPitchFrequency(hz, fine); + return false; +} + +bool SoundMacro::CmdSetPitchAdsr::Do(SoundMacroState& st, Voice& vox) const +{ + vox.setPitchAdsr(table.id, keys * 100 + cents); + return false; +} + +bool SoundMacro::CmdScaleVolumeDLS::Do(SoundMacroState& st, Voice& vox) const +{ + vox.m_curVol = int32_t(originalVol ? st.m_initVel : st.m_curVel) * scale / 4096.f / 127.f; + return false; +} + +bool SoundMacro::CmdMod2Vibrange::Do(SoundMacroState& st, Voice& vox) const +{ + vox.setMod2VibratoRange(keys * 100 + cents); + return false; +} + +bool SoundMacro::CmdSetupTremolo::Do(SoundMacroState& st, Voice& vox) const +{ + vox.setTremolo(scale / 4096.f, scale / 4096.f); + return false; +} + +bool SoundMacro::CmdReturn::Do(SoundMacroState& st, Voice& vox) const +{ + if (st.m_pc.size() > 1) + { + st.m_pc.pop_back(); + vox._setObjectId(std::get<0>(st.m_pc.back())); + } + return false; +} + +bool SoundMacro::CmdGoSub::Do(SoundMacroState& st, Voice& vox) const +{ + if (macro.id == std::get<0>(st.m_pc.back())) + st.m_pc.emplace_back(std::get<0>(st.m_pc.back()), std::get<1>(st.m_pc.back()), + std::get<1>(st.m_pc.back())->assertPC(macroStep)); + else + vox.loadSoundObject(macro.id, macroStep, st.m_ticksPerSec, st.m_initKey, st.m_initVel, st.m_initMod, true); + + + vox._setObjectId(std::get<0>(st.m_pc.back())); + + return false; +} + +bool SoundMacro::CmdTrapEvent::Do(SoundMacroState& st, Voice& vox) const +{ + switch (event) + { + case 0: + vox.m_keyoffTrap.macroId = macro.id; + vox.m_keyoffTrap.macroStep = macroStep; + break; + case 1: + vox.m_sampleEndTrap.macroId = macro.id; + vox.m_sampleEndTrap.macroStep = macroStep; + break; + case 2: + vox.m_messageTrap.macroId = macro.id; + vox.m_messageTrap.macroStep = macroStep; + break; + default: + break; + } + + return false; +} + +bool SoundMacro::CmdUntrapEvent::Do(SoundMacroState& st, Voice& vox) const +{ + switch (event) + { + case 0: + vox.m_keyoffTrap.macroId = 0xffff; + vox.m_keyoffTrap.macroStep = 0xffff; + break; + case 1: + vox.m_sampleEndTrap.macroId = 0xffff; + vox.m_sampleEndTrap.macroStep = 0xffff; + break; + case 2: + vox.m_messageTrap.macroId = 0xffff; + vox.m_messageTrap.macroStep = 0xffff; + break; + default: + break; + } + + return false; +} + +bool SoundMacro::CmdSendMessage::Do(SoundMacroState& st, Voice& vox) const +{ + if (isVar) + { + std::shared_ptr findVox = vox.getEngine().findVoice(st.m_variables[vid]); + if (findVox) + findVox->message(variable); + } + else + vox.getEngine().sendMacroMessage(macro.id, variable); + + return false; +} + +bool SoundMacro::CmdGetMessage::Do(SoundMacroState& st, Voice& vox) const +{ + if (vox.m_messageQueue.size()) + { + st.m_variables[variable] = vox.m_messageQueue.front(); + vox.m_messageQueue.pop_front(); + } + else + st.m_variables[variable] = 0; + + return false; +} + +bool SoundMacro::CmdGetVid::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_variables[variable] = playMacro ? st.m_lastPlayMacroVid : vox.vid(); + return false; +} + +bool SoundMacro::CmdAddAgeCount::Do(SoundMacroState& st, Voice& vox) const +{ + return false; +} + +bool SoundMacro::CmdSetAgeCount::Do(SoundMacroState& st, Voice& vox) const +{ + return false; +} + +bool SoundMacro::CmdSendFlag::Do(SoundMacroState& st, Voice& vox) const +{ + /* TODO: figure out a good API */ + return false; +} + +bool SoundMacro::CmdPitchWheelR::Do(SoundMacroState& st, Voice& vox) const +{ + vox.setPitchWheelRange(rangeUp, rangeDown); + return false; +} + +bool SoundMacro::CmdSetPriority::Do(SoundMacroState& st, Voice& vox) const +{ + return false; +} + +bool SoundMacro::CmdAddPriority::Do(SoundMacroState& st, Voice& vox) const +{ + return false; +} + +bool SoundMacro::CmdAgeCntSpeed::Do(SoundMacroState& st, Voice& vox) const +{ + return false; +} + +bool SoundMacro::CmdAgeCntVel::Do(SoundMacroState& st, Voice& vox) const +{ + return false; +} + +bool SoundMacro::CmdVolSelect::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_volumeSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f, + SoundMacroState::Evaluator::Combine(combine), + SoundMacroState::Evaluator::VarType(isVar)); + return false; +} + +bool SoundMacro::CmdPanSelect::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_panSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f, + SoundMacroState::Evaluator::Combine(combine), + SoundMacroState::Evaluator::VarType(isVar)); + return false; +} + +bool SoundMacro::CmdPitchWheelSelect::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_pitchWheelSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f, + SoundMacroState::Evaluator::Combine(combine), + SoundMacroState::Evaluator::VarType(isVar)); + return false; +} + +bool SoundMacro::CmdModWheelSelect::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_modWheelSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f, + SoundMacroState::Evaluator::Combine(combine), + SoundMacroState::Evaluator::VarType(isVar)); + return false; +} + +bool SoundMacro::CmdPedalSelect::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_pedalSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f, + SoundMacroState::Evaluator::Combine(combine), + SoundMacroState::Evaluator::VarType(isVar)); + return false; +} + +bool SoundMacro::CmdPortamentoSelect::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_portamentoSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f, + SoundMacroState::Evaluator::Combine(combine), + SoundMacroState::Evaluator::VarType(isVar)); + return false; +} + +bool SoundMacro::CmdReverbSelect::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_reverbSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f, + SoundMacroState::Evaluator::Combine(combine), + SoundMacroState::Evaluator::VarType(isVar)); + return false; +} + +bool SoundMacro::CmdSpanSelect::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_spanSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f, + SoundMacroState::Evaluator::Combine(combine), + SoundMacroState::Evaluator::VarType(isVar)); + return false; +} + +bool SoundMacro::CmdDopplerSelect::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_dopplerSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f, + SoundMacroState::Evaluator::Combine(combine), + SoundMacroState::Evaluator::VarType(isVar)); + return false; +} + +bool SoundMacro::CmdTremoloSelect::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_tremoloSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f, + SoundMacroState::Evaluator::Combine(combine), + SoundMacroState::Evaluator::VarType(isVar)); + return false; +} + +bool SoundMacro::CmdPreASelect::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_preAuxASel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f, + SoundMacroState::Evaluator::Combine(combine), + SoundMacroState::Evaluator::VarType(isVar)); + return false; +} + +bool SoundMacro::CmdPreBSelect::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_preAuxBSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f, + SoundMacroState::Evaluator::Combine(combine), + SoundMacroState::Evaluator::VarType(isVar)); + return false; +} + +bool SoundMacro::CmdPostBSelect::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_postAuxB.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f, + SoundMacroState::Evaluator::Combine(combine), + SoundMacroState::Evaluator::VarType(isVar)); + return false; +} + +bool SoundMacro::CmdAuxAFXSelect::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_auxAFxSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f, + SoundMacroState::Evaluator::Combine(combine), + SoundMacroState::Evaluator::VarType(isVar)); + return false; +} + +bool SoundMacro::CmdAuxBFXSelect::Do(SoundMacroState& st, Voice& vox) const +{ + st.m_auxBFxSel.addComponent(midiControl, (scalingPercentage + fineScaling / 100.f) / 100.f, + SoundMacroState::Evaluator::Combine(combine), + SoundMacroState::Evaluator::VarType(isVar)); + return false; +} + +bool SoundMacro::CmdSetupLFO::Do(SoundMacroState& st, Voice& vox) const +{ + if (lfoNumber == 0) + vox.setLFO1Period(periodInMs / 1000.f); + else if (lfoNumber == 1) + vox.setLFO2Period(periodInMs / 1000.f); + return false; +} + +bool SoundMacro::CmdModeSelect::Do(SoundMacroState& st, Voice& vox) const +{ + return false; +} + +bool SoundMacro::CmdSetKeygroup::Do(SoundMacroState& st, Voice& vox) const +{ + vox.setKeygroup(0); + if (group) + { + vox.getEngine().killKeygroup(group, killNow); + vox.setKeygroup(group); + } + return false; +} + +bool SoundMacro::CmdSRCmodeSelect::Do(SoundMacroState& st, Voice& vox) const +{ + return false; +} + +bool SoundMacro::CmdAddVars::Do(SoundMacroState& st, Voice& vox) const +{ + int32_t useB, useC; + + if (varCtrlB) + useB = vox.getCtrlValue(b); + else + useB = st.m_variables[b]; + + if (varCtrlC) + useC = vox.getCtrlValue(c); + else + useC = st.m_variables[c]; + + if (varCtrlA) + vox.setCtrlValue(a, useB + useC); + else + st.m_variables[a] = useB + useC; + + return false; +} + +bool SoundMacro::CmdSubVars::Do(SoundMacroState& st, Voice& vox) const +{ + int32_t useB, useC; + + if (varCtrlB) + useB = vox.getCtrlValue(b); + else + useB = st.m_variables[b]; + + if (varCtrlC) + useC = vox.getCtrlValue(c); + else + useC = st.m_variables[c]; + + if (varCtrlA) + vox.setCtrlValue(a, useB - useC); + else + st.m_variables[a] = useB - useC; + + return false; +} + +bool SoundMacro::CmdMulVars::Do(SoundMacroState& st, Voice& vox) const +{ + int32_t useB, useC; + + if (varCtrlB) + useB = vox.getCtrlValue(b); + else + useB = st.m_variables[b]; + + if (varCtrlC) + useC = vox.getCtrlValue(c); + else + useC = st.m_variables[c]; + + if (varCtrlA) + vox.setCtrlValue(a, useB * useC); + else + st.m_variables[a] = useB * useC; + + return false; +} + +bool SoundMacro::CmdDivVars::Do(SoundMacroState& st, Voice& vox) const +{ + int32_t useB, useC; + + if (varCtrlB) + useB = vox.getCtrlValue(b); + else + useB = st.m_variables[b]; + + if (varCtrlC) + useC = vox.getCtrlValue(c); + else + useC = st.m_variables[c]; + + if (varCtrlA) + vox.setCtrlValue(a, useB / useC); + else + st.m_variables[a] = useB / useC; + + return false; +} + +bool SoundMacro::CmdAddIVars::Do(SoundMacroState& st, Voice& vox) const +{ + int32_t useB; + + if (varCtrlB) + useB = vox.getCtrlValue(b); + else + useB = st.m_variables[b]; + + if (varCtrlA) + vox.setCtrlValue(a, useB + imm); + else + st.m_variables[a] = useB + imm; + + return false; +} + +bool SoundMacro::CmdIfEqual::Do(SoundMacroState& st, Voice& vox) const +{ + int32_t useA, useB; + + if (varCtrlA) + useA = vox.getCtrlValue(a); + else + useA = st.m_variables[a]; + + if (varCtrlB) + useB = vox.getCtrlValue(b); + else + useB = st.m_variables[b]; + + if ((useA == useB) ^ notEq) + st._setPC(macroStep); + + return false; +} + +bool SoundMacro::CmdIfLess::Do(SoundMacroState& st, Voice& vox) const +{ + int32_t useA, useB; + + if (varCtrlA) + useA = vox.getCtrlValue(a); + else + useA = st.m_variables[a]; + + if (varCtrlB) + useB = vox.getCtrlValue(b); + else + useB = st.m_variables[b]; + + if ((useA < useB) ^ notLt) + st._setPC(macroStep); + + return false; } bool SoundMacroState::advance(Voice& vox, double dt) { /* Nothing if uninitialized or finished */ - if (m_pc.empty() || m_pc.back().first == nullptr || m_pc.back().second == -1) + if (m_pc.empty() || std::get<1>(m_pc.back()) == nullptr || std::get<2>(m_pc.back()) == -1) return true; /* Loop through as many commands as we can for this time period */ @@ -193,1064 +1096,11 @@ bool SoundMacroState::advance(Voice& vox, double dt) } /* Load next command based on counter */ - const Command* commands = reinterpret_cast(m_pc.back().first + sizeof(Header)); - _assertPC(m_pc.back().second, m_header.m_size); - Command cmd = commands[m_pc.back().second++]; - if (vox.getAudioGroup().getDataFormat() != DataFormat::PC) - cmd.swapBig(); + const SoundMacro::ICmd& cmd = std::get<1>(m_pc.back())->getCmd(std::get<2>(m_pc.back())++); /* Perform function of command */ - switch (cmd.m_op) - { - case Op::End: - case Op::Stop: - _setPC(-1); + if (cmd.Do(*this, vox)) return true; - case Op::SplitKey: - { - uint8_t keyNumber = cmd.m_data[0]; - ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); - int16_t macroStep = *reinterpret_cast(&cmd.m_data[3]); - - if (m_initKey >= keyNumber) - { - /* Do Branch */ - if (macroId == m_header.m_macroId) - _setPC(macroStep); - else - vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod); - } - - break; - } - case Op::SplitVel: - { - uint8_t velocity = cmd.m_data[0]; - ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); - int16_t macroStep = *reinterpret_cast(&cmd.m_data[3]); - - if (m_curVel >= velocity) - { - /* Do Branch */ - if (macroId == m_header.m_macroId) - _setPC(macroStep); - else - vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod); - } - - break; - } - case Op::WaitTicks: - { - bool keyRelease = cmd.m_data[0]; - bool random = cmd.m_data[1]; - bool sampleEnd = cmd.m_data[2]; - bool absolute = cmd.m_data[3]; - bool ms = cmd.m_data[4]; - int16_t time = *reinterpret_cast(&cmd.m_data[5]); - - /* Set wait state */ - if (time >= 0) - { - float q = ms ? 1000.f : m_ticksPerSec; - float secTime = time / q; - if (absolute) - { - if (secTime <= m_execTime) - break; - m_waitCountdown = secTime - m_execTime; - } - else - m_waitCountdown = secTime; - - /* Randomize at the proper resolution */ - if (random) - secTime = std::fmod(vox.getEngine().nextRandom() / q, secTime); - - m_indefiniteWait = false; - } - else - m_indefiniteWait = true; - - m_inWait = true; - m_keyoffWait = keyRelease; - m_sampleEndWait = sampleEnd; - break; - } - case Op::Loop: - { - bool keyRelease = cmd.m_data[0]; - bool random = cmd.m_data[1]; - bool sampleEnd = cmd.m_data[2]; - int16_t step = *reinterpret_cast(&cmd.m_data[3]); - int16_t times = *reinterpret_cast(&cmd.m_data[5]); - - if ((keyRelease && m_keyoff) || (sampleEnd && m_sampleEnd)) - { - /* Break out of loop */ - m_loopCountdown = -1; - break; - } - - if (random) - times = vox.getEngine().nextRandom() % times; - - if (m_loopCountdown == -1 && times != -1) - m_loopCountdown = times; - - if (m_loopCountdown > 0) - { - /* Loop back to step */ - --m_loopCountdown; - _setPC(step); - } - else /* Break out of loop */ - m_loopCountdown = -1; - - break; - } - case Op::Goto: - { - ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); - int16_t macroStep = *reinterpret_cast(&cmd.m_data[3]); - - /* Do Branch */ - if (macroId == m_header.m_macroId) - _setPC(macroStep); - else - vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod); - - break; - } - case Op::WaitMs: - { - bool keyRelease = cmd.m_data[0]; - bool random = cmd.m_data[1]; - bool sampleEnd = cmd.m_data[2]; - bool absolute = cmd.m_data[3]; - int16_t time = *reinterpret_cast(&cmd.m_data[5]); - - /* Set wait state */ - if (time >= 0) - { - float secTime = time / 1000.f; - if (absolute) - { - if (secTime <= m_execTime) - break; - m_waitCountdown = secTime - m_execTime; - } - else - m_waitCountdown = secTime; - - /* Randomize at the proper resolution */ - if (random) - secTime = std::fmod(vox.getEngine().nextRandom() / 1000.f, secTime); - - m_indefiniteWait = false; - } - else - m_indefiniteWait = true; - - m_inWait = true; - m_keyoffWait = keyRelease; - m_sampleEndWait = sampleEnd; - break; - } - case Op::PlayMacro: - { - int8_t addNote = cmd.m_data[0]; - ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); - int16_t macroStep = *reinterpret_cast(&cmd.m_data[3]); - // int8_t priority = cmd.m_data[5]; - // int8_t maxVoices = cmd.m_data[6]; - - std::shared_ptr sibVox = vox.startChildMacro(addNote, macroId, macroStep); - if (sibVox) - m_lastPlayMacroVid = sibVox->vid(); - - break; - } - case Op::SendKeyOff: - { - uint8_t vid = cmd.m_data[0]; - bool lastStarted = cmd.m_data[1]; - - if (lastStarted) - { - if (m_lastPlayMacroVid != -1) - { - std::shared_ptr otherVox = vox.getEngine().findVoice(m_lastPlayMacroVid); - if (otherVox) - otherVox->keyOff(); - } - } - else - { - std::shared_ptr otherVox = vox.getEngine().findVoice(m_variables[vid]); - if (otherVox) - otherVox->keyOff(); - } - - break; - } - case Op::SplitMod: - { - uint8_t mod = cmd.m_data[0]; - ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); - int16_t macroStep = *reinterpret_cast(&cmd.m_data[3]); - - if (m_curMod >= mod) - { - /* Do Branch */ - if (macroId == m_header.m_macroId) - _setPC(macroStep); - else - vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod); - } - - break; - } - case Op::PianoPan: - { - int8_t scale = cmd.m_data[0]; - int8_t cenKey = cmd.m_data[1]; - int8_t cenPan = cmd.m_data[2]; - - int32_t pan = int32_t(m_initKey - cenKey) * scale / 127 + cenPan; - pan = std::max(-127, std::min(127, pan)); - vox.setPan(pan / 127.f); - break; - } - case Op::SetAdsr: - { - ObjectId tableId = *reinterpret_cast(&cmd.m_data[0]); - bool dlsMode = cmd.m_data[2]; - vox.setAdsr(tableId, dlsMode); - break; - } - case Op::ScaleVolume: - { - int8_t scale = cmd.m_data[0]; - int8_t add = cmd.m_data[1]; - ObjectId curve = *reinterpret_cast(&cmd.m_data[2]); - bool orgVel = cmd.m_data[4]; - - int32_t eval = int32_t(orgVel ? m_initVel : m_curVel) * scale / 127 + add; - eval = clamp(0, eval, 127); - - if (curve != 0) - { - const Curve* curveData = vox.getAudioGroup().getPool().tableAsCurves(curve); - if (curveData) - { - vox.m_curVol = (*curveData)[eval] / 127.f; - break; - } - } - - vox.m_curVol = eval / 127.f; - break; - } - case Op::Panning: - { - int8_t panPos = cmd.m_data[0]; - int16_t timeMs = *reinterpret_cast(&cmd.m_data[1]); - int8_t width = cmd.m_data[3]; - - vox.startPanning(timeMs / 1000.0, panPos, width); - break; - } - case Op::Envelope: - { - int8_t scale = cmd.m_data[0]; - int8_t add = cmd.m_data[1]; - ObjectId curve = *reinterpret_cast(&cmd.m_data[2]); - bool ms = cmd.m_data[4]; - int16_t fadeTime = *reinterpret_cast(&cmd.m_data[5]); - - double q = ms ? 1000.0 : m_ticksPerSec; - double secTime = fadeTime / q; - - int32_t eval = int32_t(m_curVel) * scale / 127 + add; - eval = clamp(0, eval, 127); - - const Curve* curveData; - if (curve != 0) - curveData = vox.getAudioGroup().getPool().tableAsCurves(curve); - else - curveData = nullptr; - - vox.startEnvelope(secTime, eval, curveData); - break; - } - case Op::StartSample: - { - int16_t smpId = *reinterpret_cast(&cmd.m_data[0]); - int8_t mode = cmd.m_data[2]; - int32_t offset = *reinterpret_cast(&cmd.m_data[3]); - - switch (mode) - { - case 1: - offset = offset * (127 - m_curVel) / 127; - break; - case 2: - offset = offset * m_curVel / 127; - break; - default: - break; - } - - vox.startSample(smpId, offset); - vox.setPitchKey(m_curPitch); - break; - } - case Op::StopSample: - { - vox.stopSample(); - break; - } - case Op::KeyOff: - { - vox._macroKeyOff(); - break; - } - case Op::SplitRnd: - { - uint8_t rndVal = cmd.m_data[0]; - ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); - int16_t macroStep = *reinterpret_cast(&cmd.m_data[3]); - - if (rndVal <= vox.getEngine().nextRandom() % 256) - { - /* Do branch */ - if (macroId == m_header.m_macroId) - _setPC(macroStep); - else - vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod); - } - - break; - } - case Op::FadeIn: - { - int8_t scale = cmd.m_data[0]; - int8_t add = cmd.m_data[1]; - ObjectId curve = *reinterpret_cast(&cmd.m_data[2]); - bool ms = cmd.m_data[4]; - int16_t fadeTime = *reinterpret_cast(&cmd.m_data[5]); - - float q = ms ? 1000.f : m_ticksPerSec; - float secTime = fadeTime / q; - - int32_t eval = int32_t(m_curVel) * scale / 127 + add; - eval = clamp(0, eval, 127); - - const Curve* curveData; - if (curve != 0) - curveData = vox.getAudioGroup().getPool().tableAsCurves(curve); - else - curveData = nullptr; - - vox.startFadeIn(secTime, eval, curveData); - break; - } - case Op::Spanning: - { - int8_t panPos = cmd.m_data[0]; - int16_t timeMs = *reinterpret_cast(&cmd.m_data[1]); - int8_t width = cmd.m_data[3]; - - vox.startSpanning(timeMs / 1000.0, panPos, width); - break; - } - case Op::SetAdsrCtrl: - { - m_useAdsrControllers = true; - m_midiAttack = cmd.m_data[0]; - m_midiDecay = cmd.m_data[1]; - m_midiSustain = cmd.m_data[2]; - m_midiRelease = cmd.m_data[3]; - - /* Bootstrap ADSR defaults here */ - if (!vox.getCtrlValue(m_midiSustain)) - { - vox.setCtrlValue(m_midiAttack, 10); - vox.setCtrlValue(m_midiSustain, 127); - vox.setCtrlValue(m_midiRelease, 10); - } - - break; - } - case Op::RndNote: - { - int32_t noteLo = int32_t(cmd.m_data[0]); - int8_t detune = cmd.m_data[1]; - int32_t noteHi = int32_t(cmd.m_data[2]); - int8_t free = cmd.m_data[3]; - int8_t rel = cmd.m_data[4]; - - if (rel) - { - noteLo = m_initKey - noteLo; - noteHi = noteLo + noteHi; - } - - noteLo *= 100; - noteHi *= 100; - - if (noteHi == noteLo) - m_curPitch = noteHi; - else - m_curPitch = (vox.getEngine().nextRandom() % (noteHi - noteLo)) + noteLo; - - if (!free) - m_curPitch = m_curPitch / 100 * 100 + detune; - - vox.setPitchKey(m_curPitch); - break; - } - case Op::AddNote: - { - int32_t add = int32_t(cmd.m_data[0]); - int8_t detune = cmd.m_data[1]; - int8_t orgKey = cmd.m_data[2]; - int8_t ms = cmd.m_data[4]; - int16_t timeMs = *reinterpret_cast(&cmd.m_data[5]); - - m_curPitch = (orgKey ? (m_initKey * 100) : m_curPitch) + add * 100 + detune; - - /* Set wait state */ - if (timeMs) - { - float q = ms ? 1000.f : m_ticksPerSec; - float secTime = timeMs / q; - m_waitCountdown = secTime; - m_inWait = true; - } - - vox.setPitchKey(m_curPitch); - break; - } - case Op::SetNote: - { - int32_t key = int32_t(cmd.m_data[0]); - int8_t detune = cmd.m_data[1]; - int8_t ms = cmd.m_data[4]; - int16_t timeMs = *reinterpret_cast(&cmd.m_data[5]); - - m_curPitch = key * 100 + detune; - - /* Set wait state */ - if (timeMs) - { - float q = ms ? 1000.f : m_ticksPerSec; - float secTime = timeMs / q; - m_waitCountdown = secTime; - m_inWait = true; - } - - vox.setPitchKey(m_curPitch); - break; - } - case Op::LastNote: - { - int32_t add = int32_t(cmd.m_data[0]); - int8_t detune = cmd.m_data[1]; - int8_t ms = cmd.m_data[4]; - int16_t timeMs = *reinterpret_cast(&cmd.m_data[5]); - - m_curPitch = (add + vox.getLastNote()) * 100 + detune; - - /* Set wait state */ - if (timeMs) - { - float q = ms ? 1000.f : m_ticksPerSec; - float secTime = timeMs / q; - m_waitCountdown = secTime; - m_inWait = true; - } - - vox.setPitchKey(m_curPitch); - break; - } - case Op::Portamento: - { - m_portamentoMode = cmd.m_data[0]; - m_portamentoType = cmd.m_data[1]; - int8_t ms = cmd.m_data[4]; - int16_t timeMs = *reinterpret_cast(&cmd.m_data[5]); - - float q = ms ? 1000.f : m_ticksPerSec; - m_portamentoTime = timeMs / q; - break; - } - case Op::Vibrato: - { - int32_t level = cmd.m_data[0] * 100 + cmd.m_data[1]; - bool modScale = cmd.m_data[2] != 0; - int8_t ms = cmd.m_data[4]; - int16_t timeMs = *reinterpret_cast(&cmd.m_data[5]); - - float q = ms ? 1000.f : m_ticksPerSec; - vox.setVibrato(level, modScale, timeMs / q); - break; - } - case Op::PitchSweep1: - { - int32_t times = int32_t(cmd.m_data[0]); - int16_t add = *reinterpret_cast(&cmd.m_data[1]); - - int8_t ms = cmd.m_data[4]; - int16_t timeMs = *reinterpret_cast(&cmd.m_data[5]); - - /* Set wait state */ - if (timeMs) - { - float q = ms ? 1000.f : m_ticksPerSec; - float secTime = timeMs / q; - m_waitCountdown = secTime; - m_inWait = true; - } - - vox.setPitchSweep1(times, add); - break; - } - case Op::PitchSweep2: - { - int32_t times = int32_t(cmd.m_data[0]); - int16_t add = *reinterpret_cast(&cmd.m_data[1]); - - int8_t ms = cmd.m_data[4]; - int16_t timeMs = *reinterpret_cast(&cmd.m_data[5]); - - /* Set wait state */ - if (timeMs) - { - float q = ms ? 1000.f : m_ticksPerSec; - float secTime = timeMs / q; - m_waitCountdown = secTime; - m_inWait = true; - } - - vox.setPitchSweep2(times, add); - break; - } - case Op::SetPitch: - { - uint32_t hz = *reinterpret_cast(&cmd.m_data[0]) >> 8; - uint16_t fine = *reinterpret_cast(&cmd.m_data[3]); - vox.setPitchFrequency(hz, fine); - break; - } - case Op::SetPitchAdsr: - { - ObjectId adsr = *reinterpret_cast(&cmd.m_data[0]); - int8_t keys = cmd.m_data[3]; - int8_t cents = cmd.m_data[4]; - vox.setPitchAdsr(adsr, keys * 100 + cents); - break; - } - case Op::ScaleVolumeDLS: - { - int16_t scale = *reinterpret_cast(&cmd.m_data[0]); - bool orgVel = cmd.m_data[2]; - vox.m_curVol = int32_t(orgVel ? m_initVel : m_curVel) * scale / 4096.f / 127.f; - break; - } - case Op::Mod2Vibrange: - { - int8_t keys = cmd.m_data[0]; - int8_t cents = cmd.m_data[1]; - vox.setMod2VibratoRange(keys * 100 + cents); - break; - } - case Op::SetupTremolo: - { - int16_t scale = *reinterpret_cast(&cmd.m_data[0]); - int16_t modScale = *reinterpret_cast(&cmd.m_data[3]); - vox.setTremolo(scale / 4096.f, modScale / 4096.f); - break; - } - case Op::Return: - { - if (m_pc.size() > 1) - { - m_pc.pop_back(); - m_header = *reinterpret_cast(m_pc.back().first); - if (vox.getAudioGroup().getDataFormat() != DataFormat::PC) - m_header.swapBig(); - vox.m_objectId = m_header.m_macroId; - } - break; - } - case Op::GoSub: - { - ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); - int16_t macroStep = *reinterpret_cast(&cmd.m_data[3]); - - if (macroId == m_header.m_macroId) - m_pc.push_back({m_pc.back().first, _assertPC(macroStep, m_header.m_size)}); - else - vox.loadSoundObject(macroId, macroStep, m_ticksPerSec, m_initKey, m_initVel, m_initMod, true); - - m_header = *reinterpret_cast(m_pc.back().first); - if (vox.getAudioGroup().getDataFormat() != DataFormat::PC) - m_header.swapBig(); - vox.m_objectId = m_header.m_macroId; - - break; - } - case Op::TrapEvent: - { - uint8_t event = cmd.m_data[0]; - ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); - int16_t macroStep = *reinterpret_cast(&cmd.m_data[3]); - - switch (event) - { - case 0: - vox.m_keyoffTrap.macroId = macroId; - vox.m_keyoffTrap.macroStep = macroStep; - break; - case 1: - vox.m_sampleEndTrap.macroId = macroId; - vox.m_sampleEndTrap.macroStep = macroStep; - break; - case 2: - vox.m_messageTrap.macroId = macroId; - vox.m_messageTrap.macroStep = macroStep; - break; - default: - break; - } - - break; - } - case Op::UntrapEvent: - { - uint8_t event = cmd.m_data[0]; - - switch (event) - { - case 0: - vox.m_keyoffTrap.macroId = 0xffff; - vox.m_keyoffTrap.macroStep = -1; - break; - case 1: - vox.m_sampleEndTrap.macroId = 0xffff; - vox.m_sampleEndTrap.macroStep = -1; - break; - case 2: - vox.m_messageTrap.macroId = 0xffff; - vox.m_messageTrap.macroStep = -1; - break; - default: - break; - } - - break; - } - case Op::SendMessage: - { - bool isVar = cmd.m_data[0]; - ObjectId macroId = *reinterpret_cast(&cmd.m_data[1]); - uint8_t vid = cmd.m_data[3]; - uint8_t val = cmd.m_data[4]; - - if (isVar) - { - std::shared_ptr findVox = vox.getEngine().findVoice(m_variables[vid]); - if (findVox) - findVox->message(val); - } - else - vox.getEngine().sendMacroMessage(macroId, val); - - break; - } - case Op::GetMessage: - { - uint8_t vid = cmd.m_data[0]; - if (vox.m_messageQueue.size()) - { - m_variables[vid] = vox.m_messageQueue.front(); - vox.m_messageQueue.pop_front(); - } - else - m_variables[vid] = 0; - break; - } - case Op::GetVid: - { - uint8_t vid = cmd.m_data[0]; - bool lastPlayMacro = cmd.m_data[1]; - m_variables[vid] = lastPlayMacro ? m_lastPlayMacroVid : vox.vid(); - break; - } - case Op::SendFlag: - { - //int8_t id = cmd.m_data[0]; - //int8_t val = cmd.m_data[1]; - break; /* TODO: figure out a good API */ - } - case Op::PitchWheelR: - { - int8_t up = cmd.m_data[0]; - int8_t down = cmd.m_data[1]; - vox.setPitchWheelRange(up, down); - break; - } - case Op::VolSelect: - { - uint8_t ctrl = cmd.m_data[0]; - int16_t perc = *reinterpret_cast(&cmd.m_data[1]); - Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); - Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); - uint8_t fine = cmd.m_data[5]; - m_volumeSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); - break; - } - case Op::PanSelect: - { - uint8_t ctrl = cmd.m_data[0]; - int16_t perc = *reinterpret_cast(&cmd.m_data[1]); - Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); - Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); - uint8_t fine = cmd.m_data[5]; - m_panSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); - break; - } - case Op::PitchWheelSelect: - { - uint8_t ctrl = cmd.m_data[0]; - int16_t perc = *reinterpret_cast(&cmd.m_data[1]); - Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); - Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); - uint8_t fine = cmd.m_data[5]; - m_pitchWheelSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); - break; - } - case Op::ModWheelSelect: - { - uint8_t ctrl = cmd.m_data[0]; - int16_t perc = *reinterpret_cast(&cmd.m_data[1]); - Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); - Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); - uint8_t fine = cmd.m_data[5]; - m_modWheelSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); - break; - } - case Op::PedalSelect: - { - uint8_t ctrl = cmd.m_data[0]; - int16_t perc = *reinterpret_cast(&cmd.m_data[1]); - Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); - Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); - uint8_t fine = cmd.m_data[5]; - m_pedalSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); - break; - } - case Op::PortamentoSelect: - { - uint8_t ctrl = cmd.m_data[0]; - int16_t perc = *reinterpret_cast(&cmd.m_data[1]); - Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); - Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); - uint8_t fine = cmd.m_data[5]; - m_portamentoSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); - break; - } - case Op::ReverbSelect: - { - uint8_t ctrl = cmd.m_data[0]; - int16_t perc = *reinterpret_cast(&cmd.m_data[1]); - Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); - Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); - uint8_t fine = cmd.m_data[5]; - m_reverbSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); - break; - } - case Op::SpanSelect: - { - uint8_t ctrl = cmd.m_data[0]; - int16_t perc = *reinterpret_cast(&cmd.m_data[1]); - Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); - Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); - uint8_t fine = cmd.m_data[5]; - m_spanSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); - break; - } - case Op::DopplerSelect: - { - uint8_t ctrl = cmd.m_data[0]; - int16_t perc = *reinterpret_cast(&cmd.m_data[1]); - Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); - Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); - uint8_t fine = cmd.m_data[5]; - m_dopplerSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); - break; - } - case Op::TremoloSelect: - { - uint8_t ctrl = cmd.m_data[0]; - int16_t perc = *reinterpret_cast(&cmd.m_data[1]); - Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); - Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); - uint8_t fine = cmd.m_data[5]; - m_tremoloSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); - break; - } - case Op::PreASelect: - { - uint8_t ctrl = cmd.m_data[0]; - int16_t perc = *reinterpret_cast(&cmd.m_data[1]); - Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); - Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); - uint8_t fine = cmd.m_data[5]; - m_preAuxASel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); - break; - } - case Op::PreBSelect: - { - uint8_t ctrl = cmd.m_data[0]; - int16_t perc = *reinterpret_cast(&cmd.m_data[1]); - Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); - Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); - uint8_t fine = cmd.m_data[5]; - m_preAuxBSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); - break; - } - case Op::PostBSelect: - { - uint8_t ctrl = cmd.m_data[0]; - int16_t perc = *reinterpret_cast(&cmd.m_data[1]); - Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); - Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); - uint8_t fine = cmd.m_data[5]; - m_postAuxB.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); - break; - } - case Op::AuxAFXSelect: - { - uint8_t ctrl = cmd.m_data[0]; - int16_t perc = *reinterpret_cast(&cmd.m_data[1]); - Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); - Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); - uint8_t fine = cmd.m_data[5]; - m_auxAFxSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); - break; - } - case Op::AuxBFXSelect: - { - uint8_t ctrl = cmd.m_data[0]; - int16_t perc = *reinterpret_cast(&cmd.m_data[1]); - Evaluator::Combine combine = Evaluator::Combine(cmd.m_data[3]); - Evaluator::VarType vtype = Evaluator::VarType(cmd.m_data[4]); - uint8_t fine = cmd.m_data[5]; - m_auxBFxSel.addComponent(ctrl, (perc + fine / 100.f) / 100.f, combine, vtype); - break; - } - case Op::SetupLFO: - { - uint8_t number = cmd.m_data[0]; - int16_t period = *reinterpret_cast(&cmd.m_data[1]); - if (number == 0) - vox.setLFO1Period(period / 1000.f); - else if (number == 1) - vox.setLFO2Period(period / 1000.f); - break; - } - case Op::SetKeygroup: - { - uint8_t id = cmd.m_data[0]; - uint8_t flag = cmd.m_data[1]; - - vox.setKeygroup(0); - if (id) - { - vox.getEngine().killKeygroup(id, flag); - vox.setKeygroup(id); - } - - break; - } - case Op::AddVars: - { - bool aCtrl = cmd.m_data[0]; - int8_t a = cmd.m_data[1]; - bool bCtrl = cmd.m_data[2]; - int8_t b = cmd.m_data[3]; - bool cCtrl = cmd.m_data[4]; - int8_t c = cmd.m_data[5]; - - if (bCtrl) - b = vox.getCtrlValue(b); - else - b = m_variables[b]; - - if (cCtrl) - c = vox.getCtrlValue(c); - else - c = m_variables[c]; - - if (aCtrl) - vox.setCtrlValue(a, b + c); - else - m_variables[a] = b + c; - - break; - } - case Op::SubVars: - { - bool aCtrl = cmd.m_data[0]; - int8_t a = cmd.m_data[1]; - bool bCtrl = cmd.m_data[2]; - int8_t b = cmd.m_data[3]; - bool cCtrl = cmd.m_data[4]; - int8_t c = cmd.m_data[5]; - - if (bCtrl) - b = vox.getCtrlValue(b); - else - b = m_variables[b]; - - if (cCtrl) - c = vox.getCtrlValue(c); - else - c = m_variables[c]; - - if (aCtrl) - vox.setCtrlValue(a, b - c); - else - m_variables[a] = b - c; - - break; - } - case Op::MulVars: - { - bool aCtrl = cmd.m_data[0]; - int8_t a = cmd.m_data[1]; - bool bCtrl = cmd.m_data[2]; - int8_t b = cmd.m_data[3]; - bool cCtrl = cmd.m_data[4]; - int8_t c = cmd.m_data[5]; - - if (bCtrl) - b = vox.getCtrlValue(b); - else - b = m_variables[b]; - - if (cCtrl) - c = vox.getCtrlValue(c); - else - c = m_variables[c]; - - if (aCtrl) - vox.setCtrlValue(a, b * c); - else - m_variables[a] = b * c; - - break; - } - case Op::DivVars: - { - bool aCtrl = cmd.m_data[0]; - int8_t a = cmd.m_data[1]; - bool bCtrl = cmd.m_data[2]; - int8_t b = cmd.m_data[3]; - bool cCtrl = cmd.m_data[4]; - int8_t c = cmd.m_data[5]; - - if (bCtrl) - b = vox.getCtrlValue(b); - else - b = m_variables[b]; - - if (cCtrl) - c = vox.getCtrlValue(c); - else - c = m_variables[c]; - - if (aCtrl) - vox.setCtrlValue(a, b / c); - else - m_variables[a] = b / c; - - break; - } - case Op::AddIVars: - { - bool aCtrl = cmd.m_data[0]; - int8_t a = cmd.m_data[1]; - bool bCtrl = cmd.m_data[2]; - int8_t b = cmd.m_data[3]; - int16_t imm = *reinterpret_cast(&cmd.m_data[4]); - - if (bCtrl) - b = vox.getCtrlValue(b); - else - b = m_variables[b]; - - if (aCtrl) - vox.setCtrlValue(a, b + imm); - else - m_variables[a] = b + imm; - - break; - } - case Op::IfEqual: - { - bool aCtrl = cmd.m_data[0]; - int8_t a = cmd.m_data[1]; - bool bCtrl = cmd.m_data[2]; - int8_t b = cmd.m_data[3]; - bool lnot = cmd.m_data[4]; - int16_t macroStep = *reinterpret_cast(&cmd.m_data[5]); - - if (aCtrl) - a = vox.getCtrlValue(a); - else - a = m_variables[a]; - - if (bCtrl) - b = vox.getCtrlValue(b); - else - b = m_variables[b]; - - if ((a == b) ^ lnot) - _setPC(macroStep); - - break; - } - case Op::IfLess: - { - bool aCtrl = cmd.m_data[0]; - int8_t a = cmd.m_data[1]; - bool bCtrl = cmd.m_data[2]; - int8_t b = cmd.m_data[3]; - bool lnot = cmd.m_data[4]; - int16_t macroStep = *reinterpret_cast(&cmd.m_data[5]); - - if (aCtrl) - a = vox.getCtrlValue(a); - else - a = m_variables[a]; - - if (bCtrl) - b = vox.getCtrlValue(b); - else - b = m_variables[b]; - - if ((a < b) ^ lnot) - _setPC(macroStep); - - break; - } - default: - break; - } } m_execTime += dt; diff --git a/lib/Voice.cpp b/lib/Voice.cpp index bd49427..900ffc9 100644 --- a/lib/Voice.cpp +++ b/lib/Voice.cpp @@ -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::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(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& layer, int macroStep, double ticksPerSec, +bool Voice::_loadLayer(ObjectId id, const std::vector& 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 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* layer = m_audioGroup.getPool().layer(objectId); + const std::vector* 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);