diff --git a/Editor/MIDIReader.cpp b/Editor/MIDIReader.cpp index 0f79f35..d481f33 100644 --- a/Editor/MIDIReader.cpp +++ b/Editor/MIDIReader.cpp @@ -10,7 +10,7 @@ void MIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity) if (keySearch == m_chanVoxs.cend()) return; - if (keySearch->second == m_lastVoice.lock()) + if (m_lastVoice->isDestroyed() || keySearch->second == m_lastVoice) m_lastVoice.reset(); keySearch->second->keyOff(); m_keyoffVoxs.emplace(std::move(keySearch->second)); @@ -19,8 +19,11 @@ void MIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity) void MIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity) { + if (m_lastVoice->isDestroyed()) + m_lastVoice.reset(); + /* If portamento is enabled for voice, pre-empt spawning new voices */ - if (std::shared_ptr lastVoice = m_lastVoice.lock()) + if (amuse::ObjToken lastVoice = m_lastVoice) { uint8_t lastNote = lastVoice->getLastNote(); if (lastVoice->doPortamento(key)) @@ -35,7 +38,7 @@ void MIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity) auto keySearch = m_chanVoxs.find(key); if (keySearch != m_chanVoxs.cend()) { - if (keySearch->second == m_lastVoice.lock()) + if (keySearch->second == m_lastVoice) m_lastVoice.reset(); keySearch->second->keyOff(); keySearch->second->setPedal(false); @@ -48,10 +51,11 @@ void MIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity) { ProjectModel::SoundMacroNode* cNode = static_cast(node); amuse::AudioGroupDatabase* group = g_MainWindow->projectModel()->getGroupNode(node)->getAudioGroup(); - std::shared_ptr& vox = m_chanVoxs[key]; - vox = m_engine.macroStart(group, cNode->id(), key, velocity, g_MainWindow->m_modulation); - vox->setPedal(g_MainWindow->m_sustain); + amuse::ObjToken& vox = m_chanVoxs[key]; + vox = m_engine.macroStart(group, cNode->id(), key, velocity, g_MainWindow->m_ctrlVals[1]); + vox->setPedal(g_MainWindow->m_ctrlVals[64] >= 0x40); vox->setPitchWheel(g_MainWindow->m_pitch); + vox->installCtrlValues(g_MainWindow->m_ctrlVals); } } diff --git a/Editor/MIDIReader.hpp b/Editor/MIDIReader.hpp index 55f0fc2..001d73b 100644 --- a/Editor/MIDIReader.hpp +++ b/Editor/MIDIReader.hpp @@ -2,13 +2,14 @@ #define AMUSE_MIDI_READER_HPP #include "amuse/BooBackend.hpp" +#include "amuse/Common.hpp" #include class MIDIReader : public amuse::BooBackendMIDIReader { - std::unordered_map> m_chanVoxs; - std::unordered_set> m_keyoffVoxs; - std::weak_ptr m_lastVoice; + std::unordered_map> m_chanVoxs; + std::unordered_set> m_keyoffVoxs; + amuse::ObjToken m_lastVoice; public: MIDIReader(amuse::Engine& engine, const char* name, bool useLock); boo::IMIDIIn* getMidiIn() const { return m_midiIn.get(); } diff --git a/Editor/MainWindow.cpp b/Editor/MainWindow.cpp index b2b308b..ed6566c 100644 --- a/Editor/MainWindow.cpp +++ b/Editor/MainWindow.cpp @@ -16,6 +16,7 @@ #include "CurveEditor.hpp" #include "KeymapEditor.hpp" #include "LayersEditor.hpp" +#include "SampleEditor.hpp" MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent), @@ -50,7 +51,8 @@ MainWindow::MainWindow(QWidget* parent) m_ui.actionSave_Project->setShortcut(QKeySequence::Save); connect(m_ui.actionSave_Project, SIGNAL(triggered()), this, SLOT(saveAction())); connect(m_ui.actionRevert_Project, SIGNAL(triggered()), this, SLOT(revertAction())); - connect(m_ui.actionImport, SIGNAL(triggered()), this, SLOT(importAction())); + connect(m_ui.actionReload_Sample_Data, SIGNAL(triggered()), this, SLOT(reloadSampleDataAction())); + connect(m_ui.actionImport_Groups, SIGNAL(triggered()), this, SLOT(importAction())); connect(m_ui.actionExport_GameCube_Groups, SIGNAL(triggered()), this, SLOT(exportAction())); for (int i = 0; i < MaxRecentFiles; ++i) @@ -109,6 +111,8 @@ MainWindow::MainWindow(QWidget* parent) m_ui.editorContents->addWidget(m_keymapEditor); m_layersEditor = new LayersEditor; m_ui.editorContents->addWidget(m_layersEditor); + m_sampleEditor = new SampleEditor; + m_ui.editorContents->addWidget(m_sampleEditor); m_ui.editorContents->setCurrentWidget(m_faceSvg); connect(m_ui.actionNew_Subproject, SIGNAL(triggered()), this, SLOT(newSubprojectAction())); @@ -129,6 +133,9 @@ MainWindow::MainWindow(QWidget* parent) m_voxAllocator = std::make_unique(*m_voxEngine); m_engine = std::make_unique(*m_voxAllocator); + m_ctrlVals[7] = 127; + m_ctrlVals[10] = 64; + startTimer(16); } @@ -167,6 +174,27 @@ void MainWindow::connectMessenger(UIMessenger* messenger, Qt::ConnectionType typ QMessageBox::StandardButton)), type); } +void MainWindow::updateWindowTitle() +{ + if (!m_projectModel) + { + setWindowTitle(tr("Amuse")); + return; + } + + QDir dir(m_projectModel->path()); + if (m_ui.editorContents->currentWidget() != m_faceSvg) + { + ProjectModel::BasePoolObjectNode* objNode = static_cast( + static_cast(m_ui.editorContents->currentWidget())->currentNode()); + setWindowTitle(tr("Amuse [%1/%2/%3]").arg(dir.dirName()).arg( + m_projectModel->getGroupNode(objNode)->text()).arg(objNode->text())); + return; + } + + setWindowTitle(tr("Amuse [%1]").arg(dir.dirName())); +} + void MainWindow::updateRecentFileActions() { QSettings settings; @@ -215,6 +243,7 @@ bool MainWindow::setProjectPath(const QString& path) } testWriteFile.remove(); + closeEditor(); if (m_projectModel) m_projectModel->deleteLater(); m_projectModel = new ProjectModel(path, this); @@ -224,9 +253,10 @@ bool MainWindow::setProjectPath(const QString& path) this, SLOT(onOutlineSelectionChanged(const QItemSelection&, const QItemSelection&))); m_ui.actionSave_Project->setEnabled(true); m_ui.actionRevert_Project->setEnabled(true); + m_ui.actionReload_Sample_Data->setEnabled(true); m_ui.actionExport_GameCube_Groups->setEnabled(true); setWindowFilePath(path); - setWindowTitle(QString("Amuse [%1]").arg(dir.dirName())); + updateWindowTitle(); onFocusChanged(nullptr, focusWidget()); m_undoStack->clear(); @@ -305,19 +335,19 @@ void MainWindow::timerEvent(QTimerEvent* ev) void MainWindow::setSustain(bool sustain) { - if (sustain && !m_sustain) + if (sustain && m_ctrlVals[64] < 0x40) { m_ui.statusbar->setNormalMessage(tr("SUSTAIN")); for (auto& v : m_engine->getActiveVoices()) v->setPedal(true); - m_sustain = true; + m_ctrlVals[64] = 127; } - else if (!sustain && m_sustain) + else if (!sustain && m_ctrlVals[64] >= 0x40) { m_ui.statusbar->setNormalMessage({}); for (auto& v : m_engine->getActiveVoices()) v->setPedal(false); - m_sustain = false; + m_ctrlVals[64] = 0; } } @@ -384,6 +414,7 @@ bool MainWindow::_setEditor(EditorWidget* editor) } m_ui.editorContents->setCurrentWidget(editor); m_ui.editorContents->update(); + updateWindowTitle(); return true; } @@ -422,6 +453,11 @@ bool MainWindow::openEditor(ProjectModel::LayersNode* node) return _setEditor(m_layersEditor->loadData(node) ? m_layersEditor : nullptr); } +bool MainWindow::openEditor(ProjectModel::SampleNode* node) +{ + return _setEditor(m_sampleEditor->loadData(node) ? m_sampleEditor : nullptr); +} + bool MainWindow::openEditor(ProjectModel::INode* node) { switch (node->type()) @@ -440,6 +476,8 @@ bool MainWindow::openEditor(ProjectModel::INode* node) return openEditor(static_cast(node)); case ProjectModel::INode::Type::Layer: return openEditor(static_cast(node)); + case ProjectModel::INode::Type::Sample: + return openEditor(static_cast(node)); default: return false; } @@ -565,11 +603,43 @@ void MainWindow::saveAction() void MainWindow::revertAction() { QString path = m_projectModel->path(); + closeEditor(); + m_undoStack->clear(); delete m_projectModel; m_projectModel = nullptr; openProject(path); } +void MainWindow::reloadSampleDataAction() +{ + ProjectModel* model = m_projectModel; + if (!m_projectModel) + return; + + QDir dir(m_projectModel->path()); + if (!dir.exists()) + return; + + startBackgroundTask(tr("Reloading Samples"), tr("Scanning Project"), + [dir, model](BackgroundTask& task) + { + QStringList childDirs = dir.entryList(QDir::Dirs); + for (const auto& chDir : childDirs) + { + if (task.isCanceled()) + return; + QString chPath = dir.filePath(chDir); + if (QFileInfo(chPath, QStringLiteral("!project.yaml")).exists() && + QFileInfo(chPath, QStringLiteral("!pool.yaml")).exists()) + { + task.setLabelText(tr("Scanning %1").arg(chDir)); + if (!model->reloadSampleData(chDir, task.uiMessenger())) + return; + } + } + }); +} + void MainWindow::importAction() { QString path = QFileDialog::getOpenFileName(this, tr("Import Project")); @@ -795,9 +865,10 @@ void MainWindow::notePressed(int key) amuse::AudioGroupDatabase* group = m_projectModel->getGroupNode(node)->getAudioGroup(); if (m_lastSound) m_lastSound->keyOff(); - m_lastSound = m_engine->macroStart(group, cNode->id(), key, m_velocity, m_modulation); - m_lastSound->setPedal(m_sustain); + m_lastSound = m_engine->macroStart(group, cNode->id(), key, m_velocity, m_ctrlVals[1]); + m_lastSound->setPedal(m_ctrlVals[64] >= 0x40); m_lastSound->setPitchWheel(m_pitch); + m_lastSound->installCtrlValues(m_ctrlVals); } } } @@ -818,9 +889,9 @@ void MainWindow::velocityChanged(int vel) void MainWindow::modulationChanged(int mod) { - m_modulation = mod; + m_ctrlVals[1] = int8_t(mod); for (auto& v : m_engine->getActiveVoices()) - v->setCtrlValue(1, int8_t(m_modulation)); + v->setCtrlValue(1, m_ctrlVals[1]); } void MainWindow::pitchChanged(int pitch) diff --git a/Editor/MainWindow.hpp b/Editor/MainWindow.hpp index 365fcc7..ce6057a 100644 --- a/Editor/MainWindow.hpp +++ b/Editor/MainWindow.hpp @@ -28,6 +28,7 @@ class ADSREditor; class CurveEditor; class KeymapEditor; class LayersEditor; +class SampleEditor; class BackgroundTask : public QObject { @@ -85,15 +86,15 @@ class MainWindow : public QMainWindow CurveEditor* m_curveEditor = nullptr; KeymapEditor* m_keymapEditor = nullptr; LayersEditor* m_layersEditor = nullptr; + SampleEditor* m_sampleEditor = nullptr; std::unique_ptr m_voxEngine; std::unique_ptr m_voxAllocator; std::unique_ptr m_engine; - std::shared_ptr m_lastSound; + amuse::ObjToken m_lastSound; int m_velocity = 90; - int m_modulation = 0; float m_pitch = 0.f; - bool m_sustain = false; + int8_t m_ctrlVals[128] = {}; bool m_uiDisabled = false; QUndoStack* m_undoStack; @@ -110,6 +111,7 @@ class MainWindow : public QMainWindow void connectMessenger(UIMessenger* messenger, Qt::ConnectionType type); + void updateWindowTitle(); void updateRecentFileActions(); bool setProjectPath(const QString& path); void refreshAudioIO(); @@ -137,6 +139,7 @@ public: bool openEditor(ProjectModel::CurveNode* node); bool openEditor(ProjectModel::KeymapNode* node); bool openEditor(ProjectModel::LayersNode* node); + bool openEditor(ProjectModel::SampleNode* node); bool openEditor(ProjectModel::INode* node); void closeEditor(); @@ -153,6 +156,7 @@ public slots: void clearRecentFilesAction(); void saveAction(); void revertAction(); + void reloadSampleDataAction(); void importAction(); void exportAction(); diff --git a/Editor/MainWindow.ui b/Editor/MainWindow.ui index f7af33e..c9f2bde 100644 --- a/Editor/MainWindow.ui +++ b/Editor/MainWindow.ui @@ -306,8 +306,9 @@ + - + @@ -398,9 +399,9 @@ &Delete - + - &Import + &Import Groups Ctrl+I @@ -556,6 +557,14 @@ &Revert Project + + + false + + + Reload Sample &Data + + diff --git a/Editor/ProjectModel.cpp b/Editor/ProjectModel.cpp index e5e4f6a..b00abbc 100644 --- a/Editor/ProjectModel.cpp +++ b/Editor/ProjectModel.cpp @@ -122,6 +122,21 @@ bool ProjectModel::openGroupData(const QString& groupName, UIMessenger& messenge return true; } +bool ProjectModel::reloadSampleData(const QString& groupName, UIMessenger& messenger) +{ + m_projectDatabase.setIdDatabases(); + QString path = QFileInfo(m_dir, groupName).filePath(); + auto search = m_groups.find(groupName); + if (search != m_groups.end()) + { + search->second.setIdDatabases(); + search->second.getSdir().reloadSampleData(QStringToSysString(path)); + } + + m_needsReset = true; + return true; +} + bool ProjectModel::importGroupData(const QString& groupName, const amuse::AudioGroupData& data, ImportMode mode, UIMessenger& messenger) { @@ -200,6 +215,7 @@ void ProjectModel::_resetModelData() auto& tables = group.getPool().tables(); auto& keymaps = group.getPool().keymaps(); auto& layers = group.getPool().layers(); + auto& samples = group.getSdir().sampleEntries(); gn.reserve(songGroups.size() + sfxGroups.size() + 4); for (const auto& grp : SortUnorderedMap(songGroups)) gn.makeChild(grp.first, grp.second.get()); @@ -218,7 +234,7 @@ void ProjectModel::_resetModelData() size_t curveCount = 0; for (auto& t : tablesSort) { - amuse::ITable::Type tp = t.second.get()->Isa(); + amuse::ITable::Type tp = (*t.second.get())->Isa(); if (tp == amuse::ITable::Type::ADSR || tp == amuse::ITable::Type::ADSRDLS) ADSRCount += 1; else if (tp == amuse::ITable::Type::Curve) @@ -230,7 +246,7 @@ void ProjectModel::_resetModelData() col.reserve(ADSRCount); for (auto& t : tablesSort) { - amuse::ITable::Type tp = t.second.get()->Isa(); + amuse::ITable::Type tp = (*t.second.get())->Isa(); if (tp == amuse::ITable::Type::ADSR || tp == amuse::ITable::Type::ADSRDLS) col.makeChild(t.first, t.second.get()); } @@ -241,9 +257,9 @@ void ProjectModel::_resetModelData() col.reserve(curveCount); for (auto& t : tablesSort) { - amuse::ITable::Type tp = t.second.get()->Isa(); + amuse::ITable::Type tp = (*t.second.get())->Isa(); if (tp == amuse::ITable::Type::Curve) - col.makeChild(t.first, std::static_pointer_cast(t.second.get())); + col.makeChild(t.first, t.second.get()); } } } @@ -261,6 +277,13 @@ void ProjectModel::_resetModelData() for (auto& keymap : SortUnorderedMap(layers)) col.makeChild(keymap.first, keymap.second.get()); } + { + CollectionNode& col = + gn.makeChild(tr("Samples"), QIcon(":/icons/IconSample.svg"), INode::Type::Sample); + col.reserve(samples.size()); + for (auto& sample : SortUnorderedMap(samples)) + col.makeChild(sample.first, sample.second.get()); + } } endResetModel(); } diff --git a/Editor/ProjectModel.hpp b/Editor/ProjectModel.hpp index 0eb3602..4243df0 100644 --- a/Editor/ProjectModel.hpp +++ b/Editor/ProjectModel.hpp @@ -61,7 +61,8 @@ public: ADSR, Curve, Keymap, - Layer + Layer, + Sample }; protected: INode* m_parent; @@ -168,8 +169,8 @@ public: { amuse::GroupId m_id; QString m_name; - std::shared_ptr m_index; - SongGroupNode(INode* parent, int row, amuse::GroupId id, std::shared_ptr index) + amuse::ObjToken m_index; + SongGroupNode(INode* parent, int row, amuse::GroupId id, amuse::ObjToken index) : INode(parent, row), m_id(id), m_name(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_index(index) {} static QIcon Icon; @@ -184,8 +185,8 @@ public: { amuse::GroupId m_id; QString m_name; - std::shared_ptr m_index; - SoundGroupNode(INode* parent, int row, amuse::GroupId id, std::shared_ptr index) + amuse::ObjToken m_index; + SoundGroupNode(INode* parent, int row, amuse::GroupId id, amuse::ObjToken index) : INode(parent, row), m_id(id), m_name(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_index(index) {} static QIcon Icon; @@ -221,30 +222,31 @@ public: struct BasePoolObjectNode : INode { amuse::ObjectId m_id; - BasePoolObjectNode(INode* parent, int row, amuse::ObjectId id) - : INode(parent, row), m_id(id) {} + QString m_name; + BasePoolObjectNode(INode* parent, int row, amuse::ObjectId id, const QString& name) + : INode(parent, row), m_id(id), m_name(name) {} amuse::ObjectId id() const { return m_id; } + QString text() const { return m_name; } + QIcon icon() const { return {}; } }; template struct PoolObjectNode : BasePoolObjectNode { - QString m_name; - std::shared_ptr m_obj; - PoolObjectNode(INode* parent, int row, ID id, std::shared_ptr obj) - : BasePoolObjectNode(parent, row, id), m_name(ID::CurNameDB->resolveNameFromId(id).data()), m_obj(obj) {} + amuse::ObjToken m_obj; + PoolObjectNode(INode* parent, int row, ID id, amuse::ObjToken obj) + : BasePoolObjectNode(parent, row, id, ID::CurNameDB->resolveNameFromId(id).data()), m_obj(obj) {} Type type() const { return TP; } - QString text() const { return m_name; } - QIcon icon() const { return {}; } std::shared_ptr> shared_from_this() { return std::static_pointer_cast>(INode::shared_from_this()); } }; using SoundMacroNode = PoolObjectNode; - using ADSRNode = PoolObjectNode; - using CurveNode = PoolObjectNode; + using ADSRNode = PoolObjectNode, INode::Type::ADSR>; + using CurveNode = PoolObjectNode, INode::Type::Curve>; using KeymapNode = PoolObjectNode; using LayersNode = PoolObjectNode, INode::Type::Layer>; + using SampleNode = PoolObjectNode; std::shared_ptr m_root; @@ -256,6 +258,7 @@ public: bool clearProjectData(); bool openGroupData(const QString& groupName, UIMessenger& messenger); + bool reloadSampleData(const QString& groupName, UIMessenger& messenger); bool importGroupData(const QString& groupName, const amuse::AudioGroupData& data, ImportMode mode, UIMessenger& messenger); bool saveToFile(UIMessenger& messenger); diff --git a/Editor/SampleEditor.cpp b/Editor/SampleEditor.cpp index 8a86a72..11fd44d 100644 --- a/Editor/SampleEditor.cpp +++ b/Editor/SampleEditor.cpp @@ -1,5 +1,10 @@ #include "SampleEditor.hpp" +bool SampleEditor::loadData(ProjectModel::SampleNode* node) +{ + return false; +} + SampleEditor::SampleEditor(QWidget* parent) : EditorWidget(parent) { diff --git a/Editor/SampleEditor.hpp b/Editor/SampleEditor.hpp index 4730c08..55b4b40 100644 --- a/Editor/SampleEditor.hpp +++ b/Editor/SampleEditor.hpp @@ -8,6 +8,7 @@ class SampleEditor : public EditorWidget Q_OBJECT public: explicit SampleEditor(QWidget* parent = Q_NULLPTR); + bool loadData(ProjectModel::SampleNode* node); }; diff --git a/Editor/SoundMacroEditor.cpp b/Editor/SoundMacroEditor.cpp index 10cd337..55ea1e4 100644 --- a/Editor/SoundMacroEditor.cpp +++ b/Editor/SoundMacroEditor.cpp @@ -237,6 +237,7 @@ CommandWidget::CommandWidget(amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::Cm } case amuse::SoundMacro::CmdIntrospection::Field::Type::SoundMacroId: case amuse::SoundMacro::CmdIntrospection::Field::Type::TableId: + case amuse::SoundMacro::CmdIntrospection::Field::Type::SampleId: { ProjectModel::INode::Type collectionType; if (field.m_tp == amuse::SoundMacro::CmdIntrospection::Field::Type::SoundMacroId) @@ -250,6 +251,10 @@ CommandWidget::CommandWidget(amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::Cm else collectionType = ProjectModel::INode::Type::Curve; } + else if (field.m_tp == amuse::SoundMacro::CmdIntrospection::Field::Type::SampleId) + { + collectionType = ProjectModel::INode::Type::Sample; + } auto* collection = g_MainWindow->projectModel()->getGroupNode(listing->currentNode())-> getCollectionOfType(collectionType); nf = new FieldProjectNode(collection); diff --git a/Editor/main.cpp b/Editor/main.cpp index 98c278b..c604aa2 100644 --- a/Editor/main.cpp +++ b/Editor/main.cpp @@ -5,6 +5,7 @@ #include "MainWindow.hpp" #include "boo/IApplication.hpp" #include +#include using namespace std::literals; @@ -101,5 +102,12 @@ int main(int argc, char* argv[]) MainWindow w; g_MainWindow = &w; w.show(); + + QCommandLineParser parser; + parser.process(a); + QStringList args = parser.positionalArguments(); + if (!args.empty()) + w.openProject(args.back()); + return a.exec(); } diff --git a/Editor/resources/IconSample.svg b/Editor/resources/IconSample.svg new file mode 100644 index 0000000..a5e54aa --- /dev/null +++ b/Editor/resources/IconSample.svg @@ -0,0 +1,74 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/Editor/resources/lang_de.ts b/Editor/resources/lang_de.ts index 2d436e1..86b4c75 100644 --- a/Editor/resources/lang_de.ts +++ b/Editor/resources/lang_de.ts @@ -13,6 +13,7 @@ MainWindow + Amuse Amuse @@ -22,7 +23,7 @@ &Datei - + P&roject Projekt @@ -60,12 +61,12 @@ & Wiederholen - + Recent &Projects - + &Cut &Schnitt @@ -85,9 +86,13 @@ &Löschen - &Import - &Einführen + &Einführen + + + + &Import Groups + @@ -170,12 +175,17 @@ - + + Reload Sample &Data + + + + Quit Verlassen - + The directory at '%1' must exist for the Amuse editor. Das Verzeichnis unter '% 1' muss für den Amuse-Editor vorhanden sein. @@ -200,7 +210,7 @@ Es konnte nicht in das Verzeichnis geschrieben werden - + No Audio Devices Found Keine Audiogeräte gefunden @@ -210,7 +220,7 @@ Keine MIDI-Geräte gefunden - + New Project Neues Projekt @@ -225,29 +235,39 @@ Das Verzeichnis '% 1' existiert nicht. - + Clear Recent Projects - - + + Amuse [%1/%2/%3] + + + + + Amuse [%1] + + + + + The directory at '%1' must not be empty. - - + + Directory empty - + SUSTAIN - + Bad Directory Schlechtes Verzeichnis @@ -258,18 +278,29 @@ - + + Scanning Project Projekt scannen - + Opening %1 Eröffnung% 1 - + + Reloading Samples + + + + + Scanning %1 + + + + Import Project Projekt importieren @@ -360,7 +391,7 @@ ProjectModel - + Sound Macros Sound-Makros @@ -384,6 +415,11 @@ Layers Lagen + + + Samples + + QObject @@ -401,7 +437,7 @@ QUndoStack - + Change %1 @@ -506,7 +542,7 @@ TargetButton - + Set step with target click diff --git a/Editor/resources/resources.qrc b/Editor/resources/resources.qrc index 2340610..b922d77 100644 --- a/Editor/resources/resources.qrc +++ b/Editor/resources/resources.qrc @@ -23,6 +23,7 @@ IconSoundMacroTarget.svg IconSoundMacroTargetDisabled.svg IconKill.svg + IconSample.svg FaceGrey.svg diff --git a/driver/amuseplay.cpp b/driver/amuseplay.cpp index 2e6c153..734dbc6 100644 --- a/driver/amuseplay.cpp +++ b/driver/amuseplay.cpp @@ -66,12 +66,12 @@ struct AppCallback : boo::IApplicationCallback int m_chanId = 0; int8_t m_octave = 4; int8_t m_velocity = 64; - std::shared_ptr m_seq; + amuse::ObjToken m_seq; amuse::ContainerRegistry::SongData* m_arrData = nullptr; /* SFX playback selection */ int m_sfxId = -1; - std::shared_ptr m_vox; + amuse::ObjToken m_vox; size_t m_lastVoxCount = 0; int8_t m_lastChanProg = -1; @@ -665,10 +665,10 @@ struct AppCallback : boo::IApplicationCallback std::list m_projs; std::map*, - const amuse::SongGroupIndex*>> + amuse::ObjToken>> allSongGroups; std::map*, - const amuse::SFXGroupIndex*>> + amuse::ObjToken>> allSFXGroups; size_t totalGroups = 0; @@ -680,10 +680,10 @@ struct AppCallback : boo::IApplicationCallback totalGroups += proj.sfxGroups().size() + proj.songGroups().size(); for (auto it = proj.songGroups().begin(); it != proj.songGroups().end(); ++it) - allSongGroups[it->first] = std::make_pair(&grp, &it->second); + allSongGroups[it->first] = std::make_pair(&grp, it->second); for (auto it = proj.sfxGroups().begin(); it != proj.sfxGroups().end(); ++it) - allSFXGroups[it->first] = std::make_pair(&grp, &it->second); + allSFXGroups[it->first] = std::make_pair(&grp, it->second); } while (m_running) @@ -825,14 +825,14 @@ struct AppCallback : boo::IApplicationCallback printf("Multiple Audio Groups discovered:\n"); for (const auto& pair : allSFXGroups) { - amuse::Printf(_S(" %d %s (SFXGroup) %" PRISize " sfx-entries\n"), pair.first, + amuse::Printf(_S(" %d %s (SFXGroup) %" PRISize " sfx-entries\n"), pair.first.id, pair.second.first->first.c_str(), pair.second.second->m_sfxEntries.size()); } for (const auto& pair : allSongGroups) { amuse::Printf(_S(" %d %s (SongGroup) %" PRISize " normal-pages, %" PRISize " drum-pages, %" PRISize " MIDI-setups\n"), - pair.first, pair.second.first->first.c_str(), pair.second.second->m_normPages.size(), + pair.first.id, pair.second.first->first.c_str(), pair.second.second->m_normPages.size(), pair.second.second->m_drumPages.size(), pair.second.second->m_midiSetups.size()); } @@ -884,8 +884,8 @@ struct AppCallback : boo::IApplicationCallback /* Make final group selection */ amuse::IntrusiveAudioGroupData* selData = nullptr; - const amuse::SongGroupIndex* songIndex = nullptr; - const amuse::SFXGroupIndex* sfxIndex = nullptr; + amuse::ObjToken songIndex; + amuse::ObjToken sfxIndex; auto songSearch = allSongGroups.find(m_groupId); if (songSearch != allSongGroups.end()) { diff --git a/driver/amuserender.cpp b/driver/amuserender.cpp index 580740b..24334f9 100644 --- a/driver/amuserender.cpp +++ b/driver/amuserender.cpp @@ -163,10 +163,10 @@ int main(int argc, const boo::SystemChar** argv) std::list m_projs; std::map*, const amuse::SongGroupIndex*>> + std::pair*, amuse::ObjToken>> allSongGroups; std::map*, const amuse::SFXGroupIndex*>> + std::pair*, amuse::ObjToken>> allSFXGroups; size_t totalGroups = 0; @@ -178,10 +178,10 @@ int main(int argc, const boo::SystemChar** argv) totalGroups += proj.sfxGroups().size() + proj.songGroups().size(); for (auto it = proj.songGroups().begin(); it != proj.songGroups().end(); ++it) - allSongGroups[it->first] = std::make_pair(&grp, &it->second); + allSongGroups[it->first] = std::make_pair(&grp, it->second); for (auto it = proj.sfxGroups().begin(); it != proj.sfxGroups().end(); ++it) - allSFXGroups[it->first] = std::make_pair(&grp, &it->second); + allSFXGroups[it->first] = std::make_pair(&grp, it->second); } /* Attempt loading song */ @@ -393,8 +393,8 @@ int main(int argc, const boo::SystemChar** argv) /* Make final group selection */ amuse::IntrusiveAudioGroupData* selData = nullptr; - const amuse::SongGroupIndex* songIndex = nullptr; - const amuse::SFXGroupIndex* sfxIndex = nullptr; + amuse::ObjToken songIndex; + amuse::ObjToken sfxIndex; auto songSearch = allSongGroups.find(m_groupId); if (songSearch != allSongGroups.end()) { @@ -465,7 +465,7 @@ int main(int argc, const boo::SystemChar** argv) } /* Enter playback loop */ - std::shared_ptr seq = engine.seqPlay(m_groupId, m_setupId, m_arrData->m_data.get()); + amuse::ObjToken seq = engine.seqPlay(m_groupId, m_setupId, m_arrData->m_data.get()); size_t wroteFrames = 0; signal(SIGINT, SIGINTHandler); do diff --git a/include/amuse/AudioGroup.hpp b/include/amuse/AudioGroup.hpp index 4cef010..f3a274b 100644 --- a/include/amuse/AudioGroup.hpp +++ b/include/amuse/AudioGroup.hpp @@ -28,8 +28,9 @@ public: void assign(const AudioGroupData& data); void assign(SystemStringView groupPath); - const AudioGroupSampleDirectory::Entry* getSample(SampleId sfxId) const; - const unsigned char* getSampleData(SampleId sfxId, const AudioGroupSampleDirectory::Entry* sample) const; + const SampleEntry* getSample(SampleId sfxId) const; + std::pair, const unsigned char*> + getSampleData(SampleId sfxId, const SampleEntry* sample) const; const AudioGroupProject& getProj() const { return m_proj; } const AudioGroupPool& getPool() const { return m_pool; } const AudioGroupSampleDirectory& getSdir() const { return m_sdir; } diff --git a/include/amuse/AudioGroupPool.hpp b/include/amuse/AudioGroupPool.hpp index ae0ac02..334c5c9 100644 --- a/include/amuse/AudioGroupPool.hpp +++ b/include/amuse/AudioGroupPool.hpp @@ -1338,10 +1338,10 @@ struct LayerMapping : BigDNA /** 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; template static AudioGroupPool _AudioGroupPool(athena::io::IStreamReader& r); @@ -1350,14 +1350,14 @@ public: static AudioGroupPool CreateAudioGroupPool(const AudioGroupData& data); static AudioGroupPool CreateAudioGroupPool(SystemStringView groupPath); - const std::unordered_map>& soundMacros() const { return m_soundMacros; } - const std::unordered_map>& tables() const { return m_tables; } - const std::unordered_map>& keymaps() const { return m_keymaps; } - const std::unordered_map>>& layers() const { return m_layers; } - std::unordered_map>& soundMacros() { return m_soundMacros; } - std::unordered_map>& tables() { return m_tables; } - std::unordered_map>& keymaps() { return m_keymaps; } - std::unordered_map>>& layers() { return m_layers; } + const std::unordered_map>& soundMacros() const { return m_soundMacros; } + const std::unordered_map>>& tables() const { return m_tables; } + const std::unordered_map>& keymaps() const { return m_keymaps; } + const std::unordered_map>>& layers() const { return m_layers; } + std::unordered_map>& soundMacros() { return m_soundMacros; } + std::unordered_map>>& tables() { return m_tables; } + std::unordered_map>& keymaps() { return m_keymaps; } + std::unordered_map>>& layers() { return m_layers; } const SoundMacro* soundMacro(ObjectId id) const; const Keymap* keymap(ObjectId id) const; diff --git a/include/amuse/AudioGroupProject.hpp b/include/amuse/AudioGroupProject.hpp index b17c537..d7f2b7d 100644 --- a/include/amuse/AudioGroupProject.hpp +++ b/include/amuse/AudioGroupProject.hpp @@ -180,8 +180,8 @@ struct SFXGroupIndex : AudioGroupIndex /** Collection of SongGroup and SFXGroup indexes */ class AudioGroupProject { - std::unordered_map> m_songGroups; - std::unordered_map> m_sfxGroups; + std::unordered_map> m_songGroups; + std::unordered_map> m_sfxGroups; AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag); template @@ -199,10 +199,10 @@ public: const SongGroupIndex* getSongGroupIndex(int groupId) const; const SFXGroupIndex* getSFXGroupIndex(int groupId) const; - const std::unordered_map>& songGroups() const { return m_songGroups; } - const std::unordered_map>& sfxGroups() const { return m_sfxGroups; } - std::unordered_map>& songGroups() { return m_songGroups; } - std::unordered_map>& sfxGroups() { return m_sfxGroups; } + const std::unordered_map>& songGroups() const { return m_songGroups; } + const std::unordered_map>& sfxGroups() const { return m_sfxGroups; } + std::unordered_map>& songGroups() { return m_songGroups; } + std::unordered_map>& sfxGroups() { return m_sfxGroups; } bool toYAML(SystemStringView groupPath) const; diff --git a/include/amuse/AudioGroupSampleDirectory.hpp b/include/amuse/AudioGroupSampleDirectory.hpp index f872e79..4676485 100644 --- a/include/amuse/AudioGroupSampleDirectory.hpp +++ b/include/amuse/AudioGroupSampleDirectory.hpp @@ -184,11 +184,11 @@ public: Value m_loopStartSample; Value m_loopLengthSamples; }; - struct Entry + struct EntryData { atUint32 m_sampleOff = 0; atUint32 m_unk = 0; - atUint8 m_pitch = 0; + atUint8 m_pitch = 60; atUint16 m_sampleRate = 0; atUint32 m_numSamples = 0; // Top 8 bits is SampleFormat atUint32 m_loopStartSample = 0; @@ -204,24 +204,24 @@ public: time_t m_looseModTime = 0; std::unique_ptr m_looseData; - Entry() = default; + EntryData() = default; template - Entry(const EntryDNA& in) + EntryData(const EntryDNA& in) : m_sampleOff(in.m_sampleOff), m_unk(in.m_unk), m_pitch(in.m_pitch), m_sampleRate(in.m_sampleRate), m_numSamples(in.m_numSamples), m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples), m_adpcmParmOffset(in.m_adpcmParmOffset) {} template - Entry(const MusyX1SdirEntry& in) + EntryData(const MusyX1SdirEntry& in) : m_sampleOff(in.m_sampleOff), m_unk(0), m_pitch(in.m_pitchSampleRate >> 24), m_sampleRate(in.m_pitchSampleRate & 0xffff), m_numSamples(in.m_numSamples), m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples), m_adpcmParmOffset(0) {} template - Entry(const MusyX1AbsSdirEntry& in) + EntryData(const MusyX1AbsSdirEntry& in) : m_sampleOff(in.m_sampleOff), m_unk(in.m_unk), m_pitch(in.m_pitchSampleRate >> 24), m_sampleRate(in.m_pitchSampleRate & 0xffff), m_numSamples(in.m_numSamples), m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples), @@ -243,14 +243,45 @@ public: return ret; } + void loadLooseDSP(SystemStringView dspPath); + void loadLooseWAV(SystemStringView wavPath); + }; + /* This double-wrapper allows Voices to keep a strong reference on + * a single instance of loaded loose data without being unexpectedly + * clobbered */ + struct Entry + { + ObjToken m_data; + + Entry() + : m_data(MakeObj()) {} + + template + Entry(const EntryDNA& in) + : m_data(MakeObj(in)) {} + + template + Entry(const MusyX1SdirEntry& in) + : m_data(MakeObj(in)) {} + + template + Entry(const MusyX1AbsSdirEntry& in) + : m_data(MakeObj(in)) {} + + template + EntryDNA toDNA(SFXId id) const + { + return m_data->toDNA(id); + } + void loadLooseData(SystemStringView basePath); }; private: - std::unordered_map m_entries; - static void _extractWAV(SampleId id, const Entry& ent, amuse::SystemStringView destDir, + std::unordered_map> m_entries; + static void _extractWAV(SampleId id, const EntryData& ent, amuse::SystemStringView destDir, const unsigned char* samp); - static void _extractCompressed(SampleId id, const Entry& ent, amuse::SystemStringView destDir, + static void _extractCompressed(SampleId id, const EntryData& ent, amuse::SystemStringView destDir, const unsigned char* samp); public: @@ -261,18 +292,23 @@ public: static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(const AudioGroupData& data); static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(SystemStringView groupPath); - const std::unordered_map& sampleEntries() const { return m_entries; } + const std::unordered_map>& sampleEntries() const { return m_entries; } void extractWAV(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const; void extractAllWAV(amuse::SystemStringView destDir, const unsigned char* samp) const; void extractCompressed(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const; void extractAllCompressed(amuse::SystemStringView destDir, const unsigned char* samp) const; + void reloadSampleData(SystemStringView groupPath); + AudioGroupSampleDirectory(const AudioGroupSampleDirectory&) = delete; AudioGroupSampleDirectory& operator=(const AudioGroupSampleDirectory&) = delete; AudioGroupSampleDirectory(AudioGroupSampleDirectory&&) = default; AudioGroupSampleDirectory& operator=(AudioGroupSampleDirectory&&) = default; }; + +using SampleEntry = AudioGroupSampleDirectory::Entry; +using SampleEntryData = AudioGroupSampleDirectory::EntryData; } #endif // __AMUSE_AUDIOGROUPSAMPLEDIR_HPP__ diff --git a/include/amuse/Common.hpp b/include/amuse/Common.hpp index bbbb524..6abfc64 100644 --- a/include/amuse/Common.hpp +++ b/include/amuse/Common.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include "athena/DNA.hpp" #ifndef _WIN32 @@ -134,6 +135,64 @@ struct LittleUInt24 : LittleDNA LittleUInt24& operator=(uint32_t valIn) { val = valIn; return *this; } }; +class IObj +{ + std::atomic_int m_refCount = {0}; +protected: + virtual ~IObj() = default; +public: + void increment() { m_refCount++; } + void decrement() + { + if (m_refCount.fetch_sub(1) == 1) + delete this; + } +}; + +template +class ObjToken; + +template +class ObjWrapper : IObj +{ + friend class ObjToken; + SubCls m_obj; + SubCls* get() { return &m_obj; } + const SubCls* get() const { return &m_obj; } +public: + template + ObjWrapper(_Args&&... args) : m_obj(std::forward<_Args>(args)...) {} +}; + +template +class ObjToken +{ + ObjWrapper* m_obj = nullptr; +public: + ObjToken() = default; + ObjToken(ObjWrapper* obj) : m_obj(obj) { if (m_obj) m_obj->increment(); } + ObjToken(const ObjToken& other) : m_obj(other.m_obj) { if (m_obj) m_obj->increment(); } + ObjToken(ObjToken&& other) : m_obj(other.m_obj) { other.m_obj = nullptr; } + ObjToken& operator=(ObjWrapper* obj) + { if (m_obj) m_obj->decrement(); m_obj = obj; if (m_obj) m_obj->increment(); return *this; } + ObjToken& operator=(const ObjToken& other) + { if (m_obj) m_obj->decrement(); m_obj = other.m_obj; if (m_obj) m_obj->increment(); return *this; } + ObjToken& operator=(ObjToken&& other) + { if (m_obj) m_obj->decrement(); m_obj = other.m_obj; other.m_obj = nullptr; return *this; } + ~ObjToken() { if (m_obj) m_obj->decrement(); } + SubCls* get() const { return m_obj->get(); } + SubCls* operator->() const { return m_obj->get(); } + SubCls& operator*() const { return *m_obj->get(); } + operator bool() const { return m_obj != nullptr; } + void reset() { if (m_obj) m_obj->decrement(); m_obj = nullptr; } +}; + +template +static inline ObjToken MakeObj(_Args&&... args) +{ + return new ObjWrapper(std::forward<_Args>(args)...); +} + #ifndef PRISize #ifdef _MSC_VER #define PRISize "Iu" @@ -480,6 +539,12 @@ DECL_ID_HASH(LayersId) DECL_ID_HASH(SongId) DECL_ID_HASH(SFXId) DECL_ID_HASH(GroupId) + +template +struct hash> +{ + size_t operator()(const amuse::ObjToken& val) const noexcept { return reinterpret_cast(val.get()); } +}; } namespace amuse diff --git a/include/amuse/Emitter.hpp b/include/amuse/Emitter.hpp index e9b8e67..048fef5 100644 --- a/include/amuse/Emitter.hpp +++ b/include/amuse/Emitter.hpp @@ -3,13 +3,13 @@ #include "Entity.hpp" #include "Common.hpp" +#include "Voice.hpp" #include #include #include namespace amuse { -class Voice; class Listener; using Vector3f = float[3]; @@ -37,7 +37,7 @@ static inline float Normalize(Vector3f& out) /** Voice wrapper with positional-3D level control */ class Emitter : public Entity { - std::shared_ptr m_vox; + ObjToken m_vox; Vector3f m_pos = {}; Vector3f m_dir = {}; float m_maxDist; @@ -54,13 +54,13 @@ class Emitter : public Entity public: ~Emitter(); - Emitter(Engine& engine, const AudioGroup& group, const std::shared_ptr& vox, + Emitter(Engine& engine, const AudioGroup& group, ObjToken vox, float maxDist, float minVol, float falloff, bool doppler); void setVectors(const float* pos, const float* dir); void setMaxVol(float maxVol) { m_maxVol = clamp(0.f, maxVol, 1.f); m_dirty = true; } - const std::shared_ptr& getVoice() const { return m_vox; } + ObjToken getVoice() const { return m_vox; } }; } diff --git a/include/amuse/Engine.hpp b/include/amuse/Engine.hpp index 6e9fca4..11b8089 100644 --- a/include/amuse/Engine.hpp +++ b/include/amuse/Engine.hpp @@ -42,13 +42,12 @@ class Engine AmplitudeMode m_ampMode; std::unique_ptr m_midiReader; std::unordered_map> m_audioGroups; - std::list> m_activeVoices; - std::list> m_activeEmitters; - std::list> m_activeListeners; - std::list> m_activeSequencers; - std::list> m_activeStudios; /* lifetime dependent on contributing audio entities */ + std::list> m_activeVoices; + std::list> m_activeEmitters; + std::list> m_activeListeners; + std::list> m_activeSequencers; bool m_defaultStudioReady = false; - std::shared_ptr m_defaultStudio; + ObjToken m_defaultStudio; std::unordered_map> m_sfxLookup; std::linear_congruential_engine m_random; int m_nextVid = 0; @@ -59,15 +58,14 @@ class Engine std::pair _findSongGroup(int groupId) const; std::pair _findSFXGroup(int groupId) const; - std::list>::iterator _allocateVoice(const AudioGroup& group, int groupId, double sampleRate, - bool dynamicPitch, bool emitter, - std::weak_ptr studio); - std::list>::iterator _allocateSequencer(const AudioGroup& group, int groupId, - int setupId, std::weak_ptr studio); - std::shared_ptr _allocateStudio(bool mainOut); - std::list>::iterator _destroyVoice(std::list>::iterator it); - std::list>::iterator - _destroySequencer(std::list>::iterator it); + std::list>::iterator _allocateVoice(const AudioGroup& group, int groupId, double sampleRate, + bool dynamicPitch, bool emitter, ObjToken studio); + std::list>::iterator _allocateSequencer(const AudioGroup& group, int groupId, + int setupId, ObjToken studio); + ObjToken _allocateStudio(bool mainOut); + std::list>::iterator _destroyVoice(std::list>::iterator it); + std::list>::iterator + _destroySequencer(std::list>::iterator it); void _bringOutYourDead(); public: @@ -84,48 +82,46 @@ public: void removeAudioGroup(const AudioGroupData& data); /** Access engine's default studio */ - std::shared_ptr getDefaultStudio() { return m_defaultStudio; } + ObjToken getDefaultStudio() { return m_defaultStudio; } /** Create new Studio within engine */ - std::shared_ptr addStudio(bool mainOut); + ObjToken addStudio(bool mainOut); /** Start soundFX playing from loaded audio groups */ - std::shared_ptr fxStart(int sfxId, float vol, float pan, std::weak_ptr smx); - std::shared_ptr fxStart(int sfxId, float vol, float pan) + ObjToken fxStart(int sfxId, float vol, float pan, ObjToken smx); + ObjToken fxStart(int sfxId, float vol, float pan) { return fxStart(sfxId, vol, pan, m_defaultStudio); } /** Start SoundMacro node playing directly (for editor use) */ - std::shared_ptr macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, - uint8_t vel, uint8_t mod, std::weak_ptr smx); - std::shared_ptr macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, - uint8_t vel, uint8_t mod) + ObjToken macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, + uint8_t vel, uint8_t mod, ObjToken smx); + ObjToken macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, + uint8_t vel, uint8_t mod) { return macroStart(group, id, key, vel, mod, m_defaultStudio); } /** Start soundFX playing from loaded audio groups, attach to positional emitter */ - std::shared_ptr addEmitter(const float* pos, const float* dir, float maxDist, float falloff, - int sfxId, float minVol, float maxVol, bool doppler, - std::weak_ptr smx); - std::shared_ptr addEmitter(const float* pos, const float* dir, float maxDist, float falloff, - int sfxId, float minVol, float maxVol, bool doppler) + ObjToken addEmitter(const float* pos, const float* dir, float maxDist, float falloff, + int sfxId, float minVol, float maxVol, bool doppler, ObjToken smx); + ObjToken addEmitter(const float* pos, const float* dir, float maxDist, float falloff, + int sfxId, float minVol, float maxVol, bool doppler) { return addEmitter(pos, dir, maxDist, falloff, sfxId, minVol, maxVol, doppler, m_defaultStudio); } /** Build listener and add to engine's listener list */ - std::shared_ptr addListener(const float* pos, const float* dir, const float* heading, const float* up, - float frontDiff, float backDiff, float soundSpeed, float volume); + ObjToken addListener(const float* pos, const float* dir, const float* heading, const float* up, + float frontDiff, float backDiff, float soundSpeed, float volume); /** Remove listener from engine's listener list */ void removeListener(Listener* listener); /** Start song playing from loaded audio groups */ - std::shared_ptr seqPlay(int groupId, int songId, const unsigned char* arrData, - std::weak_ptr smx); - std::shared_ptr seqPlay(int groupId, int songId, const unsigned char* arrData) + ObjToken seqPlay(int groupId, int songId, const unsigned char* arrData, ObjToken smx); + ObjToken seqPlay(int groupId, int songId, const unsigned char* arrData) { return seqPlay(groupId, songId, arrData, m_defaultStudio); } @@ -134,7 +130,7 @@ public: void setVolume(float vol); /** Find voice from VoiceId */ - std::shared_ptr findVoice(int vid); + ObjToken findVoice(int vid); /** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */ void killKeygroup(uint8_t kg, bool now); @@ -146,10 +142,10 @@ public: uint32_t nextRandom() { return m_random(); } /** Obtain list of active voices */ - std::list>& getActiveVoices() { return m_activeVoices; } + std::list>& getActiveVoices() { return m_activeVoices; } /** Obtain list of active sequencers */ - std::list>& getActiveSequencers() { return m_activeSequencers; } + std::list>& getActiveSequencers() { return m_activeSequencers; } /** All mixing occurs in virtual 5ms intervals; * this is called at the start of each interval for all mixable entities */ diff --git a/include/amuse/Entity.hpp b/include/amuse/Entity.hpp index f3e69d6..631e16d 100644 --- a/include/amuse/Entity.hpp +++ b/include/amuse/Entity.hpp @@ -45,6 +45,7 @@ public: const AudioGroup& getAudioGroup() const { return m_audioGroup; } int getGroupId() const { return m_groupId; } ObjectId getObjectId() const { return m_objectId; } + bool isDestroyed() const { return m_destroyed; } }; } diff --git a/include/amuse/Sequencer.hpp b/include/amuse/Sequencer.hpp index 92d27af..393f476 100644 --- a/include/amuse/Sequencer.hpp +++ b/include/amuse/Sequencer.hpp @@ -4,6 +4,8 @@ #include "Entity.hpp" #include "AudioGroupProject.hpp" #include "SongState.hpp" +#include "Studio.hpp" +#include "Voice.hpp" #include #include #include @@ -11,8 +13,6 @@ namespace amuse { -class Studio; -class Voice; /** State of sequencer over lifetime */ enum class SequencerState @@ -30,7 +30,7 @@ class Sequencer : public Entity const SongGroupIndex::MIDISetup* m_midiSetup = nullptr; /**< Selected MIDI setup (may be null) */ const SFXGroupIndex* m_sfxGroup = nullptr; /**< SFX Groups are alternatively referenced here */ std::vector m_sfxMappings; /**< SFX entries are mapped to MIDI keys this via this */ - std::shared_ptr m_studio; /**< Studio this sequencer outputs to */ + ObjToken m_studio; /**< Studio this sequencer outputs to */ const unsigned char* m_arrData = nullptr; /**< Current playing arrangement data */ SongState m_songState; /**< State of current arrangement playback */ @@ -58,9 +58,9 @@ class Sequencer : public Entity operator bool() const { return m_parent != nullptr; } /** Voices corresponding to currently-pressed keys in channel */ - std::unordered_map> m_chanVoxs; - std::unordered_set> m_keyoffVoxs; - std::weak_ptr m_lastVoice; + std::unordered_map> m_chanVoxs; + std::unordered_set> m_keyoffVoxs; + ObjToken m_lastVoice; int8_t m_ctrlVals[128] = {}; /**< MIDI controller values */ float m_curPitchWheel = 0.f; /**< MIDI pitch-wheel */ int8_t m_curProgram = 0; /**< MIDI program number */ @@ -69,7 +69,7 @@ class Sequencer : public Entity void _bringOutYourDead(); size_t getVoiceCount() const; - std::shared_ptr keyOn(uint8_t note, uint8_t velocity); + ObjToken keyOn(uint8_t note, uint8_t velocity); void keyOff(uint8_t note, uint8_t velocity); void setCtrlValue(uint8_t ctrl, int8_t val); bool programChange(int8_t prog); @@ -80,7 +80,7 @@ class Sequencer : public Entity void setPan(float pan); void allOff(); void killKeygroup(uint8_t kg, bool now); - std::shared_ptr findVoice(int vid); + ObjToken findVoice(int vid); void sendMacroMessage(ObjectId macroId, int32_t val); }; std::array m_chanStates; /**< Lazily-allocated channel states */ @@ -91,15 +91,15 @@ class Sequencer : public Entity public: ~Sequencer(); Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SongGroupIndex* songGroup, int setupId, - std::weak_ptr studio); + ObjToken studio); Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SFXGroupIndex* sfxGroup, - std::weak_ptr studio); + ObjToken studio); /** Advance current song data (if any) */ void advance(double dt); /** Obtain pointer to Sequencer's Submix */ - std::shared_ptr getStudio() { return m_studio; } + ObjToken getStudio() { return m_studio; } /** Get current state of sequencer */ SequencerState state() const { return m_state; } @@ -108,7 +108,7 @@ public: size_t getVoiceCount() const; /** Register key press with voice set */ - std::shared_ptr keyOn(uint8_t chan, uint8_t note, uint8_t velocity); + ObjToken keyOn(uint8_t chan, uint8_t note, uint8_t velocity); /** Register key release with voice set */ void keyOff(uint8_t chan, uint8_t note, uint8_t velocity); @@ -129,7 +129,7 @@ public: void killKeygroup(uint8_t kg, bool now); /** Find voice instance contained within Sequencer */ - std::shared_ptr findVoice(int vid); + ObjToken findVoice(int vid); /** Send all voices using `macroId` the message `val` */ void sendMacroMessage(ObjectId macroId, int32_t val); diff --git a/include/amuse/Studio.hpp b/include/amuse/Studio.hpp index 8034eb0..79341bd 100644 --- a/include/amuse/Studio.hpp +++ b/include/amuse/Studio.hpp @@ -4,7 +4,6 @@ #include #include #include "Entity.hpp" -#include "Voice.hpp" #include "Submix.hpp" #include @@ -20,11 +19,11 @@ class Studio Submix m_auxB; struct StudioSend { - std::shared_ptr m_targetStudio; + ObjToken m_targetStudio; float m_dryLevel; float m_auxALevel; float m_auxBLevel; - StudioSend(std::weak_ptr studio, float dry, float auxA, float auxB) + StudioSend(ObjToken studio, float dry, float auxA, float auxB) : m_targetStudio(studio), m_dryLevel(dry), m_auxALevel(auxA), m_auxBLevel(auxB) { } @@ -38,7 +37,7 @@ public: Studio(Engine& engine, bool mainOut); /** Register a target Studio to send this Studio's mixing busses */ - void addStudioSend(std::weak_ptr studio, float dry, float auxA, float auxB); + void addStudioSend(ObjToken studio, float dry, float auxA, float auxB); /** Advise submixes of changing sample rate */ void resetOutputSampleRate(double sampleRate); diff --git a/include/amuse/Voice.hpp b/include/amuse/Voice.hpp index 5f42d3a..6e52aa8 100644 --- a/include/amuse/Voice.hpp +++ b/include/amuse/Voice.hpp @@ -10,11 +10,11 @@ #include "AudioGroupSampleDirectory.hpp" #include "AudioGroup.hpp" #include "Envelope.hpp" +#include "Studio.hpp" namespace amuse { class IBackendVoice; -class Studio; struct Keymap; struct LayerMapping; @@ -47,7 +47,7 @@ class Voice : public Entity 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 */ + ObjToken m_studio; /**< Studio this voice outputs to */ std::unique_ptr m_backendVoice; /**< Handle to client-implemented backend voice */ SoundMacroState m_state; /**< State container for SoundMacro playback */ @@ -55,10 +55,10 @@ class Voice : public Entity SoundMacroState::EventTrap m_sampleEndTrap; /**< Trap for sampleend (SoundMacro overrides voice removal) */ SoundMacroState::EventTrap m_messageTrap; /**< Trap for messages sent from other SoundMacros */ int32_t m_latestMessage = 0; /**< Latest message received on voice */ - std::list> m_childVoices; /**< Child voices for PLAYMACRO usage */ + std::list> m_childVoices; /**< Child voices for PLAYMACRO usage */ uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */ - const AudioGroupSampleDirectory::Entry* m_curSample = nullptr; /**< Current sample entry playing */ + ObjToken m_curSample; /**< Current sample entry playing */ const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */ SampleFormat m_curFormat; /**< Current sample format playing */ uint32_t m_curSamplePos = 0; /**< Current sample position */ @@ -82,9 +82,7 @@ class Voice : public Entity float m_curVol = 1.f; /**< Current volume of voice */ float m_curReverbVol = 0.f; /**< Current reverb volume of voice */ float m_curAuxBVol = 0.f; /**< Current AuxB volume of voice */ - float m_userPan = 0.f; /**< User pan of voice */ float m_curPan = 0.f; /**< Current pan of voice */ - float m_userSpan = -1.f; /**< User span of voice */ float m_curSpan = -1.f; /**< Current surround pan of voice */ float m_curPitchWheel = 0.f; /**< Current normalized wheel value for control */ int32_t m_pitchWheelUp = 600; /**< Up range for pitchwheel control in cents */ @@ -156,11 +154,11 @@ class Voice : public Entity bool _isRecursivelyDead(); void _bringOutYourDead(); static uint32_t _GetBlockSampleCount(SampleFormat fmt); - std::shared_ptr _findVoice(int vid, std::weak_ptr thisPtr); + ObjToken _findVoice(int vid, ObjToken thisPtr); std::unique_ptr& _ensureCtrlVals(); - std::list>::iterator _allocateVoice(double sampleRate, bool dynamicPitch); - std::list>::iterator _destroyVoice(std::list>::iterator it); + std::list>::iterator _allocateVoice(double sampleRate, bool dynamicPitch); + std::list>::iterator _destroyVoice(std::list>::iterator it); bool _loadSoundMacro(SoundMacroId id, const SoundMacro* macroData, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false); @@ -168,8 +166,8 @@ class Voice : public Entity uint8_t midiVel, uint8_t midiMod, bool pushPc = false); bool _loadLayer(const std::vector& layer, 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); + ObjToken _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, + uint8_t midiVel, uint8_t midiMod, bool pushPc = false); void _panLaw(float coefsOut[8], float frontPan, float backPan, float totalSpan) const; void _setPan(float pan); @@ -180,9 +178,9 @@ class Voice : public Entity public: ~Voice(); - Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, std::weak_ptr studio); + Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, ObjToken studio); Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter, - std::weak_ptr studio); + ObjToken studio); /** Called before each supplyAudio invocation to prepare voice * backend for possible parameter updates */ @@ -199,7 +197,7 @@ public: void routeAudio(size_t frames, double dt, int busId, float* in, float* out); /** Obtain pointer to Voice's Studio */ - std::shared_ptr getStudio() { return m_studio; } + ObjToken getStudio() { return m_studio; } /** Get current state of voice */ VoiceState state() const { return m_voxState; } @@ -211,7 +209,7 @@ public: int maxVid() const; /** Allocate parallel macro and tie to voice for possible emitter influence */ - std::shared_ptr startChildMacro(int8_t addNote, ObjectId macroId, int macroStep); + ObjToken startChildMacro(int8_t addNote, ObjectId macroId, int macroStep); /** Load specified SoundMacro Object from within group into voice */ bool loadMacroObject(SoundMacroId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, diff --git a/lib/AudioGroup.cpp b/lib/AudioGroup.cpp index 9614ecc..ea4b772 100644 --- a/lib/AudioGroup.cpp +++ b/lib/AudioGroup.cpp @@ -21,17 +21,18 @@ void AudioGroup::assign(SystemStringView groupPath) m_samp = nullptr; } -const AudioGroupSampleDirectory::Entry* AudioGroup::getSample(SampleId sfxId) const +const SampleEntry* AudioGroup::getSample(SampleId sfxId) const { auto search = m_sdir.m_entries.find(sfxId); if (search == m_sdir.m_entries.cend()) return nullptr; - return &search->second; + return search->second.get(); } -const unsigned char* AudioGroup::getSampleData(SampleId sfxId, const AudioGroupSampleDirectory::Entry* sample) const +std::pair, const unsigned char*> + AudioGroup::getSampleData(SampleId sfxId, const SampleEntry* sample) const { - if (sample->m_looseData) + if (sample->m_data->m_looseData) { setIdDatabases(); #if _WIN32 @@ -41,9 +42,9 @@ const unsigned char* AudioGroup::getSampleData(SampleId sfxId, const AudioGroupS SystemString basePath = m_groupPath + _S('/') + SampleId::CurNameDB->resolveNameFromId(sfxId).data(); #endif - const_cast(sample)->loadLooseData(basePath); - return sample->m_looseData.get(); + const_cast(sample)->loadLooseData(basePath); + return {sample->m_data, sample->m_data->m_looseData.get()}; } - return m_samp + sample->m_sampleOff; + return {{}, m_samp + sample->m_data->m_sampleOff}; } } diff --git a/lib/AudioGroupPool.cpp b/lib/AudioGroupPool.cpp index ea89e2a..a8ba174 100644 --- a/lib/AudioGroupPool.cpp +++ b/lib/AudioGroupPool.cpp @@ -109,7 +109,7 @@ AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r) atInt64 startPos = r.position(); objHead.read(r); auto& macro = ret.m_soundMacros[objHead.objectId.id]; - macro = std::make_shared(); + macro = MakeObj(); macro->template readCmds(r, objHead.size - 8); r.seek(startPos + objHead.size, athena::Begin); } @@ -127,17 +127,17 @@ AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r) switch (objHead.size) { case 0x10: - ptr = std::make_shared(); - static_cast(*ptr).read(r); + ptr = MakeObj>(std::make_unique()); + static_cast(**ptr).read(r); break; case 0x1c: - ptr = std::make_shared(); - static_cast(*ptr).read(r); + ptr = MakeObj>(std::make_unique()); + static_cast(**ptr).read(r); break; default: - ptr = std::make_shared(); - static_cast(*ptr).data.resize(objHead.size - 8); - r.readUBytesToBuf(&static_cast(*ptr).data[0], objHead.size - 8); + ptr = MakeObj>(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); @@ -155,7 +155,7 @@ AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r) KeymapDNA kmData; kmData.read(r); auto& km = ret.m_keymaps[objHead.objectId.id]; - km = std::make_shared(kmData); + km = MakeObj(kmData); r.seek(startPos + objHead.size, athena::Begin); } } @@ -169,7 +169,7 @@ AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r) atInt64 startPos = r.position(); objHead.read(r); auto& lm = ret.m_layers[objHead.objectId.id]; - lm = std::make_shared>(); + lm = MakeObj>(); uint32_t count; athena::io::Read::Do({}, count, r); lm->reserve(count); @@ -262,7 +262,7 @@ AudioGroupPool AudioGroupPool::CreateAudioGroupPool(SystemStringView groupPath) for (const auto& sm : r.getCurNode()->m_mapChildren) { auto& smOut = ret.m_soundMacros[SoundMacroId::CurNameDB->resolveIdFromName(sm.first)]; - smOut = std::make_shared(); + smOut = MakeObj(); size_t cmdCount; if (auto __v = r.enterSubVector(sm.first.c_str(), cmdCount)) { @@ -288,20 +288,20 @@ AudioGroupPool AudioGroupPool::CreateAudioGroupPool(SystemStringView groupPath) if (auto __vta = r.enterSubRecord("velToAttack")) { __vta.leave(); - tableOut = std::make_shared(); - static_cast(*tableOut).read(r); + tableOut = MakeObj>(std::make_unique()); + static_cast(**tableOut).read(r); } else { - tableOut = std::make_shared(); - static_cast(*tableOut).read(r); + tableOut = MakeObj>(std::make_unique()); + static_cast(**tableOut).read(r); } } else if (auto __dat = r.enterSubRecord("data")) { __dat.leave(); - tableOut = std::make_shared(); - static_cast(*tableOut).read(r); + tableOut = MakeObj>(std::make_unique()); + static_cast(**tableOut).read(r); } } } @@ -314,7 +314,7 @@ AudioGroupPool AudioGroupPool::CreateAudioGroupPool(SystemStringView groupPath) if (auto __v = r.enterSubRecord(k.first.c_str())) { auto& kmOut = ret.m_keymaps[KeymapId::CurNameDB->resolveIdFromName(k.first)]; - kmOut = std::make_shared(); + kmOut = MakeObj(); kmOut->read(r); } } @@ -328,7 +328,7 @@ AudioGroupPool AudioGroupPool::CreateAudioGroupPool(SystemStringView groupPath) if (auto __v = r.enterSubVector(l.first.c_str(), mappingCount)) { auto& layOut = ret.m_layers[LayersId::CurNameDB->resolveIdFromName(l.first)]; - layOut = std::make_shared>(); + layOut = MakeObj>(); layOut->reserve(mappingCount); for (int lm = 0; lm < mappingCount; ++lm) { @@ -402,25 +402,25 @@ const std::vector* AudioGroupPool::layer(ObjectId id) const const ADSR* AudioGroupPool::tableAsAdsr(ObjectId id) const { auto search = m_tables.find(id); - if (search == m_tables.cend() || search->second->Isa() != ITable::Type::ADSR) + if (search == m_tables.cend() || (*search->second)->Isa() != ITable::Type::ADSR) return nullptr; - return static_cast(search->second.get()); + return static_cast((*search->second).get()); } const ADSRDLS* AudioGroupPool::tableAsAdsrDLS(ObjectId id) const { auto search = m_tables.find(id); - if (search == m_tables.cend() || search->second->Isa() != ITable::Type::ADSRDLS) + if (search == m_tables.cend() || (*search->second)->Isa() != ITable::Type::ADSRDLS) return nullptr; - return static_cast(search->second.get()); + return static_cast((*search->second).get()); } const Curve* AudioGroupPool::tableAsCurves(ObjectId id) const { auto search = m_tables.find(id); - if (search == m_tables.cend() || search->second->Isa() != ITable::Type::Curve) + if (search == m_tables.cend() || (*search->second)->Isa() != ITable::Type::Curve) return nullptr; - return static_cast(search->second.get()); + return static_cast((*search->second).get()); } static SoundMacro::CmdOp _ReadCmdOp(athena::io::MemoryReader& r) @@ -979,7 +979,7 @@ bool AudioGroupPool::toYAML(SystemStringView groupPath) const if (auto __v = w.enterSubRecord(TableId::CurNameDB->resolveNameFromId(p.first).data())) { w.setStyle(athena::io::YAMLNodeStyle::Flow); - p.second.get()->write(w); + (*p.second.get())->write(w); } } } diff --git a/lib/AudioGroupProject.cpp b/lib/AudioGroupProject.cpp index 04bfa45..cf86dfe 100644 --- a/lib/AudioGroupProject.cpp +++ b/lib/AudioGroupProject.cpp @@ -81,7 +81,7 @@ AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag) if (header.type == GroupType::Song) { auto& idx = m_songGroups[header.groupId]; - idx = std::make_shared(); + idx = MakeObj(); /* Normal pages */ r.seek(header.pageTableOff, athena::Begin); @@ -116,7 +116,7 @@ AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag) else if (header.type == GroupType::SFX) { auto& idx = m_sfxGroups[header.groupId]; - idx = std::make_shared(); + idx = MakeObj(); /* SFX entries */ r.seek(header.pageTableOff, athena::Begin); @@ -179,7 +179,7 @@ AudioGroupProject AudioGroupProject::_AudioGroupProject(athena::io::IStreamReade if (header.type == GroupType::Song) { auto& idx = ret.m_songGroups[header.groupId]; - idx = std::make_shared(); + idx = MakeObj(); if (absOffs) { @@ -255,7 +255,7 @@ AudioGroupProject AudioGroupProject::_AudioGroupProject(athena::io::IStreamReade else if (header.type == GroupType::SFX) { auto& idx = ret.m_sfxGroups[header.groupId]; - idx = std::make_shared(); + idx = MakeObj(); /* SFX entries */ r.seek(subDataOff + header.pageTableOff, athena::Begin); @@ -333,7 +333,7 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView gr GroupId::CurNameDB->registerPair(groupName, groupId); auto& idx = ret.m_songGroups[groupId]; - idx = std::make_shared(); + idx = MakeObj(); if (auto __v2 = r.enterSubRecord("normPages")) { idx->m_normPages.reserve(r.getCurNode()->m_mapChildren.size()); @@ -387,7 +387,7 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView gr GroupId::CurNameDB->registerPair(groupName, groupId); auto& idx = ret.m_sfxGroups[groupId]; - idx = std::make_shared(); + idx = MakeObj(); for (const auto& sfx : r.getCurNode()->m_mapChildren) if (auto __r2 = r.enterSubRecord(sfx.first.c_str())) { diff --git a/lib/AudioGroupSampleDirectory.cpp b/lib/AudioGroupSampleDirectory.cpp index fbcae2b..2122201 100644 --- a/lib/AudioGroupSampleDirectory.cpp +++ b/lib/AudioGroupSampleDirectory.cpp @@ -44,17 +44,17 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader& { EntryDNA ent; ent.read(r); - m_entries[ent.m_sfxId] = ent; + m_entries[ent.m_sfxId] = MakeObj(ent); SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId); } for (auto& p : m_entries) { - if (p.second.m_adpcmParmOffset) + if (p.second->m_data->m_adpcmParmOffset) { - r.seek(p.second.m_adpcmParmOffset, athena::Begin); - r.readUBytesToBuf(&p.second.m_ADPCMParms, sizeof(ADPCMParms::DSPParms)); - p.second.m_ADPCMParms.swapBigDSP(); + r.seek(p.second->m_data->m_adpcmParmOffset, athena::Begin); + r.readUBytesToBuf(&p.second->m_data->m_ADPCMParms, sizeof(ADPCMParms::DSPParms)); + p.second->m_data->m_ADPCMParms.swapBigDSP(); } } } @@ -68,7 +68,7 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader& { MusyX1AbsSdirEntry ent; ent.read(r); - m_entries[ent.m_sfxId] = ent; + m_entries[ent.m_sfxId] = MakeObj(ent); SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId); } } @@ -78,15 +78,15 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader& { MusyX1SdirEntry ent; ent.read(r); - m_entries[ent.m_sfxId] = ent; + m_entries[ent.m_sfxId] = MakeObj(ent); SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId); } } for (auto& p : m_entries) { - memcpy(&p.second.m_ADPCMParms, sampData + p.second.m_sampleOff, sizeof(ADPCMParms::VADPCMParms)); - p.second.m_ADPCMParms.swapBigVADPCM(); + memcpy(&p.second->m_data->m_ADPCMParms, sampData + p.second->m_data->m_sampleOff, sizeof(ADPCMParms::VADPCMParms)); + p.second->m_data->m_ADPCMParms.swapBigVADPCM(); } } @@ -98,9 +98,9 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader& { MusyX1AbsSdirEntry ent; ent.read(r); - Entry& store = m_entries[ent.m_sfxId]; - store = ent; - store.m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24; + auto& store = m_entries[ent.m_sfxId]; + store = MakeObj(ent); + store->m_data->m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24; SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId); } } @@ -110,9 +110,9 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader& { MusyX1SdirEntry ent; ent.read(r); - Entry& store = m_entries[ent.m_sfxId]; - store = ent; - store.m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24; + auto& store = m_entries[ent.m_sfxId]; + store = MakeObj(ent); + store->m_data->m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24; SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId); } } @@ -149,6 +149,88 @@ static uint32_t DSPNibbleToSample(uint32_t nibble) return ret; } +void AudioGroupSampleDirectory::EntryData::loadLooseDSP(SystemStringView dspPath) +{ + athena::io::FileReader r(dspPath); + if (!r.hasError()) + { + DSPADPCMHeader header; + header.read(r); + m_pitch = header.m_pitch; + if (m_pitch == 0) + m_pitch = 60; + m_sampleRate = atUint16(header.x8_sample_rate); + m_numSamples = header.x0_num_samples; + if (header.xc_loop_flag) + { + m_loopStartSample = DSPNibbleToSample(header.x10_loop_start_nibble); + m_loopLengthSamples = DSPNibbleToSample(header.x14_loop_end_nibble) - m_loopStartSample; + } + m_ADPCMParms.dsp.m_ps = uint8_t(header.x3e_ps); + m_ADPCMParms.dsp.m_lps = uint8_t(header.x44_loop_ps); + m_ADPCMParms.dsp.m_hist1 = header.x40_hist1; + m_ADPCMParms.dsp.m_hist2 = header.x42_hist2; + for (int i = 0; i < 8; ++i) + for (int j = 0; j < 2; ++j) + m_ADPCMParms.dsp.m_coefs[i][j] = header.x1c_coef[i][j]; + + uint32_t dataLen = (header.x4_num_nibbles + 1) / 2; + m_looseData.reset(new uint8_t[dataLen]); + r.readUBytesToBuf(m_looseData.get(), dataLen); + } +} + +void AudioGroupSampleDirectory::EntryData::loadLooseWAV(SystemStringView wavPath) +{ + athena::io::FileReader r(wavPath); + if (!r.hasError()) + { + atUint32 riffMagic = r.readUint32Little(); + if (riffMagic != SBIG('RIFF')) + return; + atUint32 wavChuckSize = r.readUint32Little(); + atUint32 wavMagic = r.readUint32Little(); + if (wavMagic != SBIG('WAVE')) + return; + + while (r.position() < wavChuckSize + 8) + { + atUint32 chunkMagic = r.readUint32Little(); + atUint32 chunkSize = r.readUint32Little(); + atUint64 startPos = r.position(); + if (chunkMagic == SBIG('fmt ')) + { + WAVFormatChunk fmt; + fmt.read(r); + m_sampleRate = atUint16(fmt.sampleRate); + } + else if (chunkMagic == SBIG('smpl')) + { + WAVSampleChunk smpl; + smpl.read(r); + m_pitch = atUint8(smpl.midiNote); + if (m_pitch == 0) + m_pitch = 60; + + if (smpl.numSampleLoops) + { + WAVSampleLoop loop; + loop.read(r); + m_loopStartSample = loop.start; + m_loopLengthSamples = loop.end - loop.start + 1; + } + } + else if (chunkMagic == SBIG('data')) + { + m_numSamples = ((chunkSize / 2) & 0xffffff) | (atUint32(SampleFormat::PCM_PC) << 24); + m_looseData.reset(new uint8_t[chunkSize]); + r.readUBytesToBuf(m_looseData.get(), chunkSize); + } + r.seek(startPos + chunkSize, athena::Begin); + } + } +} + void AudioGroupSampleDirectory::Entry::loadLooseData(SystemStringView basePath) { SystemString wavPath = SystemString(basePath) + _S(".wav"); @@ -165,88 +247,19 @@ void AudioGroupSampleDirectory::Entry::loadLooseData(SystemStringView basePath) wavValid = false; } - if (dspValid && (!m_looseData || dspStat.st_mtime > m_looseModTime)) + EntryData& curData = *m_data; + + if (dspValid && (!curData.m_looseData || dspStat.st_mtime > curData.m_looseModTime)) { - athena::io::FileReader r(dspPath); - if (!r.hasError()) - { - DSPADPCMHeader header; - header.read(r); - m_pitch = header.m_pitch; - m_sampleRate = atUint16(header.x8_sample_rate); - m_numSamples = header.x0_num_samples; - if (header.xc_loop_flag) - { - m_loopStartSample = DSPNibbleToSample(header.x10_loop_start_nibble); - m_loopLengthSamples = DSPNibbleToSample(header.x14_loop_end_nibble) - m_loopStartSample; - } - m_ADPCMParms.dsp.m_ps = uint8_t(header.x3e_ps); - m_ADPCMParms.dsp.m_lps = uint8_t(header.x44_loop_ps); - m_ADPCMParms.dsp.m_hist1 = header.x40_hist1; - m_ADPCMParms.dsp.m_hist2 = header.x42_hist2; - for (int i = 0; i < 8; ++i) - for (int j = 0; j < 2; ++j) - m_ADPCMParms.dsp.m_coefs[i][j] = header.x1c_coef[i][j]; - - uint32_t dataLen = (header.x4_num_nibbles + 1) / 2; - m_looseData.reset(new uint8_t[dataLen]); - r.readUBytesToBuf(m_looseData.get(), dataLen); - - m_looseModTime = dspStat.st_mtime; - return; - } + m_data = MakeObj(); + m_data->loadLooseDSP(dspPath); + m_data->m_looseModTime = dspStat.st_mtime; } - - if (wavValid && (!m_looseData || wavStat.st_mtime > m_looseModTime)) + else if (wavValid && (!curData.m_looseData || wavStat.st_mtime > curData.m_looseModTime)) { - athena::io::FileReader r(wavPath); - if (!r.hasError()) - { - atUint32 riffMagic = r.readUint32Little(); - if (riffMagic != SBIG('RIFF')) - return; - atUint32 wavChuckSize = r.readUint32Little(); - atUint32 wavMagic = r.readUint32Little(); - if (wavMagic != SBIG('WAVE')) - return; - - while (r.position() < wavChuckSize + 8) - { - atUint32 chunkMagic = r.readUint32Little(); - atUint32 chunkSize = r.readUint32Little(); - atUint64 startPos = r.position(); - if (chunkMagic == SBIG('fmt ')) - { - WAVFormatChunk fmt; - fmt.read(r); - m_sampleRate = atUint16(fmt.sampleRate); - } - else if (chunkMagic == SBIG('smpl')) - { - WAVSampleChunk smpl; - smpl.read(r); - m_pitch = atUint8(smpl.midiNote); - - if (smpl.numSampleLoops) - { - WAVSampleLoop loop; - loop.read(r); - m_loopStartSample = loop.start; - m_loopLengthSamples = loop.end - loop.start + 1; - } - } - else if (chunkMagic == SBIG('data')) - { - m_numSamples = ((chunkSize / 2) & 0xffffff) | (atUint32(SampleFormat::PCM_PC) << 24); - m_looseData.reset(new uint8_t[chunkSize]); - r.readUBytesToBuf(m_looseData.get(), chunkSize); - } - r.seek(startPos + chunkSize, athena::Begin); - } - - m_looseModTime = wavStat.st_mtime; - return; - } + m_data = MakeObj(); + m_data->loadLooseWAV(wavPath); + m_data->m_looseModTime = wavStat.st_mtime; } } @@ -273,15 +286,16 @@ AudioGroupSampleDirectory AudioGroupSampleDirectory::CreateAudioGroupSampleDirec SampleId::CurNameDB->registerPair(baseName, sampleId); #endif - Entry& entry = ret.m_entries[sampleId]; + auto& entry = ret.m_entries[sampleId]; + entry = MakeObj(); SystemString basePath = SystemString(ent.m_path.begin(), ent.m_path.begin() + ent.m_path.size() - 4); - entry.loadLooseData(basePath); + entry->loadLooseData(basePath); } return ret; } -void AudioGroupSampleDirectory::_extractWAV(SampleId id, const Entry& ent, +void AudioGroupSampleDirectory::_extractWAV(SampleId id, const EntryData& ent, amuse::SystemStringView destDir, const unsigned char* samp) { amuse::SystemString path(destDir); @@ -389,16 +403,16 @@ void AudioGroupSampleDirectory::extractWAV(SampleId id, amuse::SystemStringView auto search = m_entries.find(id); if (search == m_entries.cend()) return; - _extractWAV(id, search->second, destDir, samp); + _extractWAV(id, *search->second->m_data, destDir, samp); } void AudioGroupSampleDirectory::extractAllWAV(amuse::SystemStringView destDir, const unsigned char* samp) const { for (const auto& ent : m_entries) - _extractWAV(ent.first, ent.second, destDir, samp); + _extractWAV(ent.first, *ent.second->m_data, destDir, samp); } -void AudioGroupSampleDirectory::_extractCompressed(SampleId id, const Entry& ent, +void AudioGroupSampleDirectory::_extractCompressed(SampleId id, const EntryData& ent, amuse::SystemStringView destDir, const unsigned char* samp) { SampleFormat fmt = SampleFormat(ent.m_numSamples >> 24); @@ -475,13 +489,46 @@ void AudioGroupSampleDirectory::extractCompressed(SampleId id, amuse::SystemStri auto search = m_entries.find(id); if (search == m_entries.cend()) return; - _extractCompressed(id, search->second, destDir, samp); + _extractCompressed(id, *search->second->m_data, destDir, samp); } void AudioGroupSampleDirectory::extractAllCompressed(amuse::SystemStringView destDir, const unsigned char* samp) const { for (const auto& ent : m_entries) - _extractCompressed(ent.first, ent.second, destDir, samp); + _extractCompressed(ent.first, *ent.second->m_data, destDir, samp); +} + +void AudioGroupSampleDirectory::reloadSampleData(SystemStringView groupPath) +{ + DirectoryEnumerator de(groupPath, DirectoryEnumerator::Mode::FilesSorted); + for (const DirectoryEnumerator::Entry& ent : de) + { + if (ent.m_name.size() < 4) + continue; + SystemString baseName; + if (!CompareCaseInsensitive(ent.m_name.data() + ent.m_name.size() - 4, _S(".dsp")) || + !CompareCaseInsensitive(ent.m_name.data() + ent.m_name.size() - 4, _S(".wav"))) + baseName = SystemString(ent.m_name.begin(), ent.m_name.begin() + ent.m_name.size() - 4); + else + continue; + +#ifdef _WIN32 + std::string baseNameStd = athena::utility::wideToUtf8(baseName); +#else + std::string& baseNameStd = baseName; +#endif + + if (SampleId::CurNameDB->m_stringToId.find(baseNameStd) == SampleId::CurNameDB->m_stringToId.end()) + { + ObjectId sampleId = SampleId::CurNameDB->generateId(NameDB::Type::Sample); + SampleId::CurNameDB->registerPair(baseNameStd, sampleId); + + auto& entry = m_entries[sampleId]; + entry = MakeObj(); + SystemString basePath = SystemString(ent.m_path.begin(), ent.m_path.begin() + ent.m_path.size() - 4); + entry->loadLooseData(basePath); + } + } } } diff --git a/lib/BooBackend.cpp b/lib/BooBackend.cpp index 4f816a8..17ae5ff 100644 --- a/lib/BooBackend.cpp +++ b/lib/BooBackend.cpp @@ -174,7 +174,7 @@ void BooBackendMIDIReader::pumpReader(double dt) void BooBackendMIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity) { - for (std::shared_ptr& seq : m_engine.getActiveSequencers()) + for (ObjToken& seq : m_engine.getActiveSequencers()) seq->keyOff(chan, key, velocity); #if 0 openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON); @@ -185,7 +185,7 @@ void BooBackendMIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity) void BooBackendMIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity) { - for (std::shared_ptr& seq : m_engine.getActiveSequencers()) + for (ObjToken& seq : m_engine.getActiveSequencers()) seq->keyOn(chan, key, velocity); #if 0 openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON); @@ -198,13 +198,13 @@ void BooBackendMIDIReader::notePressure(uint8_t /*chan*/, uint8_t /*key*/, uint8 void BooBackendMIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value) { - for (std::shared_ptr& seq : m_engine.getActiveSequencers()) + for (ObjToken& seq : m_engine.getActiveSequencers()) seq->setCtrlValue(chan, control, value); } void BooBackendMIDIReader::programChange(uint8_t chan, uint8_t program) { - for (std::shared_ptr& seq : m_engine.getActiveSequencers()) + for (ObjToken& seq : m_engine.getActiveSequencers()) seq->setChanProgram(chan, program); } @@ -212,13 +212,13 @@ void BooBackendMIDIReader::channelPressure(uint8_t /*chan*/, uint8_t /*pressure* void BooBackendMIDIReader::pitchBend(uint8_t chan, int16_t pitch) { - for (std::shared_ptr& seq : m_engine.getActiveSequencers()) + for (ObjToken& seq : m_engine.getActiveSequencers()) seq->setPitchWheel(chan, (pitch - 0x2000) / float(0x2000)); } void BooBackendMIDIReader::allSoundOff(uint8_t chan) { - for (std::shared_ptr& seq : m_engine.getActiveSequencers()) + for (ObjToken& seq : m_engine.getActiveSequencers()) seq->allOff(chan, true); } @@ -228,7 +228,7 @@ void BooBackendMIDIReader::localControl(uint8_t /*chan*/, bool /*on*/) {} void BooBackendMIDIReader::allNotesOff(uint8_t chan) { - for (std::shared_ptr& seq : m_engine.getActiveSequencers()) + for (ObjToken& seq : m_engine.getActiveSequencers()) seq->allOff(chan, false); } diff --git a/lib/Emitter.cpp b/lib/Emitter.cpp index b4c74d2..f714acd 100644 --- a/lib/Emitter.cpp +++ b/lib/Emitter.cpp @@ -15,7 +15,7 @@ static void Delta(Vector3f& out, const Vector3f& a, const Vector3f& b) Emitter::~Emitter() {} -Emitter::Emitter(Engine& engine, const AudioGroup& group, const std::shared_ptr& vox, +Emitter::Emitter(Engine& engine, const AudioGroup& group, ObjToken vox, float maxDist, float minVol, float falloff, bool doppler) : Entity(engine, group, vox->getGroupId(), vox->getObjectId()), m_vox(vox), m_maxDist(maxDist), m_minVol(clamp(0.f, minVol, 1.f)), m_falloff(clamp(-1.f, falloff, 1.f)), m_doppler(doppler) diff --git a/lib/Engine.cpp b/lib/Engine.cpp index 86d36aa..c2f3173 100644 --- a/lib/Engine.cpp +++ b/lib/Engine.cpp @@ -16,12 +16,12 @@ static const float FullLevels[8] = {1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f, 1.f}; Engine::~Engine() { m_backend.setCallbackInterface(nullptr); - for (std::shared_ptr& seq : m_activeSequencers) + for (ObjToken& seq : m_activeSequencers) if (!seq->m_destroyed) seq->_destroy(); - for (std::shared_ptr& emitter : m_activeEmitters) + for (ObjToken& emitter : m_activeEmitters) emitter->_destroy(); - for (std::shared_ptr& vox : m_activeVoices) + for (ObjToken& vox : m_activeVoices) vox->_destroy(); } @@ -57,51 +57,49 @@ std::pair Engine::_findSFXGroup(int groupId) return {}; } -std::list>::iterator Engine::_allocateVoice(const AudioGroup& group, int groupId, - double sampleRate, bool dynamicPitch, bool emitter, - std::weak_ptr studio) +std::list>::iterator Engine::_allocateVoice(const AudioGroup& group, int groupId, + double sampleRate, bool dynamicPitch, bool emitter, + ObjToken studio) { - std::shared_ptr st = studio.lock(); auto it = - m_activeVoices.emplace(m_activeVoices.end(), new Voice(*this, group, groupId, m_nextVid++, emitter, studio)); + m_activeVoices.emplace(m_activeVoices.end(), MakeObj(*this, group, groupId, m_nextVid++, emitter, studio)); m_activeVoices.back()->m_backendVoice = m_backend.allocateVoice(*m_activeVoices.back(), sampleRate, dynamicPitch); - m_activeVoices.back()->m_backendVoice->setChannelLevels(st->getMaster().m_backendSubmix.get(), FullLevels, false); - m_activeVoices.back()->m_backendVoice->setChannelLevels(st->getAuxA().m_backendSubmix.get(), FullLevels, false); - m_activeVoices.back()->m_backendVoice->setChannelLevels(st->getAuxB().m_backendSubmix.get(), FullLevels, false); + m_activeVoices.back()->m_backendVoice->setChannelLevels(studio->getMaster().m_backendSubmix.get(), FullLevels, false); + m_activeVoices.back()->m_backendVoice->setChannelLevels(studio->getAuxA().m_backendSubmix.get(), FullLevels, false); + m_activeVoices.back()->m_backendVoice->setChannelLevels(studio->getAuxB().m_backendSubmix.get(), FullLevels, false); return it; } -std::list>::iterator Engine::_allocateSequencer(const AudioGroup& group, int groupId, - int setupId, std::weak_ptr studio) +std::list>::iterator Engine::_allocateSequencer(const AudioGroup& group, int groupId, + int setupId, ObjToken studio) { const SongGroupIndex* songGroup = group.getProj().getSongGroupIndex(groupId); if (songGroup) { auto it = m_activeSequencers.emplace(m_activeSequencers.end(), - new Sequencer(*this, group, groupId, songGroup, setupId, studio)); + MakeObj(*this, group, groupId, songGroup, setupId, studio)); return it; } const SFXGroupIndex* sfxGroup = group.getProj().getSFXGroupIndex(groupId); if (sfxGroup) { auto it = m_activeSequencers.emplace(m_activeSequencers.end(), - new Sequencer(*this, group, groupId, sfxGroup, studio)); + MakeObj(*this, group, groupId, sfxGroup, studio)); return it; } return {}; } -std::shared_ptr Engine::_allocateStudio(bool mainOut) +ObjToken Engine::_allocateStudio(bool mainOut) { - std::shared_ptr ret = std::make_shared(*this, mainOut); - m_activeStudios.emplace(m_activeStudios.end(), ret); + ObjToken ret = MakeObj(*this, mainOut); ret->m_master.m_backendSubmix = m_backend.allocateSubmix(ret->m_master, mainOut, 0); ret->m_auxA.m_backendSubmix = m_backend.allocateSubmix(ret->m_auxA, mainOut, 1); ret->m_auxB.m_backendSubmix = m_backend.allocateSubmix(ret->m_auxB, mainOut, 2); return ret; } -std::list>::iterator Engine::_destroyVoice(std::list>::iterator it) +std::list>::iterator Engine::_destroyVoice(std::list>::iterator it) { assert(this == &(*it)->getEngine()); if ((*it)->m_destroyed) @@ -110,8 +108,8 @@ std::list>::iterator Engine::_destroyVoice(std::list>::iterator -Engine::_destroySequencer(std::list>::iterator it) +std::list>::iterator +Engine::_destroySequencer(std::list>::iterator it) { assert(this == &(*it)->getEngine()); if ((*it)->m_destroyed) @@ -157,15 +155,6 @@ void Engine::_bringOutYourDead() } ++it; } - - for (auto it = m_activeStudios.begin(); it != m_activeStudios.end();) - { - std::shared_ptr st = it->lock(); - if (!st) - it = m_activeStudios.erase(it); - else - ++it; - } } void Engine::_on5MsInterval(IBackendVoiceAllocator& engine, double dt) @@ -173,11 +162,11 @@ void Engine::_on5MsInterval(IBackendVoiceAllocator& engine, double dt) m_channelSet = engine.getAvailableSet(); if (m_midiReader) m_midiReader->pumpReader(dt); - for (std::shared_ptr& seq : m_activeSequencers) + for (ObjToken& seq : m_activeSequencers) seq->advance(dt); - for (std::shared_ptr& emitter : m_activeEmitters) + for (ObjToken& emitter : m_activeEmitters) emitter->_update(); - for (std::shared_ptr& listener : m_activeListeners) + for (ObjToken& listener : m_activeListeners) listener->m_dirty = false; } @@ -187,7 +176,7 @@ void Engine::_onPumpCycleComplete(IBackendVoiceAllocator& engine) /* Determine lowest available free vid */ int maxVid = -1; - for (std::shared_ptr& vox : m_activeVoices) + for (ObjToken& vox : m_activeVoices) maxVid = std::max(maxVid, vox->maxVid()); m_nextVid = maxVid + 1; } @@ -273,10 +262,10 @@ void Engine::removeAudioGroup(const AudioGroupData& data) } /** Create new Studio within engine */ -std::shared_ptr Engine::addStudio(bool mainOut) { return _allocateStudio(mainOut); } +ObjToken Engine::addStudio(bool mainOut) { return _allocateStudio(mainOut); } /** Start soundFX playing from loaded audio groups */ -std::shared_ptr Engine::fxStart(int sfxId, float vol, float pan, std::weak_ptr smx) +ObjToken Engine::fxStart(int sfxId, float vol, float pan, ObjToken smx) { auto search = m_sfxLookup.find(sfxId); if (search == m_sfxLookup.end()) @@ -287,7 +276,7 @@ std::shared_ptr Engine::fxStart(int sfxId, float vol, float pan, std::wea if (!grp) return {}; - std::list>::iterator ret = + std::list>::iterator ret = _allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, false, smx); if (!(*ret)->loadMacroObject(entry->macro.id, 0, 1000.f, entry->defKey, entry->defVel, 0)) @@ -302,13 +291,13 @@ std::shared_ptr Engine::fxStart(int sfxId, float vol, float pan, std::wea } /** Start SoundMacro node playing directly (for editor use) */ -std::shared_ptr Engine::macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel, - uint8_t mod, std::weak_ptr smx) +ObjToken Engine::macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel, + uint8_t mod, ObjToken smx) { if (!group) return {}; - std::list>::iterator ret = + std::list>::iterator ret = _allocateVoice(*group, {}, NativeSampleRate, true, false, smx); if (!(*ret)->loadMacroObject(id, 0, 1000.f, key, vel, mod)) @@ -321,9 +310,8 @@ std::shared_ptr Engine::macroStart(const AudioGroup* group, SoundMacroId } /** Start soundFX playing from loaded audio groups, attach to positional emitter */ -std::shared_ptr Engine::addEmitter(const float* pos, const float* dir, float maxDist, float falloff, - int sfxId, float minVol, float maxVol, bool doppler, - std::weak_ptr smx) +ObjToken Engine::addEmitter(const float* pos, const float* dir, float maxDist, float falloff, + int sfxId, float minVol, float maxVol, bool doppler, ObjToken smx) { auto search = m_sfxLookup.find(sfxId); if (search == m_sfxLookup.end()) @@ -334,7 +322,7 @@ std::shared_ptr Engine::addEmitter(const float* pos, const float* dir, if (!grp) return {}; - std::list>::iterator vox = + std::list>::iterator vox = _allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, true, smx); if (!(*vox)->loadMacroObject(entry->macro, 0, 1000.f, entry->defKey, entry->defVel, 0)) @@ -344,7 +332,7 @@ std::shared_ptr Engine::addEmitter(const float* pos, const float* dir, } auto emitIt = m_activeEmitters.emplace(m_activeEmitters.end(), - new Emitter(*this, *grp, *vox, maxDist, minVol, falloff, doppler)); + MakeObj(*this, *grp, *vox, maxDist, minVol, falloff, doppler)); Emitter& ret = *(*emitIt); ret.getVoice()->setPan(entry->panning); @@ -355,11 +343,11 @@ std::shared_ptr Engine::addEmitter(const float* pos, const float* dir, } /** Build listener and add to engine's listener list */ -std::shared_ptr Engine::addListener(const float* pos, const float* dir, const float* heading, const float* up, - float frontDiff, float backDiff, float soundSpeed, float volume) +ObjToken Engine::addListener(const float* pos, const float* dir, const float* heading, const float* up, + float frontDiff, float backDiff, float soundSpeed, float volume) { auto listenerIt = m_activeListeners.emplace(m_activeListeners.end(), - new Listener(volume, frontDiff, backDiff, soundSpeed)); + MakeObj(volume, frontDiff, backDiff, soundSpeed)); Listener& ret = *(*listenerIt); ret.setVectors(pos, dir, heading, up); return *listenerIt; @@ -379,13 +367,12 @@ void Engine::removeListener(Listener* listener) } /** Start song playing from loaded audio groups */ -std::shared_ptr Engine::seqPlay(int groupId, int songId, const unsigned char* arrData, - std::weak_ptr smx) +ObjToken Engine::seqPlay(int groupId, int songId, const unsigned char* arrData, ObjToken smx) { std::pair songGrp = _findSongGroup(groupId); if (songGrp.second) { - std::list>::iterator ret = _allocateSequencer(*songGrp.first, groupId, songId, smx); + std::list>::iterator ret = _allocateSequencer(*songGrp.first, groupId, songId, smx); if (!*ret) return {}; @@ -397,7 +384,7 @@ std::shared_ptr Engine::seqPlay(int groupId, int songId, const unsign std::pair sfxGrp = _findSFXGroup(groupId); if (sfxGrp.second) { - std::list>::iterator ret = _allocateSequencer(*sfxGrp.first, groupId, songId, smx); + std::list>::iterator ret = _allocateSequencer(*sfxGrp.first, groupId, songId, smx); if (!*ret) return {}; return *ret; @@ -413,18 +400,18 @@ void Engine::setVolume(float vol) } /** Find voice from VoiceId */ -std::shared_ptr Engine::findVoice(int vid) +ObjToken Engine::findVoice(int vid) { - for (std::shared_ptr& vox : m_activeVoices) + for (ObjToken& vox : m_activeVoices) { - std::shared_ptr ret = vox->_findVoice(vid, vox); + ObjToken ret = vox->_findVoice(vid, vox); if (ret) return ret; } - for (std::shared_ptr& seq : m_activeSequencers) + for (ObjToken& seq : m_activeSequencers) { - std::shared_ptr ret = seq->findVoice(vid); + ObjToken ret = seq->findVoice(vid); if (ret) return ret; } @@ -450,7 +437,7 @@ void Engine::killKeygroup(uint8_t kg, bool now) ++it; } - for (std::shared_ptr& seq : m_activeSequencers) + for (ObjToken& seq : m_activeSequencers) seq->killKeygroup(kg, now); } @@ -464,7 +451,7 @@ void Engine::sendMacroMessage(ObjectId macroId, int32_t val) vox->message(val); } - for (std::shared_ptr& seq : m_activeSequencers) + for (ObjToken& seq : m_activeSequencers) seq->sendMacroMessage(macroId, val); } } diff --git a/lib/Sequencer.cpp b/lib/Sequencer.cpp index 046be9e..f8402d9 100644 --- a/lib/Sequencer.cpp +++ b/lib/Sequencer.cpp @@ -58,7 +58,7 @@ Sequencer::~Sequencer() } Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SongGroupIndex* songGroup, int setupId, - std::weak_ptr studio) + ObjToken studio) : Entity(engine, group, groupId), m_songGroup(songGroup), m_studio(studio) { auto it = m_songGroup->m_midiSetups.find(setupId); @@ -67,7 +67,7 @@ Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const } Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SFXGroupIndex* sfxGroup, - std::weak_ptr studio) + ObjToken studio) : Entity(engine, group, groupId), m_sfxGroup(sfxGroup), m_studio(studio) { std::map sortSFX; @@ -210,13 +210,16 @@ size_t Sequencer::getVoiceCount() const return ret; } -std::shared_ptr Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velocity) +ObjToken Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velocity) { if (m_parent->m_songGroup && !m_page) return {}; + if (m_lastVoice->isDestroyed()) + m_lastVoice.reset(); + /* If portamento is enabled for voice, pre-empt spawning new voices */ - if (std::shared_ptr lastVoice = m_lastVoice.lock()) + if (ObjToken lastVoice = m_lastVoice) { uint8_t lastNote = lastVoice->getLastNote(); if (lastVoice->doPortamento(note)) @@ -231,7 +234,7 @@ std::shared_ptr Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo auto keySearch = m_chanVoxs.find(note); if (keySearch != m_chanVoxs.cend()) { - if (keySearch->second == m_lastVoice.lock()) + if (keySearch->second == m_lastVoice) m_lastVoice.reset(); keySearch->second->keyOff(); keySearch->second->setPedal(false); @@ -239,7 +242,7 @@ std::shared_ptr Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo m_chanVoxs.erase(keySearch); } - std::list>::iterator ret = m_parent->m_engine._allocateVoice( + std::list>::iterator ret = m_parent->m_engine._allocateVoice( m_parent->m_audioGroup, m_parent->m_groupId, NativeSampleRate, true, false, m_parent->m_studio); if (*ret) { @@ -284,7 +287,7 @@ std::shared_ptr Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo return *ret; } -std::shared_ptr Sequencer::keyOn(uint8_t chan, uint8_t note, uint8_t velocity) +ObjToken Sequencer::keyOn(uint8_t chan, uint8_t note, uint8_t velocity) { if (chan > 15) return {}; @@ -301,7 +304,7 @@ void Sequencer::ChannelState::keyOff(uint8_t note, uint8_t velocity) if (keySearch == m_chanVoxs.cend()) return; - if (keySearch->second == m_lastVoice.lock()) + if (m_lastVoice->isDestroyed() || keySearch->second == m_lastVoice) m_lastVoice.reset(); keySearch->second->keyOff(); m_keyoffVoxs.emplace(std::move(keySearch->second)); @@ -425,9 +428,11 @@ void Sequencer::setTempo(double ticksPerSec) { m_ticksPerSec = ticksPerSec; } void Sequencer::ChannelState::allOff() { + if (m_lastVoice->isDestroyed()) + m_lastVoice.reset(); for (auto it = m_chanVoxs.begin(); it != m_chanVoxs.end();) { - if (it->second == m_lastVoice.lock()) + if (it->second == m_lastVoice) m_lastVoice.reset(); it->second->keyOff(); m_keyoffVoxs.emplace(std::move(it->second)); @@ -476,12 +481,15 @@ void Sequencer::allOff(uint8_t chan, bool now) void Sequencer::ChannelState::killKeygroup(uint8_t kg, bool now) { + if (m_lastVoice->isDestroyed()) + m_lastVoice.reset(); + for (auto it = m_chanVoxs.begin(); it != m_chanVoxs.end();) { Voice* vox = it->second.get(); if (vox->m_keygroup == kg) { - if (it->second == m_lastVoice.lock()) + if (it->second == m_lastVoice) m_lastVoice.reset(); if (now) { @@ -518,7 +526,7 @@ void Sequencer::killKeygroup(uint8_t kg, bool now) chan.killKeygroup(kg, now); } -std::shared_ptr Sequencer::ChannelState::findVoice(int vid) +ObjToken Sequencer::ChannelState::findVoice(int vid) { for (const auto& vox : m_chanVoxs) if (vox.second->vid() == vid) @@ -529,13 +537,13 @@ std::shared_ptr Sequencer::ChannelState::findVoice(int vid) return {}; } -std::shared_ptr Sequencer::findVoice(int vid) +ObjToken Sequencer::findVoice(int vid) { for (auto& chan : m_chanStates) { if (chan) { - std::shared_ptr ret = chan.findVoice(vid); + ObjToken ret = chan.findVoice(vid); if (ret) return ret; } diff --git a/lib/SoundMacroState.cpp b/lib/SoundMacroState.cpp index abdf7a4..9fa56be 100644 --- a/lib/SoundMacroState.cpp +++ b/lib/SoundMacroState.cpp @@ -469,7 +469,7 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdWaitMs::Introspective = }, { FIELD_HEAD(SoundMacro::CmdWaitMs, ms), - "Ticks/Millisec"sv, + "Millisec"sv, 0, 65535, 96 } } @@ -541,7 +541,7 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdPlayMacro::Introspective = }; bool SoundMacro::CmdPlayMacro::Do(SoundMacroState& st, Voice& vox) const { - std::shared_ptr sibVox = vox.startChildMacro(addNote, macro.id, macroStep.step); + ObjToken sibVox = vox.startChildMacro(addNote, macro.id, macroStep.step); if (sibVox) st.m_lastPlayMacroVid = sibVox->vid(); @@ -572,14 +572,14 @@ bool SoundMacro::CmdSendKeyOff::Do(SoundMacroState& st, Voice& vox) const { if (st.m_lastPlayMacroVid != -1) { - std::shared_ptr otherVox = vox.getEngine().findVoice(st.m_lastPlayMacroVid); + ObjToken otherVox = vox.getEngine().findVoice(st.m_lastPlayMacroVid); if (otherVox) otherVox->keyOff(); } } else { - std::shared_ptr otherVox = vox.getEngine().findVoice(st.m_variables[variable & 0x1f]); + ObjToken otherVox = vox.getEngine().findVoice(st.m_variables[variable & 0x1f]); if (otherVox) otherVox->keyOff(); } @@ -1361,12 +1361,12 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdPitchSweep1::Introspective = { { FIELD_HEAD(SoundMacro::CmdPitchSweep1, times), - "Level Note"sv, + "Times"sv, 0, 127, 100, }, { FIELD_HEAD(SoundMacro::CmdPitchSweep1, add), - "Level Fine"sv, + "Add"sv, -32768, 32767, 100, }, { @@ -1406,12 +1406,12 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdPitchSweep2::Introspective = { { FIELD_HEAD(SoundMacro::CmdPitchSweep2, times), - "Level Note"sv, + "Times"sv, 0, 127, 100, }, { FIELD_HEAD(SoundMacro::CmdPitchSweep2, add), - "Level Fine"sv, + "Add"sv, -32768, 32767, 100, }, { @@ -1744,7 +1744,7 @@ bool SoundMacro::CmdSendMessage::Do(SoundMacroState& st, Voice& vox) const { if (isVar) { - std::shared_ptr findVox = vox.getEngine().findVoice(st.m_variables[voiceVar & 0x1f]); + ObjToken findVox = vox.getEngine().findVoice(st.m_variables[voiceVar & 0x1f]); if (findVox) findVox->message(st.m_variables[valueVar & 0x1f]); } diff --git a/lib/Studio.cpp b/lib/Studio.cpp index eba881b..30e70d6 100644 --- a/lib/Studio.cpp +++ b/lib/Studio.cpp @@ -23,7 +23,7 @@ Studio::Studio(Engine& engine, bool mainOut) : m_engine(engine), m_master(engine addStudioSend(engine.getDefaultStudio(), 1.f, 1.f, 1.f); } -void Studio::addStudioSend(std::weak_ptr studio, float dry, float auxA, float auxB) +void Studio::addStudioSend(ObjToken studio, float dry, float auxA, float auxB) { m_studiosOut.emplace_back(studio, dry, auxA, auxB); diff --git a/lib/Voice.cpp b/lib/Voice.cpp index ddd4f8d..cfa91f9 100644 --- a/lib/Voice.cpp +++ b/lib/Voice.cpp @@ -17,8 +17,12 @@ void Voice::_destroy() { Entity::_destroy(); - for (std::shared_ptr& vox : m_childVoices) + for (auto& vox : m_childVoices) vox->_destroy(); + + m_studio.reset(); + m_backendVoice.reset(); + m_curSample.reset(); } Voice::~Voice() @@ -26,14 +30,14 @@ Voice::~Voice() // fprintf(stderr, "DEALLOC %d\n", m_vid); } -Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, std::weak_ptr studio) +Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, ObjToken studio) : Entity(engine, group, groupId), m_vid(vid), m_emitter(emitter), m_studio(studio) { // fprintf(stderr, "ALLOC %d\n", m_vid); } Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter, - std::weak_ptr studio) + ObjToken studio) : Entity(engine, group, groupId, oid), m_vid(vid), m_emitter(emitter), m_studio(studio) { // fprintf(stderr, "ALLOC %d\n", m_vid); @@ -115,7 +119,7 @@ void Voice::_doKeyOff() void Voice::_setTotalPitch(int32_t cents, bool slew) { // fprintf(stderr, "PITCH %d %d \n", cents, slew); - int32_t interval = cents - m_curSample->m_pitch * 100; + int32_t interval = clamp(0, cents, 12700) - m_curSample->m_pitch * 100; double ratio = std::exp2(interval / 1200.0) * m_dopplerRatio; m_sampleRate = m_curSample->m_sampleRate * ratio; m_backendVoice->setPitchRatio(ratio, slew); @@ -125,7 +129,7 @@ bool Voice::_isRecursivelyDead() { if (m_voxState != VoiceState::Dead) return false; - for (std::shared_ptr& vox : m_childVoices) + for (auto& vox : m_childVoices) if (!vox->_isRecursivelyDead()) return false; return true; @@ -146,14 +150,14 @@ void Voice::_bringOutYourDead() } } -std::shared_ptr Voice::_findVoice(int vid, std::weak_ptr thisPtr) +ObjToken Voice::_findVoice(int vid, ObjToken thisPtr) { if (m_vid == vid) - return thisPtr.lock(); + return thisPtr; - for (std::shared_ptr& vox : m_childVoices) + for (ObjToken& vox : m_childVoices) { - std::shared_ptr ret = vox->_findVoice(vid, vox); + ObjToken ret = vox->_findVoice(vid, vox); if (ret) return ret; } @@ -170,16 +174,16 @@ std::unique_ptr& Voice::_ensureCtrlVals() return m_ctrlValsSelf; } -std::list>::iterator Voice::_allocateVoice(double sampleRate, bool dynamicPitch) +std::list>::iterator Voice::_allocateVoice(double sampleRate, bool dynamicPitch) { auto it = m_childVoices.emplace( - m_childVoices.end(), new Voice(m_engine, m_audioGroup, m_groupId, m_engine.m_nextVid++, m_emitter, m_studio)); + m_childVoices.end(), MakeObj(m_engine, m_audioGroup, m_groupId, m_engine.m_nextVid++, m_emitter, m_studio)); m_childVoices.back()->m_backendVoice = m_engine.getBackend().allocateVoice(*m_childVoices.back(), sampleRate, dynamicPitch); return it; } -std::list>::iterator Voice::_destroyVoice(std::list>::iterator it) +std::list>::iterator Voice::_destroyVoice(std::list>::iterator it) { if ((*it)->m_destroyed) return m_childVoices.begin(); @@ -657,7 +661,7 @@ size_t Voice::supplyAudio(size_t samples, int16_t* data) memset(data, 0, sizeof(int16_t) * samples); if (m_voxState == VoiceState::Dead) - m_curSample = nullptr; + m_curSample.reset(); return samples; } @@ -749,27 +753,27 @@ void Voice::routeAudio(size_t frames, double dt, int busId, float* in, float* ou int Voice::maxVid() const { int maxVid = m_vid; - for (const std::shared_ptr& vox : m_childVoices) + for (const ObjToken& vox : m_childVoices) maxVid = std::max(maxVid, vox->maxVid()); return maxVid; } -std::shared_ptr Voice::_startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, - uint8_t midiVel, uint8_t midiMod, bool pushPc) +ObjToken Voice::_startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, + uint8_t midiVel, uint8_t midiMod, bool pushPc) { - std::list>::iterator vox = _allocateVoice(NativeSampleRate, true); + std::list>::iterator vox = _allocateVoice(NativeSampleRate, true); if (!(*vox)->loadMacroObject(macroId, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc)) { _destroyVoice(vox); return {}; } (*vox)->setVolume(m_targetUserVol); - (*vox)->setPan(m_userPan); - (*vox)->setSurroundPan(m_userSpan); + (*vox)->setPan(m_curPan); + (*vox)->setSurroundPan(m_curSpan); return *vox; } -std::shared_ptr Voice::startChildMacro(int8_t addNote, ObjectId macroId, int macroStep) +ObjToken Voice::startChildMacro(int8_t addNote, ObjectId macroId, int macroStep) { return _startChildMacro(macroId, macroStep, 1000.0, m_state.m_initKey + addNote, m_state.m_initVel, m_state.m_initMod); @@ -824,7 +828,7 @@ bool Voice::_loadLayer(const std::vector& layer, double ticksPerSe } else { - std::shared_ptr vox = + ObjToken vox = _startChildMacro(mapping.macro.id, 0, ticksPerSec, mappingKey, midiVel, midiMod, pushPc); if (vox) { @@ -904,7 +908,7 @@ void Voice::keyOff() else if (!m_curSample || m_curSample->m_loopLengthSamples) _macroKeyOff(); - for (const std::shared_ptr& vox : m_childVoices) + for (const ObjToken& vox : m_childVoices) vox->keyOff(); } @@ -930,10 +934,9 @@ void Voice::startSample(SampleId sampId, int32_t offset) if (m_destroyed) return; - m_curSample = m_audioGroup.getSample(sampId); - if (m_curSample) + if (const SampleEntry* sample = m_audioGroup.getSample(sampId)) { - m_curSampleData = m_audioGroup.getSampleData(sampId, m_curSample); + std::tie(m_curSample, m_curSampleData) = m_audioGroup.getSampleData(sampId, sample); m_sampleRate = m_curSample->m_sampleRate; m_curPitch = m_curSample->m_pitch; @@ -986,7 +989,7 @@ void Voice::startSample(SampleId sampId, int32_t offset) } } -void Voice::stopSample() { m_curSample = nullptr; } +void Voice::stopSample() { m_curSample.reset(); } void Voice::setVolume(float vol) { @@ -994,7 +997,7 @@ void Voice::setVolume(float vol) return; m_targetUserVol = clamp(0.f, vol, 1.f); - for (std::shared_ptr& vox : m_childVoices) + for (ObjToken& vox : m_childVoices) vox->setVolume(vol); } @@ -1116,8 +1119,8 @@ void Voice::_setPan(float pan) return; m_curPan = clamp(-1.f, pan, 1.f); - float totalPan = clamp(-1.f, m_curPan + m_userPan, 1.f); - float totalSpan = clamp(-1.f, m_curSpan + m_userSpan, 1.f); + float totalPan = clamp(-1.f, m_curPan, 1.f); + float totalSpan = clamp(-1.f, m_curSpan, 1.f); float coefs[8] = {}; _panLaw(coefs, totalPan, totalPan, totalSpan); _setChannelCoefs(coefs); @@ -1128,9 +1131,8 @@ void Voice::setPan(float pan) if (m_destroyed) return; - m_userPan = pan; - _setPan(m_curPan); - for (std::shared_ptr& vox : m_childVoices) + _setPan(pan); + for (ObjToken& vox : m_childVoices) vox->setPan(pan); } @@ -1145,9 +1147,8 @@ void Voice::setSurroundPan(float span) if (m_destroyed) return; - m_userSpan = span; - _setSurroundPan(m_curSpan); - for (std::shared_ptr& vox : m_childVoices) + _setSurroundPan(span); + for (ObjToken& vox : m_childVoices) vox->setSurroundPan(span); } @@ -1164,7 +1165,7 @@ void Voice::setChannelCoefs(const float coefs[8]) return; _setChannelCoefs(coefs); - for (std::shared_ptr& vox : m_childVoices) + for (ObjToken& vox : m_childVoices) vox->setChannelCoefs(coefs); } @@ -1220,7 +1221,7 @@ void Voice::setPedal(bool pedal) } m_sustained = pedal; - for (std::shared_ptr& vox : m_childVoices) + for (ObjToken& vox : m_childVoices) vox->setPedal(pedal); } @@ -1264,7 +1265,7 @@ void Voice::setReverbVol(float rvol) return; m_curReverbVol = clamp(0.f, rvol, 1.f); - for (std::shared_ptr& vox : m_childVoices) + for (ObjToken& vox : m_childVoices) vox->setReverbVol(rvol); } @@ -1274,7 +1275,7 @@ void Voice::setAuxBVol(float bvol) return; m_curAuxBVol = clamp(0.f, bvol, 1.f); - for (std::shared_ptr& vox : m_childVoices) + for (ObjToken& vox : m_childVoices) vox->setAuxBVol(bvol); } @@ -1351,7 +1352,7 @@ void Voice::setPitchWheel(float pitchWheel) m_curPitchWheel = amuse::clamp(-1.f, pitchWheel, 1.f); _setPitchWheel(m_curPitchWheel); - for (std::shared_ptr& vox : m_childVoices) + for (ObjToken& vox : m_childVoices) vox->setPitchWheel(pitchWheel); } @@ -1371,7 +1372,7 @@ void Voice::setAftertouch(uint8_t aftertouch) return; m_curAftertouch = aftertouch; - for (std::shared_ptr& vox : m_childVoices) + for (ObjToken& vox : m_childVoices) vox->setAftertouch(aftertouch); } @@ -1424,14 +1425,14 @@ void Voice::_notifyCtrlChange(uint8_t ctrl, int8_t val) m_state.m_curMod = uint8_t(val); } - for (std::shared_ptr& vox : m_childVoices) + for (ObjToken& vox : m_childVoices) vox->_notifyCtrlChange(ctrl, val); } size_t Voice::getTotalVoices() const { size_t ret = 1; - for (const std::shared_ptr& vox : m_childVoices) + for (const ObjToken& vox : m_childVoices) ret += vox->getTotalVoices(); return ret; } @@ -1443,7 +1444,7 @@ void Voice::kill() m_voxState = VoiceState::Dead; m_backendVoice->stop(); - for (const std::shared_ptr& vox : m_childVoices) + for (const ObjToken& vox : m_childVoices) vox->kill(); } }