ObjToken refactor and Sample nodes

This commit is contained in:
Jack Andersen 2018-07-28 17:37:06 -10:00
parent f5984141fd
commit 16745c9bf8
38 changed files with 845 additions and 460 deletions

View File

@ -10,7 +10,7 @@ void MIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
if (keySearch == m_chanVoxs.cend()) if (keySearch == m_chanVoxs.cend())
return; return;
if (keySearch->second == m_lastVoice.lock()) if (m_lastVoice->isDestroyed() || keySearch->second == m_lastVoice)
m_lastVoice.reset(); m_lastVoice.reset();
keySearch->second->keyOff(); keySearch->second->keyOff();
m_keyoffVoxs.emplace(std::move(keySearch->second)); 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) 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 portamento is enabled for voice, pre-empt spawning new voices */
if (std::shared_ptr<amuse::Voice> lastVoice = m_lastVoice.lock()) if (amuse::ObjToken<amuse::Voice> lastVoice = m_lastVoice)
{ {
uint8_t lastNote = lastVoice->getLastNote(); uint8_t lastNote = lastVoice->getLastNote();
if (lastVoice->doPortamento(key)) 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); auto keySearch = m_chanVoxs.find(key);
if (keySearch != m_chanVoxs.cend()) if (keySearch != m_chanVoxs.cend())
{ {
if (keySearch->second == m_lastVoice.lock()) if (keySearch->second == m_lastVoice)
m_lastVoice.reset(); m_lastVoice.reset();
keySearch->second->keyOff(); keySearch->second->keyOff();
keySearch->second->setPedal(false); 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<ProjectModel::SoundMacroNode*>(node); ProjectModel::SoundMacroNode* cNode = static_cast<ProjectModel::SoundMacroNode*>(node);
amuse::AudioGroupDatabase* group = g_MainWindow->projectModel()->getGroupNode(node)->getAudioGroup(); amuse::AudioGroupDatabase* group = g_MainWindow->projectModel()->getGroupNode(node)->getAudioGroup();
std::shared_ptr<amuse::Voice>& vox = m_chanVoxs[key]; amuse::ObjToken<amuse::Voice>& vox = m_chanVoxs[key];
vox = m_engine.macroStart(group, cNode->id(), key, velocity, g_MainWindow->m_modulation); vox = m_engine.macroStart(group, cNode->id(), key, velocity, g_MainWindow->m_ctrlVals[1]);
vox->setPedal(g_MainWindow->m_sustain); vox->setPedal(g_MainWindow->m_ctrlVals[64] >= 0x40);
vox->setPitchWheel(g_MainWindow->m_pitch); vox->setPitchWheel(g_MainWindow->m_pitch);
vox->installCtrlValues(g_MainWindow->m_ctrlVals);
} }
} }

View File

@ -2,13 +2,14 @@
#define AMUSE_MIDI_READER_HPP #define AMUSE_MIDI_READER_HPP
#include "amuse/BooBackend.hpp" #include "amuse/BooBackend.hpp"
#include "amuse/Common.hpp"
#include <unordered_set> #include <unordered_set>
class MIDIReader : public amuse::BooBackendMIDIReader class MIDIReader : public amuse::BooBackendMIDIReader
{ {
std::unordered_map<uint8_t, std::shared_ptr<amuse::Voice>> m_chanVoxs; std::unordered_map<uint8_t, amuse::ObjToken<amuse::Voice>> m_chanVoxs;
std::unordered_set<std::shared_ptr<amuse::Voice>> m_keyoffVoxs; std::unordered_set<amuse::ObjToken<amuse::Voice>> m_keyoffVoxs;
std::weak_ptr<amuse::Voice> m_lastVoice; amuse::ObjToken<amuse::Voice> m_lastVoice;
public: public:
MIDIReader(amuse::Engine& engine, const char* name, bool useLock); MIDIReader(amuse::Engine& engine, const char* name, bool useLock);
boo::IMIDIIn* getMidiIn() const { return m_midiIn.get(); } boo::IMIDIIn* getMidiIn() const { return m_midiIn.get(); }

View File

@ -16,6 +16,7 @@
#include "CurveEditor.hpp" #include "CurveEditor.hpp"
#include "KeymapEditor.hpp" #include "KeymapEditor.hpp"
#include "LayersEditor.hpp" #include "LayersEditor.hpp"
#include "SampleEditor.hpp"
MainWindow::MainWindow(QWidget* parent) MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent), : QMainWindow(parent),
@ -50,7 +51,8 @@ MainWindow::MainWindow(QWidget* parent)
m_ui.actionSave_Project->setShortcut(QKeySequence::Save); m_ui.actionSave_Project->setShortcut(QKeySequence::Save);
connect(m_ui.actionSave_Project, SIGNAL(triggered()), this, SLOT(saveAction())); connect(m_ui.actionSave_Project, SIGNAL(triggered()), this, SLOT(saveAction()));
connect(m_ui.actionRevert_Project, SIGNAL(triggered()), this, SLOT(revertAction())); 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())); connect(m_ui.actionExport_GameCube_Groups, SIGNAL(triggered()), this, SLOT(exportAction()));
for (int i = 0; i < MaxRecentFiles; ++i) for (int i = 0; i < MaxRecentFiles; ++i)
@ -109,6 +111,8 @@ MainWindow::MainWindow(QWidget* parent)
m_ui.editorContents->addWidget(m_keymapEditor); m_ui.editorContents->addWidget(m_keymapEditor);
m_layersEditor = new LayersEditor; m_layersEditor = new LayersEditor;
m_ui.editorContents->addWidget(m_layersEditor); m_ui.editorContents->addWidget(m_layersEditor);
m_sampleEditor = new SampleEditor;
m_ui.editorContents->addWidget(m_sampleEditor);
m_ui.editorContents->setCurrentWidget(m_faceSvg); m_ui.editorContents->setCurrentWidget(m_faceSvg);
connect(m_ui.actionNew_Subproject, SIGNAL(triggered()), this, SLOT(newSubprojectAction())); connect(m_ui.actionNew_Subproject, SIGNAL(triggered()), this, SLOT(newSubprojectAction()));
@ -129,6 +133,9 @@ MainWindow::MainWindow(QWidget* parent)
m_voxAllocator = std::make_unique<VoiceAllocator>(*m_voxEngine); m_voxAllocator = std::make_unique<VoiceAllocator>(*m_voxEngine);
m_engine = std::make_unique<amuse::Engine>(*m_voxAllocator); m_engine = std::make_unique<amuse::Engine>(*m_voxAllocator);
m_ctrlVals[7] = 127;
m_ctrlVals[10] = 64;
startTimer(16); startTimer(16);
} }
@ -167,6 +174,27 @@ void MainWindow::connectMessenger(UIMessenger* messenger, Qt::ConnectionType typ
QMessageBox::StandardButton)), type); 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<ProjectModel::BasePoolObjectNode*>(
static_cast<EditorWidget*>(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() void MainWindow::updateRecentFileActions()
{ {
QSettings settings; QSettings settings;
@ -215,6 +243,7 @@ bool MainWindow::setProjectPath(const QString& path)
} }
testWriteFile.remove(); testWriteFile.remove();
closeEditor();
if (m_projectModel) if (m_projectModel)
m_projectModel->deleteLater(); m_projectModel->deleteLater();
m_projectModel = new ProjectModel(path, this); m_projectModel = new ProjectModel(path, this);
@ -224,9 +253,10 @@ bool MainWindow::setProjectPath(const QString& path)
this, SLOT(onOutlineSelectionChanged(const QItemSelection&, const QItemSelection&))); this, SLOT(onOutlineSelectionChanged(const QItemSelection&, const QItemSelection&)));
m_ui.actionSave_Project->setEnabled(true); m_ui.actionSave_Project->setEnabled(true);
m_ui.actionRevert_Project->setEnabled(true); m_ui.actionRevert_Project->setEnabled(true);
m_ui.actionReload_Sample_Data->setEnabled(true);
m_ui.actionExport_GameCube_Groups->setEnabled(true); m_ui.actionExport_GameCube_Groups->setEnabled(true);
setWindowFilePath(path); setWindowFilePath(path);
setWindowTitle(QString("Amuse [%1]").arg(dir.dirName())); updateWindowTitle();
onFocusChanged(nullptr, focusWidget()); onFocusChanged(nullptr, focusWidget());
m_undoStack->clear(); m_undoStack->clear();
@ -305,19 +335,19 @@ void MainWindow::timerEvent(QTimerEvent* ev)
void MainWindow::setSustain(bool sustain) void MainWindow::setSustain(bool sustain)
{ {
if (sustain && !m_sustain) if (sustain && m_ctrlVals[64] < 0x40)
{ {
m_ui.statusbar->setNormalMessage(tr("SUSTAIN")); m_ui.statusbar->setNormalMessage(tr("SUSTAIN"));
for (auto& v : m_engine->getActiveVoices()) for (auto& v : m_engine->getActiveVoices())
v->setPedal(true); 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({}); m_ui.statusbar->setNormalMessage({});
for (auto& v : m_engine->getActiveVoices()) for (auto& v : m_engine->getActiveVoices())
v->setPedal(false); 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->setCurrentWidget(editor);
m_ui.editorContents->update(); m_ui.editorContents->update();
updateWindowTitle();
return true; return true;
} }
@ -422,6 +453,11 @@ bool MainWindow::openEditor(ProjectModel::LayersNode* node)
return _setEditor(m_layersEditor->loadData(node) ? m_layersEditor : nullptr); 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) bool MainWindow::openEditor(ProjectModel::INode* node)
{ {
switch (node->type()) switch (node->type())
@ -440,6 +476,8 @@ bool MainWindow::openEditor(ProjectModel::INode* node)
return openEditor(static_cast<ProjectModel::KeymapNode*>(node)); return openEditor(static_cast<ProjectModel::KeymapNode*>(node));
case ProjectModel::INode::Type::Layer: case ProjectModel::INode::Type::Layer:
return openEditor(static_cast<ProjectModel::LayersNode*>(node)); return openEditor(static_cast<ProjectModel::LayersNode*>(node));
case ProjectModel::INode::Type::Sample:
return openEditor(static_cast<ProjectModel::SampleNode*>(node));
default: default:
return false; return false;
} }
@ -565,11 +603,43 @@ void MainWindow::saveAction()
void MainWindow::revertAction() void MainWindow::revertAction()
{ {
QString path = m_projectModel->path(); QString path = m_projectModel->path();
closeEditor();
m_undoStack->clear();
delete m_projectModel; delete m_projectModel;
m_projectModel = nullptr; m_projectModel = nullptr;
openProject(path); 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() void MainWindow::importAction()
{ {
QString path = QFileDialog::getOpenFileName(this, tr("Import Project")); 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(); amuse::AudioGroupDatabase* group = m_projectModel->getGroupNode(node)->getAudioGroup();
if (m_lastSound) if (m_lastSound)
m_lastSound->keyOff(); m_lastSound->keyOff();
m_lastSound = m_engine->macroStart(group, cNode->id(), key, m_velocity, m_modulation); m_lastSound = m_engine->macroStart(group, cNode->id(), key, m_velocity, m_ctrlVals[1]);
m_lastSound->setPedal(m_sustain); m_lastSound->setPedal(m_ctrlVals[64] >= 0x40);
m_lastSound->setPitchWheel(m_pitch); m_lastSound->setPitchWheel(m_pitch);
m_lastSound->installCtrlValues(m_ctrlVals);
} }
} }
} }
@ -818,9 +889,9 @@ void MainWindow::velocityChanged(int vel)
void MainWindow::modulationChanged(int mod) void MainWindow::modulationChanged(int mod)
{ {
m_modulation = mod; m_ctrlVals[1] = int8_t(mod);
for (auto& v : m_engine->getActiveVoices()) for (auto& v : m_engine->getActiveVoices())
v->setCtrlValue(1, int8_t(m_modulation)); v->setCtrlValue(1, m_ctrlVals[1]);
} }
void MainWindow::pitchChanged(int pitch) void MainWindow::pitchChanged(int pitch)

View File

@ -28,6 +28,7 @@ class ADSREditor;
class CurveEditor; class CurveEditor;
class KeymapEditor; class KeymapEditor;
class LayersEditor; class LayersEditor;
class SampleEditor;
class BackgroundTask : public QObject class BackgroundTask : public QObject
{ {
@ -85,15 +86,15 @@ class MainWindow : public QMainWindow
CurveEditor* m_curveEditor = nullptr; CurveEditor* m_curveEditor = nullptr;
KeymapEditor* m_keymapEditor = nullptr; KeymapEditor* m_keymapEditor = nullptr;
LayersEditor* m_layersEditor = nullptr; LayersEditor* m_layersEditor = nullptr;
SampleEditor* m_sampleEditor = nullptr;
std::unique_ptr<boo::IAudioVoiceEngine> m_voxEngine; std::unique_ptr<boo::IAudioVoiceEngine> m_voxEngine;
std::unique_ptr<VoiceAllocator> m_voxAllocator; std::unique_ptr<VoiceAllocator> m_voxAllocator;
std::unique_ptr<amuse::Engine> m_engine; std::unique_ptr<amuse::Engine> m_engine;
std::shared_ptr<amuse::Voice> m_lastSound; amuse::ObjToken<amuse::Voice> m_lastSound;
int m_velocity = 90; int m_velocity = 90;
int m_modulation = 0;
float m_pitch = 0.f; float m_pitch = 0.f;
bool m_sustain = false; int8_t m_ctrlVals[128] = {};
bool m_uiDisabled = false; bool m_uiDisabled = false;
QUndoStack* m_undoStack; QUndoStack* m_undoStack;
@ -110,6 +111,7 @@ class MainWindow : public QMainWindow
void connectMessenger(UIMessenger* messenger, Qt::ConnectionType type); void connectMessenger(UIMessenger* messenger, Qt::ConnectionType type);
void updateWindowTitle();
void updateRecentFileActions(); void updateRecentFileActions();
bool setProjectPath(const QString& path); bool setProjectPath(const QString& path);
void refreshAudioIO(); void refreshAudioIO();
@ -137,6 +139,7 @@ public:
bool openEditor(ProjectModel::CurveNode* node); bool openEditor(ProjectModel::CurveNode* node);
bool openEditor(ProjectModel::KeymapNode* node); bool openEditor(ProjectModel::KeymapNode* node);
bool openEditor(ProjectModel::LayersNode* node); bool openEditor(ProjectModel::LayersNode* node);
bool openEditor(ProjectModel::SampleNode* node);
bool openEditor(ProjectModel::INode* node); bool openEditor(ProjectModel::INode* node);
void closeEditor(); void closeEditor();
@ -153,6 +156,7 @@ public slots:
void clearRecentFilesAction(); void clearRecentFilesAction();
void saveAction(); void saveAction();
void revertAction(); void revertAction();
void reloadSampleDataAction();
void importAction(); void importAction();
void exportAction(); void exportAction();

View File

@ -306,8 +306,9 @@
<addaction name="menuRecent_Projects"/> <addaction name="menuRecent_Projects"/>
<addaction name="actionSave_Project"/> <addaction name="actionSave_Project"/>
<addaction name="actionRevert_Project"/> <addaction name="actionRevert_Project"/>
<addaction name="actionReload_Sample_Data"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="actionImport"/> <addaction name="actionImport_Groups"/>
<addaction name="actionExport_GameCube_Groups"/> <addaction name="actionExport_GameCube_Groups"/>
</widget> </widget>
<widget class="QMenu" name="menuProject"> <widget class="QMenu" name="menuProject">
@ -398,9 +399,9 @@
<string>&amp;Delete</string> <string>&amp;Delete</string>
</property> </property>
</action> </action>
<action name="actionImport"> <action name="actionImport_Groups">
<property name="text"> <property name="text">
<string>&amp;Import</string> <string>&amp;Import Groups</string>
</property> </property>
<property name="shortcut"> <property name="shortcut">
<string>Ctrl+I</string> <string>Ctrl+I</string>
@ -556,6 +557,14 @@
<string>&amp;Revert Project</string> <string>&amp;Revert Project</string>
</property> </property>
</action> </action>
<action name="actionReload_Sample_Data">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Reload Sample &amp;Data</string>
</property>
</action>
</widget> </widget>
<customwidgets> <customwidgets>
<customwidget> <customwidget>

View File

@ -122,6 +122,21 @@ bool ProjectModel::openGroupData(const QString& groupName, UIMessenger& messenge
return true; 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, bool ProjectModel::importGroupData(const QString& groupName, const amuse::AudioGroupData& data,
ImportMode mode, UIMessenger& messenger) ImportMode mode, UIMessenger& messenger)
{ {
@ -200,6 +215,7 @@ void ProjectModel::_resetModelData()
auto& tables = group.getPool().tables(); auto& tables = group.getPool().tables();
auto& keymaps = group.getPool().keymaps(); auto& keymaps = group.getPool().keymaps();
auto& layers = group.getPool().layers(); auto& layers = group.getPool().layers();
auto& samples = group.getSdir().sampleEntries();
gn.reserve(songGroups.size() + sfxGroups.size() + 4); gn.reserve(songGroups.size() + sfxGroups.size() + 4);
for (const auto& grp : SortUnorderedMap(songGroups)) for (const auto& grp : SortUnorderedMap(songGroups))
gn.makeChild<SongGroupNode>(grp.first, grp.second.get()); gn.makeChild<SongGroupNode>(grp.first, grp.second.get());
@ -218,7 +234,7 @@ void ProjectModel::_resetModelData()
size_t curveCount = 0; size_t curveCount = 0;
for (auto& t : tablesSort) 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) if (tp == amuse::ITable::Type::ADSR || tp == amuse::ITable::Type::ADSRDLS)
ADSRCount += 1; ADSRCount += 1;
else if (tp == amuse::ITable::Type::Curve) else if (tp == amuse::ITable::Type::Curve)
@ -230,7 +246,7 @@ void ProjectModel::_resetModelData()
col.reserve(ADSRCount); col.reserve(ADSRCount);
for (auto& t : tablesSort) 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) if (tp == amuse::ITable::Type::ADSR || tp == amuse::ITable::Type::ADSRDLS)
col.makeChild<ADSRNode>(t.first, t.second.get()); col.makeChild<ADSRNode>(t.first, t.second.get());
} }
@ -241,9 +257,9 @@ void ProjectModel::_resetModelData()
col.reserve(curveCount); col.reserve(curveCount);
for (auto& t : tablesSort) 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) if (tp == amuse::ITable::Type::Curve)
col.makeChild<CurveNode>(t.first, std::static_pointer_cast<amuse::Curve>(t.second.get())); col.makeChild<CurveNode>(t.first, t.second.get());
} }
} }
} }
@ -261,6 +277,13 @@ void ProjectModel::_resetModelData()
for (auto& keymap : SortUnorderedMap(layers)) for (auto& keymap : SortUnorderedMap(layers))
col.makeChild<LayersNode>(keymap.first, keymap.second.get()); col.makeChild<LayersNode>(keymap.first, keymap.second.get());
} }
{
CollectionNode& col =
gn.makeChild<CollectionNode>(tr("Samples"), QIcon(":/icons/IconSample.svg"), INode::Type::Sample);
col.reserve(samples.size());
for (auto& sample : SortUnorderedMap(samples))
col.makeChild<SampleNode>(sample.first, sample.second.get());
}
} }
endResetModel(); endResetModel();
} }

View File

@ -61,7 +61,8 @@ public:
ADSR, ADSR,
Curve, Curve,
Keymap, Keymap,
Layer Layer,
Sample
}; };
protected: protected:
INode* m_parent; INode* m_parent;
@ -168,8 +169,8 @@ public:
{ {
amuse::GroupId m_id; amuse::GroupId m_id;
QString m_name; QString m_name;
std::shared_ptr<amuse::SongGroupIndex> m_index; amuse::ObjToken<amuse::SongGroupIndex> m_index;
SongGroupNode(INode* parent, int row, amuse::GroupId id, std::shared_ptr<amuse::SongGroupIndex> index) SongGroupNode(INode* parent, int row, amuse::GroupId id, amuse::ObjToken<amuse::SongGroupIndex> index)
: INode(parent, row), m_id(id), m_name(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_index(index) {} : INode(parent, row), m_id(id), m_name(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_index(index) {}
static QIcon Icon; static QIcon Icon;
@ -184,8 +185,8 @@ public:
{ {
amuse::GroupId m_id; amuse::GroupId m_id;
QString m_name; QString m_name;
std::shared_ptr<amuse::SFXGroupIndex> m_index; amuse::ObjToken<amuse::SFXGroupIndex> m_index;
SoundGroupNode(INode* parent, int row, amuse::GroupId id, std::shared_ptr<amuse::SFXGroupIndex> index) SoundGroupNode(INode* parent, int row, amuse::GroupId id, amuse::ObjToken<amuse::SFXGroupIndex> index)
: INode(parent, row), m_id(id), m_name(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_index(index) {} : INode(parent, row), m_id(id), m_name(amuse::GroupId::CurNameDB->resolveNameFromId(id).data()), m_index(index) {}
static QIcon Icon; static QIcon Icon;
@ -221,30 +222,31 @@ public:
struct BasePoolObjectNode : INode struct BasePoolObjectNode : INode
{ {
amuse::ObjectId m_id; amuse::ObjectId m_id;
BasePoolObjectNode(INode* parent, int row, amuse::ObjectId id) QString m_name;
: INode(parent, row), m_id(id) {} 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; } amuse::ObjectId id() const { return m_id; }
QString text() const { return m_name; }
QIcon icon() const { return {}; }
}; };
template <class ID, class T, INode::Type TP> template <class ID, class T, INode::Type TP>
struct PoolObjectNode : BasePoolObjectNode struct PoolObjectNode : BasePoolObjectNode
{ {
QString m_name; amuse::ObjToken<T> m_obj;
std::shared_ptr<T> m_obj; PoolObjectNode(INode* parent, int row, ID id, amuse::ObjToken<T> obj)
PoolObjectNode(INode* parent, int row, ID id, std::shared_ptr<T> obj) : BasePoolObjectNode(parent, row, id, ID::CurNameDB->resolveNameFromId(id).data()), m_obj(obj) {}
: BasePoolObjectNode(parent, row, id), m_name(ID::CurNameDB->resolveNameFromId(id).data()), m_obj(obj) {}
Type type() const { return TP; } Type type() const { return TP; }
QString text() const { return m_name; }
QIcon icon() const { return {}; }
std::shared_ptr<PoolObjectNode<ID, T, TP>> shared_from_this() std::shared_ptr<PoolObjectNode<ID, T, TP>> shared_from_this()
{ return std::static_pointer_cast<PoolObjectNode<ID, T, TP>>(INode::shared_from_this()); } { return std::static_pointer_cast<PoolObjectNode<ID, T, TP>>(INode::shared_from_this()); }
}; };
using SoundMacroNode = PoolObjectNode<amuse::SoundMacroId, amuse::SoundMacro, INode::Type::SoundMacro>; using SoundMacroNode = PoolObjectNode<amuse::SoundMacroId, amuse::SoundMacro, INode::Type::SoundMacro>;
using ADSRNode = PoolObjectNode<amuse::TableId, amuse::ITable, INode::Type::ADSR>; using ADSRNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::ADSR>;
using CurveNode = PoolObjectNode<amuse::TableId, amuse::Curve, INode::Type::Curve>; using CurveNode = PoolObjectNode<amuse::TableId, std::unique_ptr<amuse::ITable>, INode::Type::Curve>;
using KeymapNode = PoolObjectNode<amuse::KeymapId, amuse::Keymap, INode::Type::Keymap>; using KeymapNode = PoolObjectNode<amuse::KeymapId, amuse::Keymap, INode::Type::Keymap>;
using LayersNode = PoolObjectNode<amuse::LayersId, std::vector<amuse::LayerMapping>, INode::Type::Layer>; using LayersNode = PoolObjectNode<amuse::LayersId, std::vector<amuse::LayerMapping>, INode::Type::Layer>;
using SampleNode = PoolObjectNode<amuse::SampleId, amuse::SampleEntry, INode::Type::Sample>;
std::shared_ptr<RootNode> m_root; std::shared_ptr<RootNode> m_root;
@ -256,6 +258,7 @@ public:
bool clearProjectData(); bool clearProjectData();
bool openGroupData(const QString& groupName, UIMessenger& messenger); bool openGroupData(const QString& groupName, UIMessenger& messenger);
bool reloadSampleData(const QString& groupName, UIMessenger& messenger);
bool importGroupData(const QString& groupName, const amuse::AudioGroupData& data, bool importGroupData(const QString& groupName, const amuse::AudioGroupData& data,
ImportMode mode, UIMessenger& messenger); ImportMode mode, UIMessenger& messenger);
bool saveToFile(UIMessenger& messenger); bool saveToFile(UIMessenger& messenger);

View File

@ -1,5 +1,10 @@
#include "SampleEditor.hpp" #include "SampleEditor.hpp"
bool SampleEditor::loadData(ProjectModel::SampleNode* node)
{
return false;
}
SampleEditor::SampleEditor(QWidget* parent) SampleEditor::SampleEditor(QWidget* parent)
: EditorWidget(parent) : EditorWidget(parent)
{ {

View File

@ -8,6 +8,7 @@ class SampleEditor : public EditorWidget
Q_OBJECT Q_OBJECT
public: public:
explicit SampleEditor(QWidget* parent = Q_NULLPTR); explicit SampleEditor(QWidget* parent = Q_NULLPTR);
bool loadData(ProjectModel::SampleNode* node);
}; };

View File

@ -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::SoundMacroId:
case amuse::SoundMacro::CmdIntrospection::Field::Type::TableId: case amuse::SoundMacro::CmdIntrospection::Field::Type::TableId:
case amuse::SoundMacro::CmdIntrospection::Field::Type::SampleId:
{ {
ProjectModel::INode::Type collectionType; ProjectModel::INode::Type collectionType;
if (field.m_tp == amuse::SoundMacro::CmdIntrospection::Field::Type::SoundMacroId) if (field.m_tp == amuse::SoundMacro::CmdIntrospection::Field::Type::SoundMacroId)
@ -250,6 +251,10 @@ CommandWidget::CommandWidget(amuse::SoundMacro::ICmd* cmd, amuse::SoundMacro::Cm
else else
collectionType = ProjectModel::INode::Type::Curve; 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())-> auto* collection = g_MainWindow->projectModel()->getGroupNode(listing->currentNode())->
getCollectionOfType(collectionType); getCollectionOfType(collectionType);
nf = new FieldProjectNode(collection); nf = new FieldProjectNode(collection);

View File

@ -5,6 +5,7 @@
#include "MainWindow.hpp" #include "MainWindow.hpp"
#include "boo/IApplication.hpp" #include "boo/IApplication.hpp"
#include <QResource> #include <QResource>
#include <QCommandLineParser>
using namespace std::literals; using namespace std::literals;
@ -101,5 +102,12 @@ int main(int argc, char* argv[])
MainWindow w; MainWindow w;
g_MainWindow = &w; g_MainWindow = &w;
w.show(); w.show();
QCommandLineParser parser;
parser.process(a);
QStringList args = parser.positionalArguments();
if (!args.empty())
w.openProject(args.back());
return a.exec(); return a.exec();
} }

View File

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="16"
height="16"
viewBox="0 0 4.2333332 4.2333335"
id="svg2"
version="1.1"
inkscape:version="0.92.2 2405546, 2018-03-11"
sodipodi:docname="IconSample.svg">
<sodipodi:namedview
id="base"
pagecolor="#393939"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:zoom="34.25"
inkscape:cx="8.0516224"
inkscape:cy="8.452632"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="true"
units="px"
inkscape:window-width="3840"
inkscape:window-height="2079"
inkscape:window-x="0"
inkscape:window-y="40"
inkscape:window-maximized="1"
showborder="true"
objecttolerance="4"
gridtolerance="4"
guidetolerance="5">
<inkscape:grid
visible="true"
empspacing="1"
id="grid4173"
type="xygrid" />
</sodipodi:namedview>
<defs
id="defs4" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
transform="translate(0,-292.76665)"
id="layer1"
inkscape:groupmode="layer"
inkscape:label="Layer 1">
<path
id="path1184"
title="sin(x*4*pi)*sin(x*pi)"
d="m 0.36489826,294.88332 c 0.12976062,-1.7e-4 0.25952124,-0.20582 0.38928188,-0.34418 0.12976062,-0.13838 0.25952116,-0.13639 0.38928186,0.11952 0.1297606,0.25593 0.2595211,0.73341 0.3892818,0.99106 0.1297606,0.25765 0.2595213,0.22574 0.3892819,-0.11953 0.1297606,-0.34528 0.2595212,-0.94796 0.3892819,-1.29374 0.1297606,-0.34576 0.2595213,-0.37673 0.3892818,-0.11953 0.1297606,0.2572 0.2595213,0.73474 0.3892819,0.99106 0.1297607,0.25631 0.2595213,0.25771 0.3892819,0.11953 0.1297606,-0.13818 0.2595212,-0.34402 0.3892819,-0.34419"
style="fill:none;fill-opacity:1;stroke:#04e2ff;stroke-width:0.43794215;stroke-linecap:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1;paint-order:normal"
inkscape:connector-curvature="0" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -13,6 +13,7 @@
<name>MainWindow</name> <name>MainWindow</name>
<message> <message>
<location filename="../MainWindow.ui" line="+14"/> <location filename="../MainWindow.ui" line="+14"/>
<location filename="../MainWindow.cpp" line="+181"/>
<source>Amuse</source> <source>Amuse</source>
<translation>Amuse</translation> <translation>Amuse</translation>
</message> </message>
@ -22,7 +23,7 @@
<translation>&amp;Datei</translation> <translation>&amp;Datei</translation>
</message> </message>
<message> <message>
<location line="+21"/> <location line="+22"/>
<source>P&amp;roject</source> <source>P&amp;roject</source>
<translation>Projekt</translation> <translation>Projekt</translation>
</message> </message>
@ -60,12 +61,12 @@
<translation type="vanished">&amp; Wiederholen</translation> <translation type="vanished">&amp; Wiederholen</translation>
</message> </message>
<message> <message>
<location line="-65"/> <location line="-66"/>
<source>Recent &amp;Projects</source> <source>Recent &amp;Projects</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+73"/> <location line="+74"/>
<source>&amp;Cut</source> <source>&amp;Cut</source>
<translation>&amp;Schnitt</translation> <translation>&amp;Schnitt</translation>
</message> </message>
@ -85,9 +86,13 @@
<translation>&amp;Löschen</translation> <translation>&amp;Löschen</translation>
</message> </message>
<message> <message>
<location line="+5"/>
<source>&amp;Import</source> <source>&amp;Import</source>
<translation>&amp;Einführen</translation> <translation type="vanished">&amp;Einführen</translation>
</message>
<message>
<location line="+5"/>
<source>&amp;Import Groups</source>
<translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+3"/> <location line="+3"/>
@ -170,12 +175,17 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="../MainWindow.cpp" line="+70"/> <location line="+8"/>
<source>Reload Sample &amp;Data</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../MainWindow.cpp" line="-109"/>
<source>Quit</source> <source>Quit</source>
<translation>Verlassen</translation> <translation>Verlassen</translation>
</message> </message>
<message> <message>
<location line="+134"/> <location line="+160"/>
<source>The directory at &apos;%1&apos; must exist for the Amuse editor.</source> <source>The directory at &apos;%1&apos; must exist for the Amuse editor.</source>
<translation>Das Verzeichnis unter &apos;% 1&apos; muss für den Amuse-Editor vorhanden sein.</translation> <translation>Das Verzeichnis unter &apos;% 1&apos; muss für den Amuse-Editor vorhanden sein.</translation>
</message> </message>
@ -200,7 +210,7 @@
<translation>Es konnte nicht in das Verzeichnis geschrieben werden</translation> <translation>Es konnte nicht in das Verzeichnis geschrieben werden</translation>
</message> </message>
<message> <message>
<location line="+44"/> <location line="+46"/>
<source>No Audio Devices Found</source> <source>No Audio Devices Found</source>
<translation>Keine Audiogeräte gefunden</translation> <translation>Keine Audiogeräte gefunden</translation>
</message> </message>
@ -210,7 +220,7 @@
<translation>Keine MIDI-Geräte gefunden</translation> <translation>Keine MIDI-Geräte gefunden</translation>
</message> </message>
<message> <message>
<location line="+193"/> <location line="+201"/>
<source>New Project</source> <source>New Project</source>
<translation>Neues Projekt</translation> <translation>Neues Projekt</translation>
</message> </message>
@ -225,29 +235,39 @@
<translation>Das Verzeichnis &apos;% 1&apos; existiert nicht.</translation> <translation>Das Verzeichnis &apos;% 1&apos; existiert nicht.</translation>
</message> </message>
<message> <message>
<location line="-432"/> <location line="-468"/>
<source>Clear Recent Projects</source> <source>Clear Recent Projects</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+134"/> <location line="+124"/>
<location line="+292"/> <source>Amuse [%1/%2/%3]</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+5"/>
<source>Amuse [%1]</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+31"/>
<location line="+302"/>
<source>The directory at &apos;%1&apos; must not be empty.</source> <source>The directory at &apos;%1&apos; must not be empty.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="-291"/> <location line="-301"/>
<location line="+292"/> <location line="+302"/>
<source>Directory empty</source> <source>Directory empty</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="-181"/> <location line="-189"/>
<source>SUSTAIN</source> <source>SUSTAIN</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location line="+187"/> <location line="+195"/>
<source>Bad Directory</source> <source>Bad Directory</source>
<translation>Schlechtes Verzeichnis</translation> <translation>Schlechtes Verzeichnis</translation>
</message> </message>
@ -258,18 +278,29 @@
</message> </message>
<message> <message>
<location line="+0"/> <location line="+0"/>
<location line="+127"/> <location line="+76"/>
<location line="+83"/>
<location line="+45"/> <location line="+45"/>
<source>Scanning Project</source> <source>Scanning Project</source>
<translation>Projekt scannen</translation> <translation>Projekt scannen</translation>
</message> </message>
<message> <message>
<location line="-160"/> <location line="-192"/>
<source>Opening %1</source> <source>Opening %1</source>
<translation>Eröffnung% 1</translation> <translation>Eröffnung% 1</translation>
</message> </message>
<message> <message>
<location line="+54"/> <location line="+64"/>
<source>Reloading Samples</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+12"/>
<source>Scanning %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+10"/>
<source>Import Project</source> <source>Import Project</source>
<translation>Projekt importieren</translation> <translation>Projekt importieren</translation>
</message> </message>
@ -360,7 +391,7 @@
<context> <context>
<name>ProjectModel</name> <name>ProjectModel</name>
<message> <message>
<location filename="../ProjectModel.cpp" line="+210"/> <location filename="../ProjectModel.cpp" line="+226"/>
<source>Sound Macros</source> <source>Sound Macros</source>
<translation>Sound-Makros</translation> <translation>Sound-Makros</translation>
</message> </message>
@ -384,6 +415,11 @@
<source>Layers</source> <source>Layers</source>
<translation>Lagen</translation> <translation>Lagen</translation>
</message> </message>
<message>
<location line="+7"/>
<source>Samples</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>QObject</name> <name>QObject</name>
@ -401,7 +437,7 @@
<context> <context>
<name>QUndoStack</name> <name>QUndoStack</name>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="+157"/> <location filename="../SoundMacroEditor.cpp" line="+162"/>
<source>Change %1</source> <source>Change %1</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -506,7 +542,7 @@
<context> <context>
<name>TargetButton</name> <name>TargetButton</name>
<message> <message>
<location filename="../SoundMacroEditor.cpp" line="-939"/> <location filename="../SoundMacroEditor.cpp" line="-944"/>
<source>Set step with target click</source> <source>Set step with target click</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>

View File

@ -23,6 +23,7 @@
<file>IconSoundMacroTarget.svg</file> <file>IconSoundMacroTarget.svg</file>
<file>IconSoundMacroTargetDisabled.svg</file> <file>IconSoundMacroTargetDisabled.svg</file>
<file>IconKill.svg</file> <file>IconKill.svg</file>
<file>IconSample.svg</file>
</qresource> </qresource>
<qresource prefix="/bg"> <qresource prefix="/bg">
<file>FaceGrey.svg</file> <file>FaceGrey.svg</file>

View File

@ -66,12 +66,12 @@ struct AppCallback : boo::IApplicationCallback
int m_chanId = 0; int m_chanId = 0;
int8_t m_octave = 4; int8_t m_octave = 4;
int8_t m_velocity = 64; int8_t m_velocity = 64;
std::shared_ptr<amuse::Sequencer> m_seq; amuse::ObjToken<amuse::Sequencer> m_seq;
amuse::ContainerRegistry::SongData* m_arrData = nullptr; amuse::ContainerRegistry::SongData* m_arrData = nullptr;
/* SFX playback selection */ /* SFX playback selection */
int m_sfxId = -1; int m_sfxId = -1;
std::shared_ptr<amuse::Voice> m_vox; amuse::ObjToken<amuse::Voice> m_vox;
size_t m_lastVoxCount = 0; size_t m_lastVoxCount = 0;
int8_t m_lastChanProg = -1; int8_t m_lastChanProg = -1;
@ -665,10 +665,10 @@ struct AppCallback : boo::IApplicationCallback
std::list<amuse::AudioGroupProject> m_projs; std::list<amuse::AudioGroupProject> m_projs;
std::map<amuse::GroupId, std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*, std::map<amuse::GroupId, std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*,
const amuse::SongGroupIndex*>> amuse::ObjToken<amuse::SongGroupIndex>>>
allSongGroups; allSongGroups;
std::map<amuse::GroupId, std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*, std::map<amuse::GroupId, std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*,
const amuse::SFXGroupIndex*>> amuse::ObjToken<amuse::SFXGroupIndex>>>
allSFXGroups; allSFXGroups;
size_t totalGroups = 0; size_t totalGroups = 0;
@ -680,10 +680,10 @@ struct AppCallback : boo::IApplicationCallback
totalGroups += proj.sfxGroups().size() + proj.songGroups().size(); totalGroups += proj.sfxGroups().size() + proj.songGroups().size();
for (auto it = proj.songGroups().begin(); it != proj.songGroups().end(); ++it) 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) 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) while (m_running)
@ -825,14 +825,14 @@ struct AppCallback : boo::IApplicationCallback
printf("Multiple Audio Groups discovered:\n"); printf("Multiple Audio Groups discovered:\n");
for (const auto& pair : allSFXGroups) 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()); pair.second.first->first.c_str(), pair.second.second->m_sfxEntries.size());
} }
for (const auto& pair : allSongGroups) for (const auto& pair : allSongGroups)
{ {
amuse::Printf(_S(" %d %s (SongGroup) %" PRISize " normal-pages, %" PRISize amuse::Printf(_S(" %d %s (SongGroup) %" PRISize " normal-pages, %" PRISize
" drum-pages, %" PRISize " MIDI-setups\n"), " 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()); pair.second.second->m_drumPages.size(), pair.second.second->m_midiSetups.size());
} }
@ -884,8 +884,8 @@ struct AppCallback : boo::IApplicationCallback
/* Make final group selection */ /* Make final group selection */
amuse::IntrusiveAudioGroupData* selData = nullptr; amuse::IntrusiveAudioGroupData* selData = nullptr;
const amuse::SongGroupIndex* songIndex = nullptr; amuse::ObjToken<amuse::SongGroupIndex> songIndex;
const amuse::SFXGroupIndex* sfxIndex = nullptr; amuse::ObjToken<amuse::SFXGroupIndex> sfxIndex;
auto songSearch = allSongGroups.find(m_groupId); auto songSearch = allSongGroups.find(m_groupId);
if (songSearch != allSongGroups.end()) if (songSearch != allSongGroups.end())
{ {

View File

@ -163,10 +163,10 @@ int main(int argc, const boo::SystemChar** argv)
std::list<amuse::AudioGroupProject> m_projs; std::list<amuse::AudioGroupProject> m_projs;
std::map<int, std::map<int,
std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*, const amuse::SongGroupIndex*>> std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*, amuse::ObjToken<amuse::SongGroupIndex>>>
allSongGroups; allSongGroups;
std::map<int, std::map<int,
std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*, const amuse::SFXGroupIndex*>> std::pair<std::pair<amuse::SystemString, amuse::IntrusiveAudioGroupData>*, amuse::ObjToken<amuse::SFXGroupIndex>>>
allSFXGroups; allSFXGroups;
size_t totalGroups = 0; size_t totalGroups = 0;
@ -178,10 +178,10 @@ int main(int argc, const boo::SystemChar** argv)
totalGroups += proj.sfxGroups().size() + proj.songGroups().size(); totalGroups += proj.sfxGroups().size() + proj.songGroups().size();
for (auto it = proj.songGroups().begin(); it != proj.songGroups().end(); ++it) 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) 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 */ /* Attempt loading song */
@ -393,8 +393,8 @@ int main(int argc, const boo::SystemChar** argv)
/* Make final group selection */ /* Make final group selection */
amuse::IntrusiveAudioGroupData* selData = nullptr; amuse::IntrusiveAudioGroupData* selData = nullptr;
const amuse::SongGroupIndex* songIndex = nullptr; amuse::ObjToken<amuse::SongGroupIndex> songIndex;
const amuse::SFXGroupIndex* sfxIndex = nullptr; amuse::ObjToken<amuse::SFXGroupIndex> sfxIndex;
auto songSearch = allSongGroups.find(m_groupId); auto songSearch = allSongGroups.find(m_groupId);
if (songSearch != allSongGroups.end()) if (songSearch != allSongGroups.end())
{ {
@ -465,7 +465,7 @@ int main(int argc, const boo::SystemChar** argv)
} }
/* Enter playback loop */ /* Enter playback loop */
std::shared_ptr<amuse::Sequencer> seq = engine.seqPlay(m_groupId, m_setupId, m_arrData->m_data.get()); amuse::ObjToken<amuse::Sequencer> seq = engine.seqPlay(m_groupId, m_setupId, m_arrData->m_data.get());
size_t wroteFrames = 0; size_t wroteFrames = 0;
signal(SIGINT, SIGINTHandler); signal(SIGINT, SIGINTHandler);
do do

View File

@ -28,8 +28,9 @@ public:
void assign(const AudioGroupData& data); void assign(const AudioGroupData& data);
void assign(SystemStringView groupPath); void assign(SystemStringView groupPath);
const AudioGroupSampleDirectory::Entry* getSample(SampleId sfxId) const; const SampleEntry* getSample(SampleId sfxId) const;
const unsigned char* getSampleData(SampleId sfxId, const AudioGroupSampleDirectory::Entry* sample) const; std::pair<ObjToken<SampleEntryData>, const unsigned char*>
getSampleData(SampleId sfxId, const SampleEntry* sample) const;
const AudioGroupProject& getProj() const { return m_proj; } const AudioGroupProject& getProj() const { return m_proj; }
const AudioGroupPool& getPool() const { return m_pool; } const AudioGroupPool& getPool() const { return m_pool; }
const AudioGroupSampleDirectory& getSdir() const { return m_sdir; } const AudioGroupSampleDirectory& getSdir() const { return m_sdir; }

View File

@ -1338,10 +1338,10 @@ struct LayerMapping : BigDNA
/** Database of functional objects within Audio Group */ /** Database of functional objects within Audio Group */
class AudioGroupPool class AudioGroupPool
{ {
std::unordered_map<SoundMacroId, std::shared_ptr<SoundMacro>> m_soundMacros; std::unordered_map<SoundMacroId, ObjToken<SoundMacro>> m_soundMacros;
std::unordered_map<TableId, std::shared_ptr<ITable>> m_tables; std::unordered_map<TableId, ObjToken<std::unique_ptr<ITable>>> m_tables;
std::unordered_map<KeymapId, std::shared_ptr<Keymap>> m_keymaps; std::unordered_map<KeymapId, ObjToken<Keymap>> m_keymaps;
std::unordered_map<LayersId, std::shared_ptr<std::vector<LayerMapping>>> m_layers; std::unordered_map<LayersId, ObjToken<std::vector<LayerMapping>>> m_layers;
template <athena::Endian DNAE> template <athena::Endian DNAE>
static AudioGroupPool _AudioGroupPool(athena::io::IStreamReader& r); static AudioGroupPool _AudioGroupPool(athena::io::IStreamReader& r);
@ -1350,14 +1350,14 @@ public:
static AudioGroupPool CreateAudioGroupPool(const AudioGroupData& data); static AudioGroupPool CreateAudioGroupPool(const AudioGroupData& data);
static AudioGroupPool CreateAudioGroupPool(SystemStringView groupPath); static AudioGroupPool CreateAudioGroupPool(SystemStringView groupPath);
const std::unordered_map<SoundMacroId, std::shared_ptr<SoundMacro>>& soundMacros() const { return m_soundMacros; } const std::unordered_map<SoundMacroId, ObjToken<SoundMacro>>& soundMacros() const { return m_soundMacros; }
const std::unordered_map<TableId, std::shared_ptr<ITable>>& tables() const { return m_tables; } const std::unordered_map<TableId, ObjToken<std::unique_ptr<ITable>>>& tables() const { return m_tables; }
const std::unordered_map<KeymapId, std::shared_ptr<Keymap>>& keymaps() const { return m_keymaps; } const std::unordered_map<KeymapId, ObjToken<Keymap>>& keymaps() const { return m_keymaps; }
const std::unordered_map<LayersId, std::shared_ptr<std::vector<LayerMapping>>>& layers() const { return m_layers; } const std::unordered_map<LayersId, ObjToken<std::vector<LayerMapping>>>& layers() const { return m_layers; }
std::unordered_map<SoundMacroId, std::shared_ptr<SoundMacro>>& soundMacros() { return m_soundMacros; } std::unordered_map<SoundMacroId, ObjToken<SoundMacro>>& soundMacros() { return m_soundMacros; }
std::unordered_map<TableId, std::shared_ptr<ITable>>& tables() { return m_tables; } std::unordered_map<TableId, ObjToken<std::unique_ptr<ITable>>>& tables() { return m_tables; }
std::unordered_map<KeymapId, std::shared_ptr<Keymap>>& keymaps() { return m_keymaps; } std::unordered_map<KeymapId, ObjToken<Keymap>>& keymaps() { return m_keymaps; }
std::unordered_map<LayersId, std::shared_ptr<std::vector<LayerMapping>>>& layers() { return m_layers; } std::unordered_map<LayersId, ObjToken<std::vector<LayerMapping>>>& layers() { return m_layers; }
const SoundMacro* soundMacro(ObjectId id) const; const SoundMacro* soundMacro(ObjectId id) const;
const Keymap* keymap(ObjectId id) const; const Keymap* keymap(ObjectId id) const;

View File

@ -180,8 +180,8 @@ struct SFXGroupIndex : AudioGroupIndex
/** Collection of SongGroup and SFXGroup indexes */ /** Collection of SongGroup and SFXGroup indexes */
class AudioGroupProject class AudioGroupProject
{ {
std::unordered_map<GroupId, std::shared_ptr<SongGroupIndex>> m_songGroups; std::unordered_map<GroupId, ObjToken<SongGroupIndex>> m_songGroups;
std::unordered_map<GroupId, std::shared_ptr<SFXGroupIndex>> m_sfxGroups; std::unordered_map<GroupId, ObjToken<SFXGroupIndex>> m_sfxGroups;
AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag); AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag);
template <athena::Endian DNAE> template <athena::Endian DNAE>
@ -199,10 +199,10 @@ public:
const SongGroupIndex* getSongGroupIndex(int groupId) const; const SongGroupIndex* getSongGroupIndex(int groupId) const;
const SFXGroupIndex* getSFXGroupIndex(int groupId) const; const SFXGroupIndex* getSFXGroupIndex(int groupId) const;
const std::unordered_map<GroupId, std::shared_ptr<SongGroupIndex>>& songGroups() const { return m_songGroups; } const std::unordered_map<GroupId, ObjToken<SongGroupIndex>>& songGroups() const { return m_songGroups; }
const std::unordered_map<GroupId, std::shared_ptr<SFXGroupIndex>>& sfxGroups() const { return m_sfxGroups; } const std::unordered_map<GroupId, ObjToken<SFXGroupIndex>>& sfxGroups() const { return m_sfxGroups; }
std::unordered_map<GroupId, std::shared_ptr<SongGroupIndex>>& songGroups() { return m_songGroups; } std::unordered_map<GroupId, ObjToken<SongGroupIndex>>& songGroups() { return m_songGroups; }
std::unordered_map<GroupId, std::shared_ptr<SFXGroupIndex>>& sfxGroups() { return m_sfxGroups; } std::unordered_map<GroupId, ObjToken<SFXGroupIndex>>& sfxGroups() { return m_sfxGroups; }
bool toYAML(SystemStringView groupPath) const; bool toYAML(SystemStringView groupPath) const;

View File

@ -184,11 +184,11 @@ public:
Value<uint32_t, DNAEn> m_loopStartSample; Value<uint32_t, DNAEn> m_loopStartSample;
Value<uint32_t, DNAEn> m_loopLengthSamples; Value<uint32_t, DNAEn> m_loopLengthSamples;
}; };
struct Entry struct EntryData
{ {
atUint32 m_sampleOff = 0; atUint32 m_sampleOff = 0;
atUint32 m_unk = 0; atUint32 m_unk = 0;
atUint8 m_pitch = 0; atUint8 m_pitch = 60;
atUint16 m_sampleRate = 0; atUint16 m_sampleRate = 0;
atUint32 m_numSamples = 0; // Top 8 bits is SampleFormat atUint32 m_numSamples = 0; // Top 8 bits is SampleFormat
atUint32 m_loopStartSample = 0; atUint32 m_loopStartSample = 0;
@ -204,24 +204,24 @@ public:
time_t m_looseModTime = 0; time_t m_looseModTime = 0;
std::unique_ptr<uint8_t[]> m_looseData; std::unique_ptr<uint8_t[]> m_looseData;
Entry() = default; EntryData() = default;
template <athena::Endian DNAE> template <athena::Endian DNAE>
Entry(const EntryDNA<DNAE>& in) EntryData(const EntryDNA<DNAE>& in)
: m_sampleOff(in.m_sampleOff), m_unk(in.m_unk), m_pitch(in.m_pitch), : 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_sampleRate(in.m_sampleRate), m_numSamples(in.m_numSamples),
m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples), m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples),
m_adpcmParmOffset(in.m_adpcmParmOffset) {} m_adpcmParmOffset(in.m_adpcmParmOffset) {}
template <athena::Endian DNAE> template <athena::Endian DNAE>
Entry(const MusyX1SdirEntry<DNAE>& in) EntryData(const MusyX1SdirEntry<DNAE>& in)
: m_sampleOff(in.m_sampleOff), m_unk(0), m_pitch(in.m_pitchSampleRate >> 24), : 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_sampleRate(in.m_pitchSampleRate & 0xffff), m_numSamples(in.m_numSamples),
m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples), m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples),
m_adpcmParmOffset(0) {} m_adpcmParmOffset(0) {}
template <athena::Endian DNAE> template <athena::Endian DNAE>
Entry(const MusyX1AbsSdirEntry<DNAE>& in) EntryData(const MusyX1AbsSdirEntry<DNAE>& in)
: m_sampleOff(in.m_sampleOff), m_unk(in.m_unk), m_pitch(in.m_pitchSampleRate >> 24), : 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_sampleRate(in.m_pitchSampleRate & 0xffff), m_numSamples(in.m_numSamples),
m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples), m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples),
@ -243,14 +243,45 @@ public:
return ret; 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<EntryData> m_data;
Entry()
: m_data(MakeObj<EntryData>()) {}
template <athena::Endian DNAE>
Entry(const EntryDNA<DNAE>& in)
: m_data(MakeObj<EntryData>(in)) {}
template <athena::Endian DNAE>
Entry(const MusyX1SdirEntry<DNAE>& in)
: m_data(MakeObj<EntryData>(in)) {}
template <athena::Endian DNAE>
Entry(const MusyX1AbsSdirEntry<DNAE>& in)
: m_data(MakeObj<EntryData>(in)) {}
template <athena::Endian DNAEn>
EntryDNA<DNAEn> toDNA(SFXId id) const
{
return m_data->toDNA<DNAEn>(id);
}
void loadLooseData(SystemStringView basePath); void loadLooseData(SystemStringView basePath);
}; };
private: private:
std::unordered_map<SampleId, Entry> m_entries; std::unordered_map<SampleId, ObjToken<Entry>> m_entries;
static void _extractWAV(SampleId id, const Entry& ent, amuse::SystemStringView destDir, static void _extractWAV(SampleId id, const EntryData& ent, amuse::SystemStringView destDir,
const unsigned char* samp); 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); const unsigned char* samp);
public: public:
@ -261,18 +292,23 @@ public:
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(const AudioGroupData& data); static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(const AudioGroupData& data);
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(SystemStringView groupPath); static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(SystemStringView groupPath);
const std::unordered_map<SampleId, Entry>& sampleEntries() const { return m_entries; } const std::unordered_map<SampleId, ObjToken<Entry>>& sampleEntries() const { return m_entries; }
void extractWAV(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const; void extractWAV(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const;
void extractAllWAV(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 extractCompressed(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const;
void extractAllCompressed(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(const AudioGroupSampleDirectory&) = delete;
AudioGroupSampleDirectory& operator=(const AudioGroupSampleDirectory&) = delete; AudioGroupSampleDirectory& operator=(const AudioGroupSampleDirectory&) = delete;
AudioGroupSampleDirectory(AudioGroupSampleDirectory&&) = default; AudioGroupSampleDirectory(AudioGroupSampleDirectory&&) = default;
AudioGroupSampleDirectory& operator=(AudioGroupSampleDirectory&&) = default; AudioGroupSampleDirectory& operator=(AudioGroupSampleDirectory&&) = default;
}; };
using SampleEntry = AudioGroupSampleDirectory::Entry;
using SampleEntryData = AudioGroupSampleDirectory::EntryData;
} }
#endif // __AMUSE_AUDIOGROUPSAMPLEDIR_HPP__ #endif // __AMUSE_AUDIOGROUPSAMPLEDIR_HPP__

View File

@ -9,6 +9,7 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <cstring> #include <cstring>
#include <atomic>
#include "athena/DNA.hpp" #include "athena/DNA.hpp"
#ifndef _WIN32 #ifndef _WIN32
@ -134,6 +135,64 @@ struct LittleUInt24 : LittleDNA
LittleUInt24& operator=(uint32_t valIn) { val = valIn; return *this; } 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 SubCls>
class ObjToken;
template<class SubCls>
class ObjWrapper : IObj
{
friend class ObjToken<SubCls>;
SubCls m_obj;
SubCls* get() { return &m_obj; }
const SubCls* get() const { return &m_obj; }
public:
template <class... _Args>
ObjWrapper(_Args&&... args) : m_obj(std::forward<_Args>(args)...) {}
};
template<class SubCls>
class ObjToken
{
ObjWrapper<SubCls>* m_obj = nullptr;
public:
ObjToken() = default;
ObjToken(ObjWrapper<SubCls>* 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<SubCls>* 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 <class Tp, class... _Args>
static inline ObjToken<Tp> MakeObj(_Args&&... args)
{
return new ObjWrapper<Tp>(std::forward<_Args>(args)...);
}
#ifndef PRISize #ifndef PRISize
#ifdef _MSC_VER #ifdef _MSC_VER
#define PRISize "Iu" #define PRISize "Iu"
@ -480,6 +539,12 @@ DECL_ID_HASH(LayersId)
DECL_ID_HASH(SongId) DECL_ID_HASH(SongId)
DECL_ID_HASH(SFXId) DECL_ID_HASH(SFXId)
DECL_ID_HASH(GroupId) DECL_ID_HASH(GroupId)
template<class T>
struct hash<amuse::ObjToken<T>>
{
size_t operator()(const amuse::ObjToken<T>& val) const noexcept { return reinterpret_cast<size_t>(val.get()); }
};
} }
namespace amuse namespace amuse

View File

@ -3,13 +3,13 @@
#include "Entity.hpp" #include "Entity.hpp"
#include "Common.hpp" #include "Common.hpp"
#include "Voice.hpp"
#include <memory> #include <memory>
#include <cmath> #include <cmath>
#include <cfloat> #include <cfloat>
namespace amuse namespace amuse
{ {
class Voice;
class Listener; class Listener;
using Vector3f = float[3]; using Vector3f = float[3];
@ -37,7 +37,7 @@ static inline float Normalize(Vector3f& out)
/** Voice wrapper with positional-3D level control */ /** Voice wrapper with positional-3D level control */
class Emitter : public Entity class Emitter : public Entity
{ {
std::shared_ptr<Voice> m_vox; ObjToken<Voice> m_vox;
Vector3f m_pos = {}; Vector3f m_pos = {};
Vector3f m_dir = {}; Vector3f m_dir = {};
float m_maxDist; float m_maxDist;
@ -54,13 +54,13 @@ class Emitter : public Entity
public: public:
~Emitter(); ~Emitter();
Emitter(Engine& engine, const AudioGroup& group, const std::shared_ptr<Voice>& vox, Emitter(Engine& engine, const AudioGroup& group, ObjToken<Voice> vox,
float maxDist, float minVol, float falloff, bool doppler); float maxDist, float minVol, float falloff, bool doppler);
void setVectors(const float* pos, const float* dir); void setVectors(const float* pos, const float* dir);
void setMaxVol(float maxVol) { m_maxVol = clamp(0.f, maxVol, 1.f); m_dirty = true; } void setMaxVol(float maxVol) { m_maxVol = clamp(0.f, maxVol, 1.f); m_dirty = true; }
const std::shared_ptr<Voice>& getVoice() const { return m_vox; } ObjToken<Voice> getVoice() const { return m_vox; }
}; };
} }

View File

@ -42,13 +42,12 @@ class Engine
AmplitudeMode m_ampMode; AmplitudeMode m_ampMode;
std::unique_ptr<IMIDIReader> m_midiReader; std::unique_ptr<IMIDIReader> m_midiReader;
std::unordered_map<const AudioGroupData*, std::unique_ptr<AudioGroup>> m_audioGroups; std::unordered_map<const AudioGroupData*, std::unique_ptr<AudioGroup>> m_audioGroups;
std::list<std::shared_ptr<Voice>> m_activeVoices; std::list<ObjToken<Voice>> m_activeVoices;
std::list<std::shared_ptr<Emitter>> m_activeEmitters; std::list<ObjToken<Emitter>> m_activeEmitters;
std::list<std::shared_ptr<Listener>> m_activeListeners; std::list<ObjToken<Listener>> m_activeListeners;
std::list<std::shared_ptr<Sequencer>> m_activeSequencers; std::list<ObjToken<Sequencer>> m_activeSequencers;
std::list<std::weak_ptr<Studio>> m_activeStudios; /* lifetime dependent on contributing audio entities */
bool m_defaultStudioReady = false; bool m_defaultStudioReady = false;
std::shared_ptr<Studio> m_defaultStudio; ObjToken<Studio> m_defaultStudio;
std::unordered_map<SFXId, std::tuple<AudioGroup*, int, const SFXGroupIndex::SFXEntry*>> m_sfxLookup; std::unordered_map<SFXId, std::tuple<AudioGroup*, int, const SFXGroupIndex::SFXEntry*>> m_sfxLookup;
std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random; std::linear_congruential_engine<uint32_t, 0x41c64e6d, 0x3039, UINT32_MAX> m_random;
int m_nextVid = 0; int m_nextVid = 0;
@ -59,15 +58,14 @@ class Engine
std::pair<AudioGroup*, const SongGroupIndex*> _findSongGroup(int groupId) const; std::pair<AudioGroup*, const SongGroupIndex*> _findSongGroup(int groupId) const;
std::pair<AudioGroup*, const SFXGroupIndex*> _findSFXGroup(int groupId) const; std::pair<AudioGroup*, const SFXGroupIndex*> _findSFXGroup(int groupId) const;
std::list<std::shared_ptr<Voice>>::iterator _allocateVoice(const AudioGroup& group, int groupId, double sampleRate, std::list<ObjToken<Voice>>::iterator _allocateVoice(const AudioGroup& group, int groupId, double sampleRate,
bool dynamicPitch, bool emitter, bool dynamicPitch, bool emitter, ObjToken<Studio> studio);
std::weak_ptr<Studio> studio); std::list<ObjToken<Sequencer>>::iterator _allocateSequencer(const AudioGroup& group, int groupId,
std::list<std::shared_ptr<Sequencer>>::iterator _allocateSequencer(const AudioGroup& group, int groupId, int setupId, ObjToken<Studio> studio);
int setupId, std::weak_ptr<Studio> studio); ObjToken<Studio> _allocateStudio(bool mainOut);
std::shared_ptr<Studio> _allocateStudio(bool mainOut); std::list<ObjToken<Voice>>::iterator _destroyVoice(std::list<ObjToken<Voice>>::iterator it);
std::list<std::shared_ptr<Voice>>::iterator _destroyVoice(std::list<std::shared_ptr<Voice>>::iterator it); std::list<ObjToken<Sequencer>>::iterator
std::list<std::shared_ptr<Sequencer>>::iterator _destroySequencer(std::list<ObjToken<Sequencer>>::iterator it);
_destroySequencer(std::list<std::shared_ptr<Sequencer>>::iterator it);
void _bringOutYourDead(); void _bringOutYourDead();
public: public:
@ -84,48 +82,46 @@ public:
void removeAudioGroup(const AudioGroupData& data); void removeAudioGroup(const AudioGroupData& data);
/** Access engine's default studio */ /** Access engine's default studio */
std::shared_ptr<Studio> getDefaultStudio() { return m_defaultStudio; } ObjToken<Studio> getDefaultStudio() { return m_defaultStudio; }
/** Create new Studio within engine */ /** Create new Studio within engine */
std::shared_ptr<Studio> addStudio(bool mainOut); ObjToken<Studio> addStudio(bool mainOut);
/** Start soundFX playing from loaded audio groups */ /** Start soundFX playing from loaded audio groups */
std::shared_ptr<Voice> fxStart(int sfxId, float vol, float pan, std::weak_ptr<Studio> smx); ObjToken<Voice> fxStart(int sfxId, float vol, float pan, ObjToken<Studio> smx);
std::shared_ptr<Voice> fxStart(int sfxId, float vol, float pan) ObjToken<Voice> fxStart(int sfxId, float vol, float pan)
{ {
return fxStart(sfxId, vol, pan, m_defaultStudio); return fxStart(sfxId, vol, pan, m_defaultStudio);
} }
/** Start SoundMacro node playing directly (for editor use) */ /** Start SoundMacro node playing directly (for editor use) */
std::shared_ptr<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, ObjToken<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key,
uint8_t vel, uint8_t mod, std::weak_ptr<Studio> smx); uint8_t vel, uint8_t mod, ObjToken<Studio> smx);
std::shared_ptr<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, ObjToken<Voice> macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key,
uint8_t vel, uint8_t mod) uint8_t vel, uint8_t mod)
{ {
return macroStart(group, id, key, vel, mod, m_defaultStudio); return macroStart(group, id, key, vel, mod, m_defaultStudio);
} }
/** Start soundFX playing from loaded audio groups, attach to positional emitter */ /** Start soundFX playing from loaded audio groups, attach to positional emitter */
std::shared_ptr<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff, ObjToken<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
int sfxId, float minVol, float maxVol, bool doppler, int sfxId, float minVol, float maxVol, bool doppler, ObjToken<Studio> smx);
std::weak_ptr<Studio> smx); ObjToken<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
std::shared_ptr<Emitter> addEmitter(const float* pos, const float* dir, float maxDist, float falloff, int sfxId, float minVol, float maxVol, bool doppler)
int sfxId, float minVol, float maxVol, bool doppler)
{ {
return addEmitter(pos, dir, maxDist, falloff, sfxId, minVol, maxVol, doppler, m_defaultStudio); return addEmitter(pos, dir, maxDist, falloff, sfxId, minVol, maxVol, doppler, m_defaultStudio);
} }
/** Build listener and add to engine's listener list */ /** Build listener and add to engine's listener list */
std::shared_ptr<Listener> addListener(const float* pos, const float* dir, const float* heading, const float* up, ObjToken<Listener> addListener(const float* pos, const float* dir, const float* heading, const float* up,
float frontDiff, float backDiff, float soundSpeed, float volume); float frontDiff, float backDiff, float soundSpeed, float volume);
/** Remove listener from engine's listener list */ /** Remove listener from engine's listener list */
void removeListener(Listener* listener); void removeListener(Listener* listener);
/** Start song playing from loaded audio groups */ /** Start song playing from loaded audio groups */
std::shared_ptr<Sequencer> seqPlay(int groupId, int songId, const unsigned char* arrData, ObjToken<Sequencer> seqPlay(int groupId, int songId, const unsigned char* arrData, ObjToken<Studio> smx);
std::weak_ptr<Studio> smx); ObjToken<Sequencer> seqPlay(int groupId, int songId, const unsigned char* arrData)
std::shared_ptr<Sequencer> seqPlay(int groupId, int songId, const unsigned char* arrData)
{ {
return seqPlay(groupId, songId, arrData, m_defaultStudio); return seqPlay(groupId, songId, arrData, m_defaultStudio);
} }
@ -134,7 +130,7 @@ public:
void setVolume(float vol); void setVolume(float vol);
/** Find voice from VoiceId */ /** Find voice from VoiceId */
std::shared_ptr<Voice> findVoice(int vid); ObjToken<Voice> findVoice(int vid);
/** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */ /** Stop all voices in `kg`, stops immediately (no KeyOff) when `now` set */
void killKeygroup(uint8_t kg, bool now); void killKeygroup(uint8_t kg, bool now);
@ -146,10 +142,10 @@ public:
uint32_t nextRandom() { return m_random(); } uint32_t nextRandom() { return m_random(); }
/** Obtain list of active voices */ /** Obtain list of active voices */
std::list<std::shared_ptr<Voice>>& getActiveVoices() { return m_activeVoices; } std::list<ObjToken<Voice>>& getActiveVoices() { return m_activeVoices; }
/** Obtain list of active sequencers */ /** Obtain list of active sequencers */
std::list<std::shared_ptr<Sequencer>>& getActiveSequencers() { return m_activeSequencers; } std::list<ObjToken<Sequencer>>& getActiveSequencers() { return m_activeSequencers; }
/** All mixing occurs in virtual 5ms intervals; /** All mixing occurs in virtual 5ms intervals;
* this is called at the start of each interval for all mixable entities */ * this is called at the start of each interval for all mixable entities */

View File

@ -45,6 +45,7 @@ public:
const AudioGroup& getAudioGroup() const { return m_audioGroup; } const AudioGroup& getAudioGroup() const { return m_audioGroup; }
int getGroupId() const { return m_groupId; } int getGroupId() const { return m_groupId; }
ObjectId getObjectId() const { return m_objectId; } ObjectId getObjectId() const { return m_objectId; }
bool isDestroyed() const { return m_destroyed; }
}; };
} }

View File

@ -4,6 +4,8 @@
#include "Entity.hpp" #include "Entity.hpp"
#include "AudioGroupProject.hpp" #include "AudioGroupProject.hpp"
#include "SongState.hpp" #include "SongState.hpp"
#include "Studio.hpp"
#include "Voice.hpp"
#include <unordered_map> #include <unordered_map>
#include <unordered_set> #include <unordered_set>
#include <memory> #include <memory>
@ -11,8 +13,6 @@
namespace amuse namespace amuse
{ {
class Studio;
class Voice;
/** State of sequencer over lifetime */ /** State of sequencer over lifetime */
enum class SequencerState enum class SequencerState
@ -30,7 +30,7 @@ class Sequencer : public Entity
const SongGroupIndex::MIDISetup* m_midiSetup = nullptr; /**< Selected MIDI setup (may be null) */ const SongGroupIndex::MIDISetup* m_midiSetup = nullptr; /**< Selected MIDI setup (may be null) */
const SFXGroupIndex* m_sfxGroup = nullptr; /**< SFX Groups are alternatively referenced here */ const SFXGroupIndex* m_sfxGroup = nullptr; /**< SFX Groups are alternatively referenced here */
std::vector<const SFXGroupIndex::SFXEntry*> m_sfxMappings; /**< SFX entries are mapped to MIDI keys this via this */ std::vector<const SFXGroupIndex::SFXEntry*> m_sfxMappings; /**< SFX entries are mapped to MIDI keys this via this */
std::shared_ptr<Studio> m_studio; /**< Studio this sequencer outputs to */ ObjToken<Studio> m_studio; /**< Studio this sequencer outputs to */
const unsigned char* m_arrData = nullptr; /**< Current playing arrangement data */ const unsigned char* m_arrData = nullptr; /**< Current playing arrangement data */
SongState m_songState; /**< State of current arrangement playback */ SongState m_songState; /**< State of current arrangement playback */
@ -58,9 +58,9 @@ class Sequencer : public Entity
operator bool() const { return m_parent != nullptr; } operator bool() const { return m_parent != nullptr; }
/** Voices corresponding to currently-pressed keys in channel */ /** Voices corresponding to currently-pressed keys in channel */
std::unordered_map<uint8_t, std::shared_ptr<Voice>> m_chanVoxs; std::unordered_map<uint8_t, ObjToken<Voice>> m_chanVoxs;
std::unordered_set<std::shared_ptr<Voice>> m_keyoffVoxs; std::unordered_set<ObjToken<Voice>> m_keyoffVoxs;
std::weak_ptr<Voice> m_lastVoice; ObjToken<Voice> m_lastVoice;
int8_t m_ctrlVals[128] = {}; /**< MIDI controller values */ int8_t m_ctrlVals[128] = {}; /**< MIDI controller values */
float m_curPitchWheel = 0.f; /**< MIDI pitch-wheel */ float m_curPitchWheel = 0.f; /**< MIDI pitch-wheel */
int8_t m_curProgram = 0; /**< MIDI program number */ int8_t m_curProgram = 0; /**< MIDI program number */
@ -69,7 +69,7 @@ class Sequencer : public Entity
void _bringOutYourDead(); void _bringOutYourDead();
size_t getVoiceCount() const; size_t getVoiceCount() const;
std::shared_ptr<Voice> keyOn(uint8_t note, uint8_t velocity); ObjToken<Voice> keyOn(uint8_t note, uint8_t velocity);
void keyOff(uint8_t note, uint8_t velocity); void keyOff(uint8_t note, uint8_t velocity);
void setCtrlValue(uint8_t ctrl, int8_t val); void setCtrlValue(uint8_t ctrl, int8_t val);
bool programChange(int8_t prog); bool programChange(int8_t prog);
@ -80,7 +80,7 @@ class Sequencer : public Entity
void setPan(float pan); void setPan(float pan);
void allOff(); void allOff();
void killKeygroup(uint8_t kg, bool now); void killKeygroup(uint8_t kg, bool now);
std::shared_ptr<Voice> findVoice(int vid); ObjToken<Voice> findVoice(int vid);
void sendMacroMessage(ObjectId macroId, int32_t val); void sendMacroMessage(ObjectId macroId, int32_t val);
}; };
std::array<ChannelState, 16> m_chanStates; /**< Lazily-allocated channel states */ std::array<ChannelState, 16> m_chanStates; /**< Lazily-allocated channel states */
@ -91,15 +91,15 @@ class Sequencer : public Entity
public: public:
~Sequencer(); ~Sequencer();
Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SongGroupIndex* songGroup, int setupId, Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SongGroupIndex* songGroup, int setupId,
std::weak_ptr<Studio> studio); ObjToken<Studio> studio);
Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SFXGroupIndex* sfxGroup, Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SFXGroupIndex* sfxGroup,
std::weak_ptr<Studio> studio); ObjToken<Studio> studio);
/** Advance current song data (if any) */ /** Advance current song data (if any) */
void advance(double dt); void advance(double dt);
/** Obtain pointer to Sequencer's Submix */ /** Obtain pointer to Sequencer's Submix */
std::shared_ptr<Studio> getStudio() { return m_studio; } ObjToken<Studio> getStudio() { return m_studio; }
/** Get current state of sequencer */ /** Get current state of sequencer */
SequencerState state() const { return m_state; } SequencerState state() const { return m_state; }
@ -108,7 +108,7 @@ public:
size_t getVoiceCount() const; size_t getVoiceCount() const;
/** Register key press with voice set */ /** Register key press with voice set */
std::shared_ptr<Voice> keyOn(uint8_t chan, uint8_t note, uint8_t velocity); ObjToken<Voice> keyOn(uint8_t chan, uint8_t note, uint8_t velocity);
/** Register key release with voice set */ /** Register key release with voice set */
void keyOff(uint8_t chan, uint8_t note, uint8_t velocity); void keyOff(uint8_t chan, uint8_t note, uint8_t velocity);
@ -129,7 +129,7 @@ public:
void killKeygroup(uint8_t kg, bool now); void killKeygroup(uint8_t kg, bool now);
/** Find voice instance contained within Sequencer */ /** Find voice instance contained within Sequencer */
std::shared_ptr<Voice> findVoice(int vid); ObjToken<Voice> findVoice(int vid);
/** Send all voices using `macroId` the message `val` */ /** Send all voices using `macroId` the message `val` */
void sendMacroMessage(ObjectId macroId, int32_t val); void sendMacroMessage(ObjectId macroId, int32_t val);

View File

@ -4,7 +4,6 @@
#include <memory> #include <memory>
#include <list> #include <list>
#include "Entity.hpp" #include "Entity.hpp"
#include "Voice.hpp"
#include "Submix.hpp" #include "Submix.hpp"
#include <type_traits> #include <type_traits>
@ -20,11 +19,11 @@ class Studio
Submix m_auxB; Submix m_auxB;
struct StudioSend struct StudioSend
{ {
std::shared_ptr<Studio> m_targetStudio; ObjToken<Studio> m_targetStudio;
float m_dryLevel; float m_dryLevel;
float m_auxALevel; float m_auxALevel;
float m_auxBLevel; float m_auxBLevel;
StudioSend(std::weak_ptr<Studio> studio, float dry, float auxA, float auxB) StudioSend(ObjToken<Studio> studio, float dry, float auxA, float auxB)
: m_targetStudio(studio), m_dryLevel(dry), m_auxALevel(auxA), m_auxBLevel(auxB) : m_targetStudio(studio), m_dryLevel(dry), m_auxALevel(auxA), m_auxBLevel(auxB)
{ {
} }
@ -38,7 +37,7 @@ public:
Studio(Engine& engine, bool mainOut); Studio(Engine& engine, bool mainOut);
/** Register a target Studio to send this Studio's mixing busses */ /** Register a target Studio to send this Studio's mixing busses */
void addStudioSend(std::weak_ptr<Studio> studio, float dry, float auxA, float auxB); void addStudioSend(ObjToken<Studio> studio, float dry, float auxA, float auxB);
/** Advise submixes of changing sample rate */ /** Advise submixes of changing sample rate */
void resetOutputSampleRate(double sampleRate); void resetOutputSampleRate(double sampleRate);

View File

@ -10,11 +10,11 @@
#include "AudioGroupSampleDirectory.hpp" #include "AudioGroupSampleDirectory.hpp"
#include "AudioGroup.hpp" #include "AudioGroup.hpp"
#include "Envelope.hpp" #include "Envelope.hpp"
#include "Studio.hpp"
namespace amuse namespace amuse
{ {
class IBackendVoice; class IBackendVoice;
class Studio;
struct Keymap; struct Keymap;
struct LayerMapping; struct LayerMapping;
@ -47,7 +47,7 @@ class Voice : public Entity
int m_vid; /**< VoiceID of this voice instance */ int m_vid; /**< VoiceID of this voice instance */
bool m_emitter; /**< Voice is part of an Emitter */ bool m_emitter; /**< Voice is part of an Emitter */
std::shared_ptr<Studio> m_studio; /**< Studio this voice outputs to */ ObjToken<Studio> m_studio; /**< Studio this voice outputs to */
std::unique_ptr<IBackendVoice> m_backendVoice; /**< Handle to client-implemented backend voice */ std::unique_ptr<IBackendVoice> m_backendVoice; /**< Handle to client-implemented backend voice */
SoundMacroState m_state; /**< State container for SoundMacro playback */ 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_sampleEndTrap; /**< Trap for sampleend (SoundMacro overrides voice removal) */
SoundMacroState::EventTrap m_messageTrap; /**< Trap for messages sent from other SoundMacros */ SoundMacroState::EventTrap m_messageTrap; /**< Trap for messages sent from other SoundMacros */
int32_t m_latestMessage = 0; /**< Latest message received on voice */ int32_t m_latestMessage = 0; /**< Latest message received on voice */
std::list<std::shared_ptr<Voice>> m_childVoices; /**< Child voices for PLAYMACRO usage */ std::list<ObjToken<Voice>> m_childVoices; /**< Child voices for PLAYMACRO usage */
uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */ uint8_t m_keygroup = 0; /**< Keygroup voice is a member of */
const AudioGroupSampleDirectory::Entry* m_curSample = nullptr; /**< Current sample entry playing */ ObjToken<SampleEntryData> m_curSample; /**< Current sample entry playing */
const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */ const unsigned char* m_curSampleData = nullptr; /**< Current sample data playing */
SampleFormat m_curFormat; /**< Current sample format playing */ SampleFormat m_curFormat; /**< Current sample format playing */
uint32_t m_curSamplePos = 0; /**< Current sample position */ 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_curVol = 1.f; /**< Current volume of voice */
float m_curReverbVol = 0.f; /**< Current reverb 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_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_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_curSpan = -1.f; /**< Current surround pan of voice */
float m_curPitchWheel = 0.f; /**< Current normalized wheel value for control */ float m_curPitchWheel = 0.f; /**< Current normalized wheel value for control */
int32_t m_pitchWheelUp = 600; /**< Up range for pitchwheel control in cents */ int32_t m_pitchWheelUp = 600; /**< Up range for pitchwheel control in cents */
@ -156,11 +154,11 @@ class Voice : public Entity
bool _isRecursivelyDead(); bool _isRecursivelyDead();
void _bringOutYourDead(); void _bringOutYourDead();
static uint32_t _GetBlockSampleCount(SampleFormat fmt); static uint32_t _GetBlockSampleCount(SampleFormat fmt);
std::shared_ptr<Voice> _findVoice(int vid, std::weak_ptr<Voice> thisPtr); ObjToken<Voice> _findVoice(int vid, ObjToken<Voice> thisPtr);
std::unique_ptr<int8_t[]>& _ensureCtrlVals(); std::unique_ptr<int8_t[]>& _ensureCtrlVals();
std::list<std::shared_ptr<Voice>>::iterator _allocateVoice(double sampleRate, bool dynamicPitch); std::list<ObjToken<Voice>>::iterator _allocateVoice(double sampleRate, bool dynamicPitch);
std::list<std::shared_ptr<Voice>>::iterator _destroyVoice(std::list<std::shared_ptr<Voice>>::iterator it); std::list<ObjToken<Voice>>::iterator _destroyVoice(std::list<ObjToken<Voice>>::iterator it);
bool _loadSoundMacro(SoundMacroId id, const SoundMacro* macroData, int macroStep, double ticksPerSec, bool _loadSoundMacro(SoundMacroId id, const SoundMacro* macroData, int macroStep, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false); 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); uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
bool _loadLayer(const std::vector<LayerMapping>& layer, double ticksPerSec, bool _loadLayer(const std::vector<LayerMapping>& layer, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false); uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
std::shared_ptr<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, ObjToken<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey,
uint8_t midiVel, uint8_t midiMod, bool pushPc = false); uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
void _panLaw(float coefsOut[8], float frontPan, float backPan, float totalSpan) const; void _panLaw(float coefsOut[8], float frontPan, float backPan, float totalSpan) const;
void _setPan(float pan); void _setPan(float pan);
@ -180,9 +178,9 @@ class Voice : public Entity
public: public:
~Voice(); ~Voice();
Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, std::weak_ptr<Studio> studio); Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, ObjToken<Studio> studio);
Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter, Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter,
std::weak_ptr<Studio> studio); ObjToken<Studio> studio);
/** Called before each supplyAudio invocation to prepare voice /** Called before each supplyAudio invocation to prepare voice
* backend for possible parameter updates */ * backend for possible parameter updates */
@ -199,7 +197,7 @@ public:
void routeAudio(size_t frames, double dt, int busId, float* in, float* out); void routeAudio(size_t frames, double dt, int busId, float* in, float* out);
/** Obtain pointer to Voice's Studio */ /** Obtain pointer to Voice's Studio */
std::shared_ptr<Studio> getStudio() { return m_studio; } ObjToken<Studio> getStudio() { return m_studio; }
/** Get current state of voice */ /** Get current state of voice */
VoiceState state() const { return m_voxState; } VoiceState state() const { return m_voxState; }
@ -211,7 +209,7 @@ public:
int maxVid() const; int maxVid() const;
/** Allocate parallel macro and tie to voice for possible emitter influence */ /** Allocate parallel macro and tie to voice for possible emitter influence */
std::shared_ptr<Voice> startChildMacro(int8_t addNote, ObjectId macroId, int macroStep); ObjToken<Voice> startChildMacro(int8_t addNote, ObjectId macroId, int macroStep);
/** Load specified SoundMacro Object from within group into voice */ /** Load specified SoundMacro Object from within group into voice */
bool loadMacroObject(SoundMacroId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel, bool loadMacroObject(SoundMacroId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, uint8_t midiVel,

View File

@ -21,17 +21,18 @@ void AudioGroup::assign(SystemStringView groupPath)
m_samp = nullptr; 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); auto search = m_sdir.m_entries.find(sfxId);
if (search == m_sdir.m_entries.cend()) if (search == m_sdir.m_entries.cend())
return nullptr; return nullptr;
return &search->second; return search->second.get();
} }
const unsigned char* AudioGroup::getSampleData(SampleId sfxId, const AudioGroupSampleDirectory::Entry* sample) const std::pair<ObjToken<SampleEntryData>, const unsigned char*>
AudioGroup::getSampleData(SampleId sfxId, const SampleEntry* sample) const
{ {
if (sample->m_looseData) if (sample->m_data->m_looseData)
{ {
setIdDatabases(); setIdDatabases();
#if _WIN32 #if _WIN32
@ -41,9 +42,9 @@ const unsigned char* AudioGroup::getSampleData(SampleId sfxId, const AudioGroupS
SystemString basePath = m_groupPath + _S('/') + SystemString basePath = m_groupPath + _S('/') +
SampleId::CurNameDB->resolveNameFromId(sfxId).data(); SampleId::CurNameDB->resolveNameFromId(sfxId).data();
#endif #endif
const_cast<AudioGroupSampleDirectory::Entry*>(sample)->loadLooseData(basePath); const_cast<SampleEntry*>(sample)->loadLooseData(basePath);
return sample->m_looseData.get(); return {sample->m_data, sample->m_data->m_looseData.get()};
} }
return m_samp + sample->m_sampleOff; return {{}, m_samp + sample->m_data->m_sampleOff};
} }
} }

View File

@ -109,7 +109,7 @@ AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r)
atInt64 startPos = r.position(); atInt64 startPos = r.position();
objHead.read(r); objHead.read(r);
auto& macro = ret.m_soundMacros[objHead.objectId.id]; auto& macro = ret.m_soundMacros[objHead.objectId.id];
macro = std::make_shared<SoundMacro>(); macro = MakeObj<SoundMacro>();
macro->template readCmds<DNAE>(r, objHead.size - 8); macro->template readCmds<DNAE>(r, objHead.size - 8);
r.seek(startPos + objHead.size, athena::Begin); r.seek(startPos + objHead.size, athena::Begin);
} }
@ -127,17 +127,17 @@ AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r)
switch (objHead.size) switch (objHead.size)
{ {
case 0x10: case 0x10:
ptr = std::make_shared<ADSR>(); ptr = MakeObj<std::unique_ptr<ITable>>(std::make_unique<ADSR>());
static_cast<ADSR&>(*ptr).read(r); static_cast<ADSR&>(**ptr).read(r);
break; break;
case 0x1c: case 0x1c:
ptr = std::make_shared<ADSRDLS>(); ptr = MakeObj<std::unique_ptr<ITable>>(std::make_unique<ADSRDLS>());
static_cast<ADSRDLS&>(*ptr).read(r); static_cast<ADSRDLS&>(**ptr).read(r);
break; break;
default: default:
ptr = std::make_shared<Curve>(); ptr = MakeObj<std::unique_ptr<ITable>>(std::make_unique<Curve>());
static_cast<Curve&>(*ptr).data.resize(objHead.size - 8); static_cast<Curve&>(**ptr).data.resize(objHead.size - 8);
r.readUBytesToBuf(&static_cast<Curve&>(*ptr).data[0], objHead.size - 8); r.readUBytesToBuf(&static_cast<Curve&>(**ptr).data[0], objHead.size - 8);
break; break;
} }
r.seek(startPos + objHead.size, athena::Begin); r.seek(startPos + objHead.size, athena::Begin);
@ -155,7 +155,7 @@ AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r)
KeymapDNA<DNAE> kmData; KeymapDNA<DNAE> kmData;
kmData.read(r); kmData.read(r);
auto& km = ret.m_keymaps[objHead.objectId.id]; auto& km = ret.m_keymaps[objHead.objectId.id];
km = std::make_shared<Keymap>(kmData); km = MakeObj<Keymap>(kmData);
r.seek(startPos + objHead.size, athena::Begin); r.seek(startPos + objHead.size, athena::Begin);
} }
} }
@ -169,7 +169,7 @@ AudioGroupPool AudioGroupPool::_AudioGroupPool(athena::io::IStreamReader& r)
atInt64 startPos = r.position(); atInt64 startPos = r.position();
objHead.read(r); objHead.read(r);
auto& lm = ret.m_layers[objHead.objectId.id]; auto& lm = ret.m_layers[objHead.objectId.id];
lm = std::make_shared<std::vector<LayerMapping>>(); lm = MakeObj<std::vector<LayerMapping>>();
uint32_t count; uint32_t count;
athena::io::Read<athena::io::PropType::None>::Do<decltype(count), DNAE>({}, count, r); athena::io::Read<athena::io::PropType::None>::Do<decltype(count), DNAE>({}, count, r);
lm->reserve(count); lm->reserve(count);
@ -262,7 +262,7 @@ AudioGroupPool AudioGroupPool::CreateAudioGroupPool(SystemStringView groupPath)
for (const auto& sm : r.getCurNode()->m_mapChildren) for (const auto& sm : r.getCurNode()->m_mapChildren)
{ {
auto& smOut = ret.m_soundMacros[SoundMacroId::CurNameDB->resolveIdFromName(sm.first)]; auto& smOut = ret.m_soundMacros[SoundMacroId::CurNameDB->resolveIdFromName(sm.first)];
smOut = std::make_shared<SoundMacro>(); smOut = MakeObj<SoundMacro>();
size_t cmdCount; size_t cmdCount;
if (auto __v = r.enterSubVector(sm.first.c_str(), 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")) if (auto __vta = r.enterSubRecord("velToAttack"))
{ {
__vta.leave(); __vta.leave();
tableOut = std::make_shared<ADSRDLS>(); tableOut = MakeObj<std::unique_ptr<ITable>>(std::make_unique<ADSRDLS>());
static_cast<ADSRDLS&>(*tableOut).read(r); static_cast<ADSRDLS&>(**tableOut).read(r);
} }
else else
{ {
tableOut = std::make_shared<ADSR>(); tableOut = MakeObj<std::unique_ptr<ITable>>(std::make_unique<ADSR>());
static_cast<ADSR&>(*tableOut).read(r); static_cast<ADSR&>(**tableOut).read(r);
} }
} }
else if (auto __dat = r.enterSubRecord("data")) else if (auto __dat = r.enterSubRecord("data"))
{ {
__dat.leave(); __dat.leave();
tableOut = std::make_shared<Curve>(); tableOut = MakeObj<std::unique_ptr<ITable>>(std::make_unique<Curve>());
static_cast<Curve&>(*tableOut).read(r); static_cast<Curve&>(**tableOut).read(r);
} }
} }
} }
@ -314,7 +314,7 @@ AudioGroupPool AudioGroupPool::CreateAudioGroupPool(SystemStringView groupPath)
if (auto __v = r.enterSubRecord(k.first.c_str())) if (auto __v = r.enterSubRecord(k.first.c_str()))
{ {
auto& kmOut = ret.m_keymaps[KeymapId::CurNameDB->resolveIdFromName(k.first)]; auto& kmOut = ret.m_keymaps[KeymapId::CurNameDB->resolveIdFromName(k.first)];
kmOut = std::make_shared<Keymap>(); kmOut = MakeObj<Keymap>();
kmOut->read(r); kmOut->read(r);
} }
} }
@ -328,7 +328,7 @@ AudioGroupPool AudioGroupPool::CreateAudioGroupPool(SystemStringView groupPath)
if (auto __v = r.enterSubVector(l.first.c_str(), mappingCount)) if (auto __v = r.enterSubVector(l.first.c_str(), mappingCount))
{ {
auto& layOut = ret.m_layers[LayersId::CurNameDB->resolveIdFromName(l.first)]; auto& layOut = ret.m_layers[LayersId::CurNameDB->resolveIdFromName(l.first)];
layOut = std::make_shared<std::vector<LayerMapping>>(); layOut = MakeObj<std::vector<LayerMapping>>();
layOut->reserve(mappingCount); layOut->reserve(mappingCount);
for (int lm = 0; lm < mappingCount; ++lm) for (int lm = 0; lm < mappingCount; ++lm)
{ {
@ -402,25 +402,25 @@ const std::vector<LayerMapping>* AudioGroupPool::layer(ObjectId id) const
const ADSR* AudioGroupPool::tableAsAdsr(ObjectId id) const const ADSR* AudioGroupPool::tableAsAdsr(ObjectId id) const
{ {
auto search = m_tables.find(id); 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 nullptr;
return static_cast<const ADSR*>(search->second.get()); return static_cast<const ADSR*>((*search->second).get());
} }
const ADSRDLS* AudioGroupPool::tableAsAdsrDLS(ObjectId id) const const ADSRDLS* AudioGroupPool::tableAsAdsrDLS(ObjectId id) const
{ {
auto search = m_tables.find(id); 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 nullptr;
return static_cast<const ADSRDLS*>(search->second.get()); return static_cast<const ADSRDLS*>((*search->second).get());
} }
const Curve* AudioGroupPool::tableAsCurves(ObjectId id) const const Curve* AudioGroupPool::tableAsCurves(ObjectId id) const
{ {
auto search = m_tables.find(id); 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 nullptr;
return static_cast<const Curve*>(search->second.get()); return static_cast<const Curve*>((*search->second).get());
} }
static SoundMacro::CmdOp _ReadCmdOp(athena::io::MemoryReader& r) 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())) if (auto __v = w.enterSubRecord(TableId::CurNameDB->resolveNameFromId(p.first).data()))
{ {
w.setStyle(athena::io::YAMLNodeStyle::Flow); w.setStyle(athena::io::YAMLNodeStyle::Flow);
p.second.get()->write(w); (*p.second.get())->write(w);
} }
} }
} }

View File

@ -81,7 +81,7 @@ AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag)
if (header.type == GroupType::Song) if (header.type == GroupType::Song)
{ {
auto& idx = m_songGroups[header.groupId]; auto& idx = m_songGroups[header.groupId];
idx = std::make_shared<SongGroupIndex>(); idx = MakeObj<SongGroupIndex>();
/* Normal pages */ /* Normal pages */
r.seek(header.pageTableOff, athena::Begin); r.seek(header.pageTableOff, athena::Begin);
@ -116,7 +116,7 @@ AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag)
else if (header.type == GroupType::SFX) else if (header.type == GroupType::SFX)
{ {
auto& idx = m_sfxGroups[header.groupId]; auto& idx = m_sfxGroups[header.groupId];
idx = std::make_shared<SFXGroupIndex>(); idx = MakeObj<SFXGroupIndex>();
/* SFX entries */ /* SFX entries */
r.seek(header.pageTableOff, athena::Begin); r.seek(header.pageTableOff, athena::Begin);
@ -179,7 +179,7 @@ AudioGroupProject AudioGroupProject::_AudioGroupProject(athena::io::IStreamReade
if (header.type == GroupType::Song) if (header.type == GroupType::Song)
{ {
auto& idx = ret.m_songGroups[header.groupId]; auto& idx = ret.m_songGroups[header.groupId];
idx = std::make_shared<SongGroupIndex>(); idx = MakeObj<SongGroupIndex>();
if (absOffs) if (absOffs)
{ {
@ -255,7 +255,7 @@ AudioGroupProject AudioGroupProject::_AudioGroupProject(athena::io::IStreamReade
else if (header.type == GroupType::SFX) else if (header.type == GroupType::SFX)
{ {
auto& idx = ret.m_sfxGroups[header.groupId]; auto& idx = ret.m_sfxGroups[header.groupId];
idx = std::make_shared<SFXGroupIndex>(); idx = MakeObj<SFXGroupIndex>();
/* SFX entries */ /* SFX entries */
r.seek(subDataOff + header.pageTableOff, athena::Begin); r.seek(subDataOff + header.pageTableOff, athena::Begin);
@ -333,7 +333,7 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView gr
GroupId::CurNameDB->registerPair(groupName, groupId); GroupId::CurNameDB->registerPair(groupName, groupId);
auto& idx = ret.m_songGroups[groupId]; auto& idx = ret.m_songGroups[groupId];
idx = std::make_shared<SongGroupIndex>(); idx = MakeObj<SongGroupIndex>();
if (auto __v2 = r.enterSubRecord("normPages")) if (auto __v2 = r.enterSubRecord("normPages"))
{ {
idx->m_normPages.reserve(r.getCurNode()->m_mapChildren.size()); idx->m_normPages.reserve(r.getCurNode()->m_mapChildren.size());
@ -387,7 +387,7 @@ AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView gr
GroupId::CurNameDB->registerPair(groupName, groupId); GroupId::CurNameDB->registerPair(groupName, groupId);
auto& idx = ret.m_sfxGroups[groupId]; auto& idx = ret.m_sfxGroups[groupId];
idx = std::make_shared<SFXGroupIndex>(); idx = MakeObj<SFXGroupIndex>();
for (const auto& sfx : r.getCurNode()->m_mapChildren) for (const auto& sfx : r.getCurNode()->m_mapChildren)
if (auto __r2 = r.enterSubRecord(sfx.first.c_str())) if (auto __r2 = r.enterSubRecord(sfx.first.c_str()))
{ {

View File

@ -44,17 +44,17 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
{ {
EntryDNA<athena::Big> ent; EntryDNA<athena::Big> ent;
ent.read(r); ent.read(r);
m_entries[ent.m_sfxId] = ent; m_entries[ent.m_sfxId] = MakeObj<Entry>(ent);
SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId); SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId);
} }
for (auto& p : m_entries) 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.seek(p.second->m_data->m_adpcmParmOffset, athena::Begin);
r.readUBytesToBuf(&p.second.m_ADPCMParms, sizeof(ADPCMParms::DSPParms)); r.readUBytesToBuf(&p.second->m_data->m_ADPCMParms, sizeof(ADPCMParms::DSPParms));
p.second.m_ADPCMParms.swapBigDSP(); p.second->m_data->m_ADPCMParms.swapBigDSP();
} }
} }
} }
@ -68,7 +68,7 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
{ {
MusyX1AbsSdirEntry<athena::Big> ent; MusyX1AbsSdirEntry<athena::Big> ent;
ent.read(r); ent.read(r);
m_entries[ent.m_sfxId] = ent; m_entries[ent.m_sfxId] = MakeObj<Entry>(ent);
SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId); SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId);
} }
} }
@ -78,15 +78,15 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
{ {
MusyX1SdirEntry<athena::Big> ent; MusyX1SdirEntry<athena::Big> ent;
ent.read(r); ent.read(r);
m_entries[ent.m_sfxId] = ent; m_entries[ent.m_sfxId] = MakeObj<Entry>(ent);
SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId); SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId);
} }
} }
for (auto& p : m_entries) for (auto& p : m_entries)
{ {
memcpy(&p.second.m_ADPCMParms, sampData + p.second.m_sampleOff, sizeof(ADPCMParms::VADPCMParms)); memcpy(&p.second->m_data->m_ADPCMParms, sampData + p.second->m_data->m_sampleOff, sizeof(ADPCMParms::VADPCMParms));
p.second.m_ADPCMParms.swapBigVADPCM(); p.second->m_data->m_ADPCMParms.swapBigVADPCM();
} }
} }
@ -98,9 +98,9 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
{ {
MusyX1AbsSdirEntry<athena::Little> ent; MusyX1AbsSdirEntry<athena::Little> ent;
ent.read(r); ent.read(r);
Entry& store = m_entries[ent.m_sfxId]; auto& store = m_entries[ent.m_sfxId];
store = ent; store = MakeObj<Entry>(ent);
store.m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24; store->m_data->m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24;
SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId); SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId);
} }
} }
@ -110,9 +110,9 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
{ {
MusyX1SdirEntry<athena::Little> ent; MusyX1SdirEntry<athena::Little> ent;
ent.read(r); ent.read(r);
Entry& store = m_entries[ent.m_sfxId]; auto& store = m_entries[ent.m_sfxId];
store = ent; store = MakeObj<Entry>(ent);
store.m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24; store->m_data->m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24;
SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId); 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; 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) void AudioGroupSampleDirectory::Entry::loadLooseData(SystemStringView basePath)
{ {
SystemString wavPath = SystemString(basePath) + _S(".wav"); SystemString wavPath = SystemString(basePath) + _S(".wav");
@ -165,88 +247,19 @@ void AudioGroupSampleDirectory::Entry::loadLooseData(SystemStringView basePath)
wavValid = false; 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); m_data = MakeObj<EntryData>();
if (!r.hasError()) m_data->loadLooseDSP(dspPath);
{ m_data->m_looseModTime = dspStat.st_mtime;
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;
}
} }
else if (wavValid && (!curData.m_looseData || wavStat.st_mtime > curData.m_looseModTime))
if (wavValid && (!m_looseData || wavStat.st_mtime > m_looseModTime))
{ {
athena::io::FileReader r(wavPath); m_data = MakeObj<EntryData>();
if (!r.hasError()) m_data->loadLooseWAV(wavPath);
{ m_data->m_looseModTime = wavStat.st_mtime;
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;
}
} }
} }
@ -273,15 +286,16 @@ AudioGroupSampleDirectory AudioGroupSampleDirectory::CreateAudioGroupSampleDirec
SampleId::CurNameDB->registerPair(baseName, sampleId); SampleId::CurNameDB->registerPair(baseName, sampleId);
#endif #endif
Entry& entry = ret.m_entries[sampleId]; auto& entry = ret.m_entries[sampleId];
entry = MakeObj<Entry>();
SystemString basePath = SystemString(ent.m_path.begin(), ent.m_path.begin() + ent.m_path.size() - 4); SystemString basePath = SystemString(ent.m_path.begin(), ent.m_path.begin() + ent.m_path.size() - 4);
entry.loadLooseData(basePath); entry->loadLooseData(basePath);
} }
return ret; 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::SystemStringView destDir, const unsigned char* samp)
{ {
amuse::SystemString path(destDir); amuse::SystemString path(destDir);
@ -389,16 +403,16 @@ void AudioGroupSampleDirectory::extractWAV(SampleId id, amuse::SystemStringView
auto search = m_entries.find(id); auto search = m_entries.find(id);
if (search == m_entries.cend()) if (search == m_entries.cend())
return; 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 void AudioGroupSampleDirectory::extractAllWAV(amuse::SystemStringView destDir, const unsigned char* samp) const
{ {
for (const auto& ent : m_entries) 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) amuse::SystemStringView destDir, const unsigned char* samp)
{ {
SampleFormat fmt = SampleFormat(ent.m_numSamples >> 24); SampleFormat fmt = SampleFormat(ent.m_numSamples >> 24);
@ -475,13 +489,46 @@ void AudioGroupSampleDirectory::extractCompressed(SampleId id, amuse::SystemStri
auto search = m_entries.find(id); auto search = m_entries.find(id);
if (search == m_entries.cend()) if (search == m_entries.cend())
return; return;
_extractCompressed(id, search->second, destDir, samp); _extractCompressed(id, *search->second->m_data, destDir, samp);
} }
void AudioGroupSampleDirectory::extractAllCompressed(amuse::SystemStringView destDir, void AudioGroupSampleDirectory::extractAllCompressed(amuse::SystemStringView destDir,
const unsigned char* samp) const const unsigned char* samp) const
{ {
for (const auto& ent : m_entries) 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<Entry>();
SystemString basePath = SystemString(ent.m_path.begin(), ent.m_path.begin() + ent.m_path.size() - 4);
entry->loadLooseData(basePath);
}
}
} }
} }

View File

@ -174,7 +174,7 @@ void BooBackendMIDIReader::pumpReader(double dt)
void BooBackendMIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity) void BooBackendMIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
{ {
for (std::shared_ptr<Sequencer>& seq : m_engine.getActiveSequencers()) for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->keyOff(chan, key, velocity); seq->keyOff(chan, key, velocity);
#if 0 #if 0
openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON); 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) void BooBackendMIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity)
{ {
for (std::shared_ptr<Sequencer>& seq : m_engine.getActiveSequencers()) for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->keyOn(chan, key, velocity); seq->keyOn(chan, key, velocity);
#if 0 #if 0
openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON); 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) void BooBackendMIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value)
{ {
for (std::shared_ptr<Sequencer>& seq : m_engine.getActiveSequencers()) for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->setCtrlValue(chan, control, value); seq->setCtrlValue(chan, control, value);
} }
void BooBackendMIDIReader::programChange(uint8_t chan, uint8_t program) void BooBackendMIDIReader::programChange(uint8_t chan, uint8_t program)
{ {
for (std::shared_ptr<Sequencer>& seq : m_engine.getActiveSequencers()) for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->setChanProgram(chan, program); 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) void BooBackendMIDIReader::pitchBend(uint8_t chan, int16_t pitch)
{ {
for (std::shared_ptr<Sequencer>& seq : m_engine.getActiveSequencers()) for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->setPitchWheel(chan, (pitch - 0x2000) / float(0x2000)); seq->setPitchWheel(chan, (pitch - 0x2000) / float(0x2000));
} }
void BooBackendMIDIReader::allSoundOff(uint8_t chan) void BooBackendMIDIReader::allSoundOff(uint8_t chan)
{ {
for (std::shared_ptr<Sequencer>& seq : m_engine.getActiveSequencers()) for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->allOff(chan, true); seq->allOff(chan, true);
} }
@ -228,7 +228,7 @@ void BooBackendMIDIReader::localControl(uint8_t /*chan*/, bool /*on*/) {}
void BooBackendMIDIReader::allNotesOff(uint8_t chan) void BooBackendMIDIReader::allNotesOff(uint8_t chan)
{ {
for (std::shared_ptr<Sequencer>& seq : m_engine.getActiveSequencers()) for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->allOff(chan, false); seq->allOff(chan, false);
} }

View File

@ -15,7 +15,7 @@ static void Delta(Vector3f& out, const Vector3f& a, const Vector3f& b)
Emitter::~Emitter() {} Emitter::~Emitter() {}
Emitter::Emitter(Engine& engine, const AudioGroup& group, const std::shared_ptr<Voice>& vox, Emitter::Emitter(Engine& engine, const AudioGroup& group, ObjToken<Voice> vox,
float maxDist, float minVol, float falloff, bool doppler) float maxDist, float minVol, float falloff, bool doppler)
: Entity(engine, group, vox->getGroupId(), vox->getObjectId()), m_vox(vox), m_maxDist(maxDist), : 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) m_minVol(clamp(0.f, minVol, 1.f)), m_falloff(clamp(-1.f, falloff, 1.f)), m_doppler(doppler)

View File

@ -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() Engine::~Engine()
{ {
m_backend.setCallbackInterface(nullptr); m_backend.setCallbackInterface(nullptr);
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers) for (ObjToken<Sequencer>& seq : m_activeSequencers)
if (!seq->m_destroyed) if (!seq->m_destroyed)
seq->_destroy(); seq->_destroy();
for (std::shared_ptr<Emitter>& emitter : m_activeEmitters) for (ObjToken<Emitter>& emitter : m_activeEmitters)
emitter->_destroy(); emitter->_destroy();
for (std::shared_ptr<Voice>& vox : m_activeVoices) for (ObjToken<Voice>& vox : m_activeVoices)
vox->_destroy(); vox->_destroy();
} }
@ -57,51 +57,49 @@ std::pair<AudioGroup*, const SFXGroupIndex*> Engine::_findSFXGroup(int groupId)
return {}; return {};
} }
std::list<std::shared_ptr<Voice>>::iterator Engine::_allocateVoice(const AudioGroup& group, int groupId, std::list<ObjToken<Voice>>::iterator Engine::_allocateVoice(const AudioGroup& group, int groupId,
double sampleRate, bool dynamicPitch, bool emitter, double sampleRate, bool dynamicPitch, bool emitter,
std::weak_ptr<Studio> studio) ObjToken<Studio> studio)
{ {
std::shared_ptr<Studio> st = studio.lock();
auto it = auto it =
m_activeVoices.emplace(m_activeVoices.end(), new Voice(*this, group, groupId, m_nextVid++, emitter, studio)); m_activeVoices.emplace(m_activeVoices.end(), MakeObj<Voice>(*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 = 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(studio->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(studio->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->getAuxB().m_backendSubmix.get(), FullLevels, false);
return it; return it;
} }
std::list<std::shared_ptr<Sequencer>>::iterator Engine::_allocateSequencer(const AudioGroup& group, int groupId, std::list<ObjToken<Sequencer>>::iterator Engine::_allocateSequencer(const AudioGroup& group, int groupId,
int setupId, std::weak_ptr<Studio> studio) int setupId, ObjToken<Studio> studio)
{ {
const SongGroupIndex* songGroup = group.getProj().getSongGroupIndex(groupId); const SongGroupIndex* songGroup = group.getProj().getSongGroupIndex(groupId);
if (songGroup) if (songGroup)
{ {
auto it = m_activeSequencers.emplace(m_activeSequencers.end(), auto it = m_activeSequencers.emplace(m_activeSequencers.end(),
new Sequencer(*this, group, groupId, songGroup, setupId, studio)); MakeObj<Sequencer>(*this, group, groupId, songGroup, setupId, studio));
return it; return it;
} }
const SFXGroupIndex* sfxGroup = group.getProj().getSFXGroupIndex(groupId); const SFXGroupIndex* sfxGroup = group.getProj().getSFXGroupIndex(groupId);
if (sfxGroup) if (sfxGroup)
{ {
auto it = m_activeSequencers.emplace(m_activeSequencers.end(), auto it = m_activeSequencers.emplace(m_activeSequencers.end(),
new Sequencer(*this, group, groupId, sfxGroup, studio)); MakeObj<Sequencer>(*this, group, groupId, sfxGroup, studio));
return it; return it;
} }
return {}; return {};
} }
std::shared_ptr<Studio> Engine::_allocateStudio(bool mainOut) ObjToken<Studio> Engine::_allocateStudio(bool mainOut)
{ {
std::shared_ptr<Studio> ret = std::make_shared<Studio>(*this, mainOut); ObjToken<Studio> ret = MakeObj<Studio>(*this, mainOut);
m_activeStudios.emplace(m_activeStudios.end(), ret);
ret->m_master.m_backendSubmix = m_backend.allocateSubmix(ret->m_master, mainOut, 0); 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_auxA.m_backendSubmix = m_backend.allocateSubmix(ret->m_auxA, mainOut, 1);
ret->m_auxB.m_backendSubmix = m_backend.allocateSubmix(ret->m_auxB, mainOut, 2); ret->m_auxB.m_backendSubmix = m_backend.allocateSubmix(ret->m_auxB, mainOut, 2);
return ret; return ret;
} }
std::list<std::shared_ptr<Voice>>::iterator Engine::_destroyVoice(std::list<std::shared_ptr<Voice>>::iterator it) std::list<ObjToken<Voice>>::iterator Engine::_destroyVoice(std::list<ObjToken<Voice>>::iterator it)
{ {
assert(this == &(*it)->getEngine()); assert(this == &(*it)->getEngine());
if ((*it)->m_destroyed) if ((*it)->m_destroyed)
@ -110,8 +108,8 @@ std::list<std::shared_ptr<Voice>>::iterator Engine::_destroyVoice(std::list<std:
return m_activeVoices.erase(it); return m_activeVoices.erase(it);
} }
std::list<std::shared_ptr<Sequencer>>::iterator std::list<ObjToken<Sequencer>>::iterator
Engine::_destroySequencer(std::list<std::shared_ptr<Sequencer>>::iterator it) Engine::_destroySequencer(std::list<ObjToken<Sequencer>>::iterator it)
{ {
assert(this == &(*it)->getEngine()); assert(this == &(*it)->getEngine());
if ((*it)->m_destroyed) if ((*it)->m_destroyed)
@ -157,15 +155,6 @@ void Engine::_bringOutYourDead()
} }
++it; ++it;
} }
for (auto it = m_activeStudios.begin(); it != m_activeStudios.end();)
{
std::shared_ptr<Studio> st = it->lock();
if (!st)
it = m_activeStudios.erase(it);
else
++it;
}
} }
void Engine::_on5MsInterval(IBackendVoiceAllocator& engine, double dt) void Engine::_on5MsInterval(IBackendVoiceAllocator& engine, double dt)
@ -173,11 +162,11 @@ void Engine::_on5MsInterval(IBackendVoiceAllocator& engine, double dt)
m_channelSet = engine.getAvailableSet(); m_channelSet = engine.getAvailableSet();
if (m_midiReader) if (m_midiReader)
m_midiReader->pumpReader(dt); m_midiReader->pumpReader(dt);
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers) for (ObjToken<Sequencer>& seq : m_activeSequencers)
seq->advance(dt); seq->advance(dt);
for (std::shared_ptr<Emitter>& emitter : m_activeEmitters) for (ObjToken<Emitter>& emitter : m_activeEmitters)
emitter->_update(); emitter->_update();
for (std::shared_ptr<Listener>& listener : m_activeListeners) for (ObjToken<Listener>& listener : m_activeListeners)
listener->m_dirty = false; listener->m_dirty = false;
} }
@ -187,7 +176,7 @@ void Engine::_onPumpCycleComplete(IBackendVoiceAllocator& engine)
/* Determine lowest available free vid */ /* Determine lowest available free vid */
int maxVid = -1; int maxVid = -1;
for (std::shared_ptr<Voice>& vox : m_activeVoices) for (ObjToken<Voice>& vox : m_activeVoices)
maxVid = std::max(maxVid, vox->maxVid()); maxVid = std::max(maxVid, vox->maxVid());
m_nextVid = maxVid + 1; m_nextVid = maxVid + 1;
} }
@ -273,10 +262,10 @@ void Engine::removeAudioGroup(const AudioGroupData& data)
} }
/** Create new Studio within engine */ /** Create new Studio within engine */
std::shared_ptr<Studio> Engine::addStudio(bool mainOut) { return _allocateStudio(mainOut); } ObjToken<Studio> Engine::addStudio(bool mainOut) { return _allocateStudio(mainOut); }
/** Start soundFX playing from loaded audio groups */ /** Start soundFX playing from loaded audio groups */
std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, std::weak_ptr<Studio> smx) ObjToken<Voice> Engine::fxStart(int sfxId, float vol, float pan, ObjToken<Studio> smx)
{ {
auto search = m_sfxLookup.find(sfxId); auto search = m_sfxLookup.find(sfxId);
if (search == m_sfxLookup.end()) if (search == m_sfxLookup.end())
@ -287,7 +276,7 @@ std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, std::wea
if (!grp) if (!grp)
return {}; return {};
std::list<std::shared_ptr<Voice>>::iterator ret = std::list<ObjToken<Voice>>::iterator ret =
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, false, smx); _allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, false, smx);
if (!(*ret)->loadMacroObject(entry->macro.id, 0, 1000.f, entry->defKey, entry->defVel, 0)) if (!(*ret)->loadMacroObject(entry->macro.id, 0, 1000.f, entry->defKey, entry->defVel, 0))
@ -302,13 +291,13 @@ std::shared_ptr<Voice> Engine::fxStart(int sfxId, float vol, float pan, std::wea
} }
/** Start SoundMacro node playing directly (for editor use) */ /** Start SoundMacro node playing directly (for editor use) */
std::shared_ptr<Voice> Engine::macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel, ObjToken<Voice> Engine::macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel,
uint8_t mod, std::weak_ptr<Studio> smx) uint8_t mod, ObjToken<Studio> smx)
{ {
if (!group) if (!group)
return {}; return {};
std::list<std::shared_ptr<Voice>>::iterator ret = std::list<ObjToken<Voice>>::iterator ret =
_allocateVoice(*group, {}, NativeSampleRate, true, false, smx); _allocateVoice(*group, {}, NativeSampleRate, true, false, smx);
if (!(*ret)->loadMacroObject(id, 0, 1000.f, key, vel, mod)) if (!(*ret)->loadMacroObject(id, 0, 1000.f, key, vel, mod))
@ -321,9 +310,8 @@ std::shared_ptr<Voice> Engine::macroStart(const AudioGroup* group, SoundMacroId
} }
/** Start soundFX playing from loaded audio groups, attach to positional emitter */ /** Start soundFX playing from loaded audio groups, attach to positional emitter */
std::shared_ptr<Emitter> Engine::addEmitter(const float* pos, const float* dir, float maxDist, float falloff, ObjToken<Emitter> Engine::addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
int sfxId, float minVol, float maxVol, bool doppler, int sfxId, float minVol, float maxVol, bool doppler, ObjToken<Studio> smx)
std::weak_ptr<Studio> smx)
{ {
auto search = m_sfxLookup.find(sfxId); auto search = m_sfxLookup.find(sfxId);
if (search == m_sfxLookup.end()) if (search == m_sfxLookup.end())
@ -334,7 +322,7 @@ std::shared_ptr<Emitter> Engine::addEmitter(const float* pos, const float* dir,
if (!grp) if (!grp)
return {}; return {};
std::list<std::shared_ptr<Voice>>::iterator vox = std::list<ObjToken<Voice>>::iterator vox =
_allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, true, smx); _allocateVoice(*grp, std::get<1>(search->second), NativeSampleRate, true, true, smx);
if (!(*vox)->loadMacroObject(entry->macro, 0, 1000.f, entry->defKey, entry->defVel, 0)) if (!(*vox)->loadMacroObject(entry->macro, 0, 1000.f, entry->defKey, entry->defVel, 0))
@ -344,7 +332,7 @@ std::shared_ptr<Emitter> Engine::addEmitter(const float* pos, const float* dir,
} }
auto emitIt = m_activeEmitters.emplace(m_activeEmitters.end(), auto emitIt = m_activeEmitters.emplace(m_activeEmitters.end(),
new Emitter(*this, *grp, *vox, maxDist, minVol, falloff, doppler)); MakeObj<Emitter>(*this, *grp, *vox, maxDist, minVol, falloff, doppler));
Emitter& ret = *(*emitIt); Emitter& ret = *(*emitIt);
ret.getVoice()->setPan(entry->panning); ret.getVoice()->setPan(entry->panning);
@ -355,11 +343,11 @@ std::shared_ptr<Emitter> Engine::addEmitter(const float* pos, const float* dir,
} }
/** Build listener and add to engine's listener list */ /** Build listener and add to engine's listener list */
std::shared_ptr<Listener> Engine::addListener(const float* pos, const float* dir, const float* heading, const float* up, ObjToken<Listener> Engine::addListener(const float* pos, const float* dir, const float* heading, const float* up,
float frontDiff, float backDiff, float soundSpeed, float volume) float frontDiff, float backDiff, float soundSpeed, float volume)
{ {
auto listenerIt = m_activeListeners.emplace(m_activeListeners.end(), auto listenerIt = m_activeListeners.emplace(m_activeListeners.end(),
new Listener(volume, frontDiff, backDiff, soundSpeed)); MakeObj<Listener>(volume, frontDiff, backDiff, soundSpeed));
Listener& ret = *(*listenerIt); Listener& ret = *(*listenerIt);
ret.setVectors(pos, dir, heading, up); ret.setVectors(pos, dir, heading, up);
return *listenerIt; return *listenerIt;
@ -379,13 +367,12 @@ void Engine::removeListener(Listener* listener)
} }
/** Start song playing from loaded audio groups */ /** Start song playing from loaded audio groups */
std::shared_ptr<Sequencer> Engine::seqPlay(int groupId, int songId, const unsigned char* arrData, ObjToken<Sequencer> Engine::seqPlay(int groupId, int songId, const unsigned char* arrData, ObjToken<Studio> smx)
std::weak_ptr<Studio> smx)
{ {
std::pair<AudioGroup*, const SongGroupIndex*> songGrp = _findSongGroup(groupId); std::pair<AudioGroup*, const SongGroupIndex*> songGrp = _findSongGroup(groupId);
if (songGrp.second) if (songGrp.second)
{ {
std::list<std::shared_ptr<Sequencer>>::iterator ret = _allocateSequencer(*songGrp.first, groupId, songId, smx); std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*songGrp.first, groupId, songId, smx);
if (!*ret) if (!*ret)
return {}; return {};
@ -397,7 +384,7 @@ std::shared_ptr<Sequencer> Engine::seqPlay(int groupId, int songId, const unsign
std::pair<AudioGroup*, const SFXGroupIndex*> sfxGrp = _findSFXGroup(groupId); std::pair<AudioGroup*, const SFXGroupIndex*> sfxGrp = _findSFXGroup(groupId);
if (sfxGrp.second) if (sfxGrp.second)
{ {
std::list<std::shared_ptr<Sequencer>>::iterator ret = _allocateSequencer(*sfxGrp.first, groupId, songId, smx); std::list<ObjToken<Sequencer>>::iterator ret = _allocateSequencer(*sfxGrp.first, groupId, songId, smx);
if (!*ret) if (!*ret)
return {}; return {};
return *ret; return *ret;
@ -413,18 +400,18 @@ void Engine::setVolume(float vol)
} }
/** Find voice from VoiceId */ /** Find voice from VoiceId */
std::shared_ptr<Voice> Engine::findVoice(int vid) ObjToken<Voice> Engine::findVoice(int vid)
{ {
for (std::shared_ptr<Voice>& vox : m_activeVoices) for (ObjToken<Voice>& vox : m_activeVoices)
{ {
std::shared_ptr<Voice> ret = vox->_findVoice(vid, vox); ObjToken<Voice> ret = vox->_findVoice(vid, vox);
if (ret) if (ret)
return ret; return ret;
} }
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers) for (ObjToken<Sequencer>& seq : m_activeSequencers)
{ {
std::shared_ptr<Voice> ret = seq->findVoice(vid); ObjToken<Voice> ret = seq->findVoice(vid);
if (ret) if (ret)
return ret; return ret;
} }
@ -450,7 +437,7 @@ void Engine::killKeygroup(uint8_t kg, bool now)
++it; ++it;
} }
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers) for (ObjToken<Sequencer>& seq : m_activeSequencers)
seq->killKeygroup(kg, now); seq->killKeygroup(kg, now);
} }
@ -464,7 +451,7 @@ void Engine::sendMacroMessage(ObjectId macroId, int32_t val)
vox->message(val); vox->message(val);
} }
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers) for (ObjToken<Sequencer>& seq : m_activeSequencers)
seq->sendMacroMessage(macroId, val); seq->sendMacroMessage(macroId, val);
} }
} }

View File

@ -58,7 +58,7 @@ Sequencer::~Sequencer()
} }
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SongGroupIndex* songGroup, int setupId, Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SongGroupIndex* songGroup, int setupId,
std::weak_ptr<Studio> studio) ObjToken<Studio> studio)
: Entity(engine, group, groupId), m_songGroup(songGroup), m_studio(studio) : Entity(engine, group, groupId), m_songGroup(songGroup), m_studio(studio)
{ {
auto it = m_songGroup->m_midiSetups.find(setupId); 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, Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SFXGroupIndex* sfxGroup,
std::weak_ptr<Studio> studio) ObjToken<Studio> studio)
: Entity(engine, group, groupId), m_sfxGroup(sfxGroup), m_studio(studio) : Entity(engine, group, groupId), m_sfxGroup(sfxGroup), m_studio(studio)
{ {
std::map<uint16_t, const SFXGroupIndex::SFXEntry*> sortSFX; std::map<uint16_t, const SFXGroupIndex::SFXEntry*> sortSFX;
@ -210,13 +210,16 @@ size_t Sequencer::getVoiceCount() const
return ret; return ret;
} }
std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velocity) ObjToken<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velocity)
{ {
if (m_parent->m_songGroup && !m_page) if (m_parent->m_songGroup && !m_page)
return {}; return {};
if (m_lastVoice->isDestroyed())
m_lastVoice.reset();
/* If portamento is enabled for voice, pre-empt spawning new voices */ /* If portamento is enabled for voice, pre-empt spawning new voices */
if (std::shared_ptr<Voice> lastVoice = m_lastVoice.lock()) if (ObjToken<Voice> lastVoice = m_lastVoice)
{ {
uint8_t lastNote = lastVoice->getLastNote(); uint8_t lastNote = lastVoice->getLastNote();
if (lastVoice->doPortamento(note)) if (lastVoice->doPortamento(note))
@ -231,7 +234,7 @@ std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo
auto keySearch = m_chanVoxs.find(note); auto keySearch = m_chanVoxs.find(note);
if (keySearch != m_chanVoxs.cend()) if (keySearch != m_chanVoxs.cend())
{ {
if (keySearch->second == m_lastVoice.lock()) if (keySearch->second == m_lastVoice)
m_lastVoice.reset(); m_lastVoice.reset();
keySearch->second->keyOff(); keySearch->second->keyOff();
keySearch->second->setPedal(false); keySearch->second->setPedal(false);
@ -239,7 +242,7 @@ std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo
m_chanVoxs.erase(keySearch); m_chanVoxs.erase(keySearch);
} }
std::list<std::shared_ptr<Voice>>::iterator ret = m_parent->m_engine._allocateVoice( std::list<ObjToken<Voice>>::iterator ret = m_parent->m_engine._allocateVoice(
m_parent->m_audioGroup, m_parent->m_groupId, NativeSampleRate, true, false, m_parent->m_studio); m_parent->m_audioGroup, m_parent->m_groupId, NativeSampleRate, true, false, m_parent->m_studio);
if (*ret) if (*ret)
{ {
@ -284,7 +287,7 @@ std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo
return *ret; return *ret;
} }
std::shared_ptr<Voice> Sequencer::keyOn(uint8_t chan, uint8_t note, uint8_t velocity) ObjToken<Voice> Sequencer::keyOn(uint8_t chan, uint8_t note, uint8_t velocity)
{ {
if (chan > 15) if (chan > 15)
return {}; return {};
@ -301,7 +304,7 @@ void Sequencer::ChannelState::keyOff(uint8_t note, uint8_t velocity)
if (keySearch == m_chanVoxs.cend()) if (keySearch == m_chanVoxs.cend())
return; return;
if (keySearch->second == m_lastVoice.lock()) if (m_lastVoice->isDestroyed() || keySearch->second == m_lastVoice)
m_lastVoice.reset(); m_lastVoice.reset();
keySearch->second->keyOff(); keySearch->second->keyOff();
m_keyoffVoxs.emplace(std::move(keySearch->second)); m_keyoffVoxs.emplace(std::move(keySearch->second));
@ -425,9 +428,11 @@ void Sequencer::setTempo(double ticksPerSec) { m_ticksPerSec = ticksPerSec; }
void Sequencer::ChannelState::allOff() void Sequencer::ChannelState::allOff()
{ {
if (m_lastVoice->isDestroyed())
m_lastVoice.reset();
for (auto it = m_chanVoxs.begin(); it != m_chanVoxs.end();) for (auto it = m_chanVoxs.begin(); it != m_chanVoxs.end();)
{ {
if (it->second == m_lastVoice.lock()) if (it->second == m_lastVoice)
m_lastVoice.reset(); m_lastVoice.reset();
it->second->keyOff(); it->second->keyOff();
m_keyoffVoxs.emplace(std::move(it->second)); 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) 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();) for (auto it = m_chanVoxs.begin(); it != m_chanVoxs.end();)
{ {
Voice* vox = it->second.get(); Voice* vox = it->second.get();
if (vox->m_keygroup == kg) if (vox->m_keygroup == kg)
{ {
if (it->second == m_lastVoice.lock()) if (it->second == m_lastVoice)
m_lastVoice.reset(); m_lastVoice.reset();
if (now) if (now)
{ {
@ -518,7 +526,7 @@ void Sequencer::killKeygroup(uint8_t kg, bool now)
chan.killKeygroup(kg, now); chan.killKeygroup(kg, now);
} }
std::shared_ptr<Voice> Sequencer::ChannelState::findVoice(int vid) ObjToken<Voice> Sequencer::ChannelState::findVoice(int vid)
{ {
for (const auto& vox : m_chanVoxs) for (const auto& vox : m_chanVoxs)
if (vox.second->vid() == vid) if (vox.second->vid() == vid)
@ -529,13 +537,13 @@ std::shared_ptr<Voice> Sequencer::ChannelState::findVoice(int vid)
return {}; return {};
} }
std::shared_ptr<Voice> Sequencer::findVoice(int vid) ObjToken<Voice> Sequencer::findVoice(int vid)
{ {
for (auto& chan : m_chanStates) for (auto& chan : m_chanStates)
{ {
if (chan) if (chan)
{ {
std::shared_ptr<Voice> ret = chan.findVoice(vid); ObjToken<Voice> ret = chan.findVoice(vid);
if (ret) if (ret)
return ret; return ret;
} }

View File

@ -469,7 +469,7 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdWaitMs::Introspective =
}, },
{ {
FIELD_HEAD(SoundMacro::CmdWaitMs, ms), FIELD_HEAD(SoundMacro::CmdWaitMs, ms),
"Ticks/Millisec"sv, "Millisec"sv,
0, 65535, 96 0, 65535, 96
} }
} }
@ -541,7 +541,7 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdPlayMacro::Introspective =
}; };
bool SoundMacro::CmdPlayMacro::Do(SoundMacroState& st, Voice& vox) const bool SoundMacro::CmdPlayMacro::Do(SoundMacroState& st, Voice& vox) const
{ {
std::shared_ptr<Voice> sibVox = vox.startChildMacro(addNote, macro.id, macroStep.step); ObjToken<Voice> sibVox = vox.startChildMacro(addNote, macro.id, macroStep.step);
if (sibVox) if (sibVox)
st.m_lastPlayMacroVid = sibVox->vid(); st.m_lastPlayMacroVid = sibVox->vid();
@ -572,14 +572,14 @@ bool SoundMacro::CmdSendKeyOff::Do(SoundMacroState& st, Voice& vox) const
{ {
if (st.m_lastPlayMacroVid != -1) if (st.m_lastPlayMacroVid != -1)
{ {
std::shared_ptr<Voice> otherVox = vox.getEngine().findVoice(st.m_lastPlayMacroVid); ObjToken<Voice> otherVox = vox.getEngine().findVoice(st.m_lastPlayMacroVid);
if (otherVox) if (otherVox)
otherVox->keyOff(); otherVox->keyOff();
} }
} }
else else
{ {
std::shared_ptr<Voice> otherVox = vox.getEngine().findVoice(st.m_variables[variable & 0x1f]); ObjToken<Voice> otherVox = vox.getEngine().findVoice(st.m_variables[variable & 0x1f]);
if (otherVox) if (otherVox)
otherVox->keyOff(); otherVox->keyOff();
} }
@ -1361,12 +1361,12 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdPitchSweep1::Introspective =
{ {
{ {
FIELD_HEAD(SoundMacro::CmdPitchSweep1, times), FIELD_HEAD(SoundMacro::CmdPitchSweep1, times),
"Level Note"sv, "Times"sv,
0, 127, 100, 0, 127, 100,
}, },
{ {
FIELD_HEAD(SoundMacro::CmdPitchSweep1, add), FIELD_HEAD(SoundMacro::CmdPitchSweep1, add),
"Level Fine"sv, "Add"sv,
-32768, 32767, 100, -32768, 32767, 100,
}, },
{ {
@ -1406,12 +1406,12 @@ const SoundMacro::CmdIntrospection SoundMacro::CmdPitchSweep2::Introspective =
{ {
{ {
FIELD_HEAD(SoundMacro::CmdPitchSweep2, times), FIELD_HEAD(SoundMacro::CmdPitchSweep2, times),
"Level Note"sv, "Times"sv,
0, 127, 100, 0, 127, 100,
}, },
{ {
FIELD_HEAD(SoundMacro::CmdPitchSweep2, add), FIELD_HEAD(SoundMacro::CmdPitchSweep2, add),
"Level Fine"sv, "Add"sv,
-32768, 32767, 100, -32768, 32767, 100,
}, },
{ {
@ -1744,7 +1744,7 @@ bool SoundMacro::CmdSendMessage::Do(SoundMacroState& st, Voice& vox) const
{ {
if (isVar) if (isVar)
{ {
std::shared_ptr<Voice> findVox = vox.getEngine().findVoice(st.m_variables[voiceVar & 0x1f]); ObjToken<Voice> findVox = vox.getEngine().findVoice(st.m_variables[voiceVar & 0x1f]);
if (findVox) if (findVox)
findVox->message(st.m_variables[valueVar & 0x1f]); findVox->message(st.m_variables[valueVar & 0x1f]);
} }

View File

@ -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); addStudioSend(engine.getDefaultStudio(), 1.f, 1.f, 1.f);
} }
void Studio::addStudioSend(std::weak_ptr<Studio> studio, float dry, float auxA, float auxB) void Studio::addStudioSend(ObjToken<Studio> studio, float dry, float auxA, float auxB)
{ {
m_studiosOut.emplace_back(studio, dry, auxA, auxB); m_studiosOut.emplace_back(studio, dry, auxA, auxB);

View File

@ -17,8 +17,12 @@ void Voice::_destroy()
{ {
Entity::_destroy(); Entity::_destroy();
for (std::shared_ptr<Voice>& vox : m_childVoices) for (auto& vox : m_childVoices)
vox->_destroy(); vox->_destroy();
m_studio.reset();
m_backendVoice.reset();
m_curSample.reset();
} }
Voice::~Voice() Voice::~Voice()
@ -26,14 +30,14 @@ Voice::~Voice()
// fprintf(stderr, "DEALLOC %d\n", m_vid); // fprintf(stderr, "DEALLOC %d\n", m_vid);
} }
Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, std::weak_ptr<Studio> studio) Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, ObjToken<Studio> studio)
: Entity(engine, group, groupId), m_vid(vid), m_emitter(emitter), m_studio(studio) : Entity(engine, group, groupId), m_vid(vid), m_emitter(emitter), m_studio(studio)
{ {
// fprintf(stderr, "ALLOC %d\n", m_vid); // fprintf(stderr, "ALLOC %d\n", m_vid);
} }
Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter, Voice::Voice(Engine& engine, const AudioGroup& group, int groupId, ObjectId oid, int vid, bool emitter,
std::weak_ptr<Studio> studio) ObjToken<Studio> studio)
: Entity(engine, group, groupId, oid), m_vid(vid), m_emitter(emitter), m_studio(studio) : Entity(engine, group, groupId, oid), m_vid(vid), m_emitter(emitter), m_studio(studio)
{ {
// fprintf(stderr, "ALLOC %d\n", m_vid); // fprintf(stderr, "ALLOC %d\n", m_vid);
@ -115,7 +119,7 @@ void Voice::_doKeyOff()
void Voice::_setTotalPitch(int32_t cents, bool slew) void Voice::_setTotalPitch(int32_t cents, bool slew)
{ {
// fprintf(stderr, "PITCH %d %d \n", cents, 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; double ratio = std::exp2(interval / 1200.0) * m_dopplerRatio;
m_sampleRate = m_curSample->m_sampleRate * ratio; m_sampleRate = m_curSample->m_sampleRate * ratio;
m_backendVoice->setPitchRatio(ratio, slew); m_backendVoice->setPitchRatio(ratio, slew);
@ -125,7 +129,7 @@ bool Voice::_isRecursivelyDead()
{ {
if (m_voxState != VoiceState::Dead) if (m_voxState != VoiceState::Dead)
return false; return false;
for (std::shared_ptr<Voice>& vox : m_childVoices) for (auto& vox : m_childVoices)
if (!vox->_isRecursivelyDead()) if (!vox->_isRecursivelyDead())
return false; return false;
return true; return true;
@ -146,14 +150,14 @@ void Voice::_bringOutYourDead()
} }
} }
std::shared_ptr<Voice> Voice::_findVoice(int vid, std::weak_ptr<Voice> thisPtr) ObjToken<Voice> Voice::_findVoice(int vid, ObjToken<Voice> thisPtr)
{ {
if (m_vid == vid) if (m_vid == vid)
return thisPtr.lock(); return thisPtr;
for (std::shared_ptr<Voice>& vox : m_childVoices) for (ObjToken<Voice>& vox : m_childVoices)
{ {
std::shared_ptr<Voice> ret = vox->_findVoice(vid, vox); ObjToken<Voice> ret = vox->_findVoice(vid, vox);
if (ret) if (ret)
return ret; return ret;
} }
@ -170,16 +174,16 @@ std::unique_ptr<int8_t[]>& Voice::_ensureCtrlVals()
return m_ctrlValsSelf; return m_ctrlValsSelf;
} }
std::list<std::shared_ptr<Voice>>::iterator Voice::_allocateVoice(double sampleRate, bool dynamicPitch) std::list<ObjToken<Voice>>::iterator Voice::_allocateVoice(double sampleRate, bool dynamicPitch)
{ {
auto it = m_childVoices.emplace( 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<Voice>(m_engine, m_audioGroup, m_groupId, m_engine.m_nextVid++, m_emitter, m_studio));
m_childVoices.back()->m_backendVoice = m_childVoices.back()->m_backendVoice =
m_engine.getBackend().allocateVoice(*m_childVoices.back(), sampleRate, dynamicPitch); m_engine.getBackend().allocateVoice(*m_childVoices.back(), sampleRate, dynamicPitch);
return it; return it;
} }
std::list<std::shared_ptr<Voice>>::iterator Voice::_destroyVoice(std::list<std::shared_ptr<Voice>>::iterator it) std::list<ObjToken<Voice>>::iterator Voice::_destroyVoice(std::list<ObjToken<Voice>>::iterator it)
{ {
if ((*it)->m_destroyed) if ((*it)->m_destroyed)
return m_childVoices.begin(); 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); memset(data, 0, sizeof(int16_t) * samples);
if (m_voxState == VoiceState::Dead) if (m_voxState == VoiceState::Dead)
m_curSample = nullptr; m_curSample.reset();
return samples; 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 Voice::maxVid() const
{ {
int maxVid = m_vid; int maxVid = m_vid;
for (const std::shared_ptr<Voice>& vox : m_childVoices) for (const ObjToken<Voice>& vox : m_childVoices)
maxVid = std::max(maxVid, vox->maxVid()); maxVid = std::max(maxVid, vox->maxVid());
return maxVid; return maxVid;
} }
std::shared_ptr<Voice> Voice::_startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey, ObjToken<Voice> Voice::_startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey,
uint8_t midiVel, uint8_t midiMod, bool pushPc) uint8_t midiVel, uint8_t midiMod, bool pushPc)
{ {
std::list<std::shared_ptr<Voice>>::iterator vox = _allocateVoice(NativeSampleRate, true); std::list<ObjToken<Voice>>::iterator vox = _allocateVoice(NativeSampleRate, true);
if (!(*vox)->loadMacroObject(macroId, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc)) if (!(*vox)->loadMacroObject(macroId, macroStep, ticksPerSec, midiKey, midiVel, midiMod, pushPc))
{ {
_destroyVoice(vox); _destroyVoice(vox);
return {}; return {};
} }
(*vox)->setVolume(m_targetUserVol); (*vox)->setVolume(m_targetUserVol);
(*vox)->setPan(m_userPan); (*vox)->setPan(m_curPan);
(*vox)->setSurroundPan(m_userSpan); (*vox)->setSurroundPan(m_curSpan);
return *vox; return *vox;
} }
std::shared_ptr<Voice> Voice::startChildMacro(int8_t addNote, ObjectId macroId, int macroStep) ObjToken<Voice> Voice::startChildMacro(int8_t addNote, ObjectId macroId, int macroStep)
{ {
return _startChildMacro(macroId, macroStep, 1000.0, m_state.m_initKey + addNote, m_state.m_initVel, return _startChildMacro(macroId, macroStep, 1000.0, m_state.m_initKey + addNote, m_state.m_initVel,
m_state.m_initMod); m_state.m_initMod);
@ -824,7 +828,7 @@ bool Voice::_loadLayer(const std::vector<LayerMapping>& layer, double ticksPerSe
} }
else else
{ {
std::shared_ptr<Voice> vox = ObjToken<Voice> vox =
_startChildMacro(mapping.macro.id, 0, ticksPerSec, mappingKey, midiVel, midiMod, pushPc); _startChildMacro(mapping.macro.id, 0, ticksPerSec, mappingKey, midiVel, midiMod, pushPc);
if (vox) if (vox)
{ {
@ -904,7 +908,7 @@ void Voice::keyOff()
else if (!m_curSample || m_curSample->m_loopLengthSamples) else if (!m_curSample || m_curSample->m_loopLengthSamples)
_macroKeyOff(); _macroKeyOff();
for (const std::shared_ptr<Voice>& vox : m_childVoices) for (const ObjToken<Voice>& vox : m_childVoices)
vox->keyOff(); vox->keyOff();
} }
@ -930,10 +934,9 @@ void Voice::startSample(SampleId sampId, int32_t offset)
if (m_destroyed) if (m_destroyed)
return; return;
m_curSample = m_audioGroup.getSample(sampId); if (const SampleEntry* sample = m_audioGroup.getSample(sampId))
if (m_curSample)
{ {
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_sampleRate = m_curSample->m_sampleRate;
m_curPitch = m_curSample->m_pitch; 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) void Voice::setVolume(float vol)
{ {
@ -994,7 +997,7 @@ void Voice::setVolume(float vol)
return; return;
m_targetUserVol = clamp(0.f, vol, 1.f); m_targetUserVol = clamp(0.f, vol, 1.f);
for (std::shared_ptr<Voice>& vox : m_childVoices) for (ObjToken<Voice>& vox : m_childVoices)
vox->setVolume(vol); vox->setVolume(vol);
} }
@ -1116,8 +1119,8 @@ void Voice::_setPan(float pan)
return; return;
m_curPan = clamp(-1.f, pan, 1.f); m_curPan = clamp(-1.f, pan, 1.f);
float totalPan = clamp(-1.f, m_curPan + m_userPan, 1.f); float totalPan = clamp(-1.f, m_curPan, 1.f);
float totalSpan = clamp(-1.f, m_curSpan + m_userSpan, 1.f); float totalSpan = clamp(-1.f, m_curSpan, 1.f);
float coefs[8] = {}; float coefs[8] = {};
_panLaw(coefs, totalPan, totalPan, totalSpan); _panLaw(coefs, totalPan, totalPan, totalSpan);
_setChannelCoefs(coefs); _setChannelCoefs(coefs);
@ -1128,9 +1131,8 @@ void Voice::setPan(float pan)
if (m_destroyed) if (m_destroyed)
return; return;
m_userPan = pan; _setPan(pan);
_setPan(m_curPan); for (ObjToken<Voice>& vox : m_childVoices)
for (std::shared_ptr<Voice>& vox : m_childVoices)
vox->setPan(pan); vox->setPan(pan);
} }
@ -1145,9 +1147,8 @@ void Voice::setSurroundPan(float span)
if (m_destroyed) if (m_destroyed)
return; return;
m_userSpan = span; _setSurroundPan(span);
_setSurroundPan(m_curSpan); for (ObjToken<Voice>& vox : m_childVoices)
for (std::shared_ptr<Voice>& vox : m_childVoices)
vox->setSurroundPan(span); vox->setSurroundPan(span);
} }
@ -1164,7 +1165,7 @@ void Voice::setChannelCoefs(const float coefs[8])
return; return;
_setChannelCoefs(coefs); _setChannelCoefs(coefs);
for (std::shared_ptr<Voice>& vox : m_childVoices) for (ObjToken<Voice>& vox : m_childVoices)
vox->setChannelCoefs(coefs); vox->setChannelCoefs(coefs);
} }
@ -1220,7 +1221,7 @@ void Voice::setPedal(bool pedal)
} }
m_sustained = pedal; m_sustained = pedal;
for (std::shared_ptr<Voice>& vox : m_childVoices) for (ObjToken<Voice>& vox : m_childVoices)
vox->setPedal(pedal); vox->setPedal(pedal);
} }
@ -1264,7 +1265,7 @@ void Voice::setReverbVol(float rvol)
return; return;
m_curReverbVol = clamp(0.f, rvol, 1.f); m_curReverbVol = clamp(0.f, rvol, 1.f);
for (std::shared_ptr<Voice>& vox : m_childVoices) for (ObjToken<Voice>& vox : m_childVoices)
vox->setReverbVol(rvol); vox->setReverbVol(rvol);
} }
@ -1274,7 +1275,7 @@ void Voice::setAuxBVol(float bvol)
return; return;
m_curAuxBVol = clamp(0.f, bvol, 1.f); m_curAuxBVol = clamp(0.f, bvol, 1.f);
for (std::shared_ptr<Voice>& vox : m_childVoices) for (ObjToken<Voice>& vox : m_childVoices)
vox->setAuxBVol(bvol); vox->setAuxBVol(bvol);
} }
@ -1351,7 +1352,7 @@ void Voice::setPitchWheel(float pitchWheel)
m_curPitchWheel = amuse::clamp(-1.f, pitchWheel, 1.f); m_curPitchWheel = amuse::clamp(-1.f, pitchWheel, 1.f);
_setPitchWheel(m_curPitchWheel); _setPitchWheel(m_curPitchWheel);
for (std::shared_ptr<Voice>& vox : m_childVoices) for (ObjToken<Voice>& vox : m_childVoices)
vox->setPitchWheel(pitchWheel); vox->setPitchWheel(pitchWheel);
} }
@ -1371,7 +1372,7 @@ void Voice::setAftertouch(uint8_t aftertouch)
return; return;
m_curAftertouch = aftertouch; m_curAftertouch = aftertouch;
for (std::shared_ptr<Voice>& vox : m_childVoices) for (ObjToken<Voice>& vox : m_childVoices)
vox->setAftertouch(aftertouch); vox->setAftertouch(aftertouch);
} }
@ -1424,14 +1425,14 @@ void Voice::_notifyCtrlChange(uint8_t ctrl, int8_t val)
m_state.m_curMod = uint8_t(val); m_state.m_curMod = uint8_t(val);
} }
for (std::shared_ptr<Voice>& vox : m_childVoices) for (ObjToken<Voice>& vox : m_childVoices)
vox->_notifyCtrlChange(ctrl, val); vox->_notifyCtrlChange(ctrl, val);
} }
size_t Voice::getTotalVoices() const size_t Voice::getTotalVoices() const
{ {
size_t ret = 1; size_t ret = 1;
for (const std::shared_ptr<Voice>& vox : m_childVoices) for (const ObjToken<Voice>& vox : m_childVoices)
ret += vox->getTotalVoices(); ret += vox->getTotalVoices();
return ret; return ret;
} }
@ -1443,7 +1444,7 @@ void Voice::kill()
m_voxState = VoiceState::Dead; m_voxState = VoiceState::Dead;
m_backendVoice->stop(); m_backendVoice->stop();
for (const std::shared_ptr<Voice>& vox : m_childVoices) for (const ObjToken<Voice>& vox : m_childVoices)
vox->kill(); vox->kill();
} }
} }