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

View File

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

View File

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

View File

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

View File

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

View File

@ -122,6 +122,21 @@ bool ProjectModel::openGroupData(const QString& groupName, UIMessenger& messenge
return true;
}
bool ProjectModel::reloadSampleData(const QString& groupName, UIMessenger& messenger)
{
m_projectDatabase.setIdDatabases();
QString path = QFileInfo(m_dir, groupName).filePath();
auto search = m_groups.find(groupName);
if (search != m_groups.end())
{
search->second.setIdDatabases();
search->second.getSdir().reloadSampleData(QStringToSysString(path));
}
m_needsReset = true;
return true;
}
bool ProjectModel::importGroupData(const QString& groupName, const amuse::AudioGroupData& data,
ImportMode mode, UIMessenger& messenger)
{
@ -200,6 +215,7 @@ void ProjectModel::_resetModelData()
auto& tables = group.getPool().tables();
auto& keymaps = group.getPool().keymaps();
auto& layers = group.getPool().layers();
auto& samples = group.getSdir().sampleEntries();
gn.reserve(songGroups.size() + sfxGroups.size() + 4);
for (const auto& grp : SortUnorderedMap(songGroups))
gn.makeChild<SongGroupNode>(grp.first, grp.second.get());
@ -218,7 +234,7 @@ void ProjectModel::_resetModelData()
size_t curveCount = 0;
for (auto& t : tablesSort)
{
amuse::ITable::Type tp = t.second.get()->Isa();
amuse::ITable::Type tp = (*t.second.get())->Isa();
if (tp == amuse::ITable::Type::ADSR || tp == amuse::ITable::Type::ADSRDLS)
ADSRCount += 1;
else if (tp == amuse::ITable::Type::Curve)
@ -230,7 +246,7 @@ void ProjectModel::_resetModelData()
col.reserve(ADSRCount);
for (auto& t : tablesSort)
{
amuse::ITable::Type tp = t.second.get()->Isa();
amuse::ITable::Type tp = (*t.second.get())->Isa();
if (tp == amuse::ITable::Type::ADSR || tp == amuse::ITable::Type::ADSRDLS)
col.makeChild<ADSRNode>(t.first, t.second.get());
}
@ -241,9 +257,9 @@ void ProjectModel::_resetModelData()
col.reserve(curveCount);
for (auto& t : tablesSort)
{
amuse::ITable::Type tp = t.second.get()->Isa();
amuse::ITable::Type tp = (*t.second.get())->Isa();
if (tp == amuse::ITable::Type::Curve)
col.makeChild<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))
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();
}

View File

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

View File

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

View File

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

View File

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

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>
<message>
<location filename="../MainWindow.ui" line="+14"/>
<location filename="../MainWindow.cpp" line="+181"/>
<source>Amuse</source>
<translation>Amuse</translation>
</message>
@ -22,7 +23,7 @@
<translation>&amp;Datei</translation>
</message>
<message>
<location line="+21"/>
<location line="+22"/>
<source>P&amp;roject</source>
<translation>Projekt</translation>
</message>
@ -60,12 +61,12 @@
<translation type="vanished">&amp; Wiederholen</translation>
</message>
<message>
<location line="-65"/>
<location line="-66"/>
<source>Recent &amp;Projects</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+73"/>
<location line="+74"/>
<source>&amp;Cut</source>
<translation>&amp;Schnitt</translation>
</message>
@ -85,9 +86,13 @@
<translation>&amp;Löschen</translation>
</message>
<message>
<location line="+5"/>
<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>
<location line="+3"/>
@ -170,12 +175,17 @@
<translation type="unfinished"></translation>
</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>
<translation>Verlassen</translation>
</message>
<message>
<location line="+134"/>
<location line="+160"/>
<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>
</message>
@ -200,7 +210,7 @@
<translation>Es konnte nicht in das Verzeichnis geschrieben werden</translation>
</message>
<message>
<location line="+44"/>
<location line="+46"/>
<source>No Audio Devices Found</source>
<translation>Keine Audiogeräte gefunden</translation>
</message>
@ -210,7 +220,7 @@
<translation>Keine MIDI-Geräte gefunden</translation>
</message>
<message>
<location line="+193"/>
<location line="+201"/>
<source>New Project</source>
<translation>Neues Projekt</translation>
</message>
@ -225,29 +235,39 @@
<translation>Das Verzeichnis &apos;% 1&apos; existiert nicht.</translation>
</message>
<message>
<location line="-432"/>
<location line="-468"/>
<source>Clear Recent Projects</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+134"/>
<location line="+292"/>
<location line="+124"/>
<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>
<translation type="unfinished"></translation>
</message>
<message>
<location line="-291"/>
<location line="+292"/>
<location line="-301"/>
<location line="+302"/>
<source>Directory empty</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="-181"/>
<location line="-189"/>
<source>SUSTAIN</source>
<translation type="unfinished"></translation>
</message>
<message>
<location line="+187"/>
<location line="+195"/>
<source>Bad Directory</source>
<translation>Schlechtes Verzeichnis</translation>
</message>
@ -258,18 +278,29 @@
</message>
<message>
<location line="+0"/>
<location line="+127"/>
<location line="+76"/>
<location line="+83"/>
<location line="+45"/>
<source>Scanning Project</source>
<translation>Projekt scannen</translation>
</message>
<message>
<location line="-160"/>
<location line="-192"/>
<source>Opening %1</source>
<translation>Eröffnung% 1</translation>
</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>
<translation>Projekt importieren</translation>
</message>
@ -360,7 +391,7 @@
<context>
<name>ProjectModel</name>
<message>
<location filename="../ProjectModel.cpp" line="+210"/>
<location filename="../ProjectModel.cpp" line="+226"/>
<source>Sound Macros</source>
<translation>Sound-Makros</translation>
</message>
@ -384,6 +415,11 @@
<source>Layers</source>
<translation>Lagen</translation>
</message>
<message>
<location line="+7"/>
<source>Samples</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QObject</name>
@ -401,7 +437,7 @@
<context>
<name>QUndoStack</name>
<message>
<location filename="../SoundMacroEditor.cpp" line="+157"/>
<location filename="../SoundMacroEditor.cpp" line="+162"/>
<source>Change %1</source>
<translation type="unfinished"></translation>
</message>
@ -506,7 +542,7 @@
<context>
<name>TargetButton</name>
<message>
<location filename="../SoundMacroEditor.cpp" line="-939"/>
<location filename="../SoundMacroEditor.cpp" line="-944"/>
<source>Set step with target click</source>
<translation type="unfinished"></translation>
</message>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -184,11 +184,11 @@ public:
Value<uint32_t, DNAEn> m_loopStartSample;
Value<uint32_t, DNAEn> m_loopLengthSamples;
};
struct Entry
struct EntryData
{
atUint32 m_sampleOff = 0;
atUint32 m_unk = 0;
atUint8 m_pitch = 0;
atUint8 m_pitch = 60;
atUint16 m_sampleRate = 0;
atUint32 m_numSamples = 0; // Top 8 bits is SampleFormat
atUint32 m_loopStartSample = 0;
@ -204,24 +204,24 @@ public:
time_t m_looseModTime = 0;
std::unique_ptr<uint8_t[]> m_looseData;
Entry() = default;
EntryData() = default;
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_sampleRate(in.m_sampleRate), m_numSamples(in.m_numSamples),
m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples),
m_adpcmParmOffset(in.m_adpcmParmOffset) {}
template <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_sampleRate(in.m_pitchSampleRate & 0xffff), m_numSamples(in.m_numSamples),
m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples),
m_adpcmParmOffset(0) {}
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_sampleRate(in.m_pitchSampleRate & 0xffff), m_numSamples(in.m_numSamples),
m_loopStartSample(in.m_loopStartSample), m_loopLengthSamples(in.m_loopLengthSamples),
@ -243,14 +243,45 @@ public:
return ret;
}
void loadLooseDSP(SystemStringView dspPath);
void loadLooseWAV(SystemStringView wavPath);
};
/* This double-wrapper allows Voices to keep a strong reference on
* a single instance of loaded loose data without being unexpectedly
* clobbered */
struct Entry
{
ObjToken<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);
};
private:
std::unordered_map<SampleId, Entry> m_entries;
static void _extractWAV(SampleId id, const Entry& ent, amuse::SystemStringView destDir,
std::unordered_map<SampleId, ObjToken<Entry>> m_entries;
static void _extractWAV(SampleId id, const EntryData& ent, amuse::SystemStringView destDir,
const unsigned char* samp);
static void _extractCompressed(SampleId id, const Entry& ent, amuse::SystemStringView destDir,
static void _extractCompressed(SampleId id, const EntryData& ent, amuse::SystemStringView destDir,
const unsigned char* samp);
public:
@ -261,18 +292,23 @@ public:
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(const AudioGroupData& data);
static AudioGroupSampleDirectory CreateAudioGroupSampleDirectory(SystemStringView groupPath);
const std::unordered_map<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 extractAllWAV(amuse::SystemStringView destDir, const unsigned char* samp) const;
void extractCompressed(SampleId id, amuse::SystemStringView destDir, const unsigned char* samp) const;
void extractAllCompressed(amuse::SystemStringView destDir, const unsigned char* samp) const;
void reloadSampleData(SystemStringView groupPath);
AudioGroupSampleDirectory(const AudioGroupSampleDirectory&) = delete;
AudioGroupSampleDirectory& operator=(const AudioGroupSampleDirectory&) = delete;
AudioGroupSampleDirectory(AudioGroupSampleDirectory&&) = default;
AudioGroupSampleDirectory& operator=(AudioGroupSampleDirectory&&) = default;
};
using SampleEntry = AudioGroupSampleDirectory::Entry;
using SampleEntryData = AudioGroupSampleDirectory::EntryData;
}
#endif // __AMUSE_AUDIOGROUPSAMPLEDIR_HPP__

View File

@ -9,6 +9,7 @@
#include <string>
#include <string_view>
#include <cstring>
#include <atomic>
#include "athena/DNA.hpp"
#ifndef _WIN32
@ -134,6 +135,64 @@ struct LittleUInt24 : LittleDNA
LittleUInt24& operator=(uint32_t valIn) { val = valIn; return *this; }
};
class IObj
{
std::atomic_int m_refCount = {0};
protected:
virtual ~IObj() = default;
public:
void increment() { m_refCount++; }
void decrement()
{
if (m_refCount.fetch_sub(1) == 1)
delete this;
}
};
template<class 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
#ifdef _MSC_VER
#define PRISize "Iu"
@ -480,6 +539,12 @@ DECL_ID_HASH(LayersId)
DECL_ID_HASH(SongId)
DECL_ID_HASH(SFXId)
DECL_ID_HASH(GroupId)
template<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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,11 +10,11 @@
#include "AudioGroupSampleDirectory.hpp"
#include "AudioGroup.hpp"
#include "Envelope.hpp"
#include "Studio.hpp"
namespace amuse
{
class IBackendVoice;
class Studio;
struct Keymap;
struct LayerMapping;
@ -47,7 +47,7 @@ class Voice : public Entity
int m_vid; /**< VoiceID of this voice instance */
bool m_emitter; /**< Voice is part of an Emitter */
std::shared_ptr<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 */
SoundMacroState m_state; /**< State container for SoundMacro playback */
@ -55,10 +55,10 @@ class Voice : public Entity
SoundMacroState::EventTrap m_sampleEndTrap; /**< Trap for sampleend (SoundMacro overrides voice removal) */
SoundMacroState::EventTrap m_messageTrap; /**< Trap for messages sent from other SoundMacros */
int32_t m_latestMessage = 0; /**< Latest message received on voice */
std::list<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 */
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 */
SampleFormat m_curFormat; /**< Current sample format playing */
uint32_t m_curSamplePos = 0; /**< Current sample position */
@ -82,9 +82,7 @@ class Voice : public Entity
float m_curVol = 1.f; /**< Current volume of voice */
float m_curReverbVol = 0.f; /**< Current reverb volume of voice */
float m_curAuxBVol = 0.f; /**< Current AuxB volume of voice */
float m_userPan = 0.f; /**< User pan of voice */
float m_curPan = 0.f; /**< Current pan of voice */
float m_userSpan = -1.f; /**< User span of voice */
float m_curSpan = -1.f; /**< Current surround pan of voice */
float m_curPitchWheel = 0.f; /**< Current normalized wheel value for control */
int32_t m_pitchWheelUp = 600; /**< Up range for pitchwheel control in cents */
@ -156,11 +154,11 @@ class Voice : public Entity
bool _isRecursivelyDead();
void _bringOutYourDead();
static uint32_t _GetBlockSampleCount(SampleFormat fmt);
std::shared_ptr<Voice> _findVoice(int vid, std::weak_ptr<Voice> thisPtr);
ObjToken<Voice> _findVoice(int vid, ObjToken<Voice> thisPtr);
std::unique_ptr<int8_t[]>& _ensureCtrlVals();
std::list<std::shared_ptr<Voice>>::iterator _allocateVoice(double sampleRate, bool dynamicPitch);
std::list<std::shared_ptr<Voice>>::iterator _destroyVoice(std::list<std::shared_ptr<Voice>>::iterator it);
std::list<ObjToken<Voice>>::iterator _allocateVoice(double sampleRate, bool dynamicPitch);
std::list<ObjToken<Voice>>::iterator _destroyVoice(std::list<ObjToken<Voice>>::iterator it);
bool _loadSoundMacro(SoundMacroId id, const SoundMacro* macroData, int macroStep, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
@ -168,8 +166,8 @@ class Voice : public Entity
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
bool _loadLayer(const std::vector<LayerMapping>& layer, double ticksPerSec,
uint8_t midiKey, uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
std::shared_ptr<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey,
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
ObjToken<Voice> _startChildMacro(ObjectId macroId, int macroStep, double ticksPerSec, uint8_t midiKey,
uint8_t midiVel, uint8_t midiMod, bool pushPc = false);
void _panLaw(float coefsOut[8], float frontPan, float backPan, float totalSpan) const;
void _setPan(float pan);
@ -180,9 +178,9 @@ class Voice : public Entity
public:
~Voice();
Voice(Engine& engine, const AudioGroup& group, int groupId, int vid, bool emitter, std::weak_ptr<Studio> 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,
std::weak_ptr<Studio> studio);
ObjToken<Studio> studio);
/** Called before each supplyAudio invocation to prepare voice
* backend for possible parameter updates */
@ -199,7 +197,7 @@ public:
void routeAudio(size_t frames, double dt, int busId, float* in, float* out);
/** Obtain pointer to Voice's Studio */
std::shared_ptr<Studio> getStudio() { return m_studio; }
ObjToken<Studio> getStudio() { return m_studio; }
/** Get current state of voice */
VoiceState state() const { return m_voxState; }
@ -211,7 +209,7 @@ public:
int maxVid() const;
/** Allocate parallel macro and tie to voice for possible emitter influence */
std::shared_ptr<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 */
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;
}
const AudioGroupSampleDirectory::Entry* AudioGroup::getSample(SampleId sfxId) const
const SampleEntry* AudioGroup::getSample(SampleId sfxId) const
{
auto search = m_sdir.m_entries.find(sfxId);
if (search == m_sdir.m_entries.cend())
return nullptr;
return &search->second;
return search->second.get();
}
const unsigned char* AudioGroup::getSampleData(SampleId sfxId, const AudioGroupSampleDirectory::Entry* sample) const
std::pair<ObjToken<SampleEntryData>, const unsigned char*>
AudioGroup::getSampleData(SampleId sfxId, const SampleEntry* sample) const
{
if (sample->m_looseData)
if (sample->m_data->m_looseData)
{
setIdDatabases();
#if _WIN32
@ -41,9 +42,9 @@ const unsigned char* AudioGroup::getSampleData(SampleId sfxId, const AudioGroupS
SystemString basePath = m_groupPath + _S('/') +
SampleId::CurNameDB->resolveNameFromId(sfxId).data();
#endif
const_cast<AudioGroupSampleDirectory::Entry*>(sample)->loadLooseData(basePath);
return sample->m_looseData.get();
const_cast<SampleEntry*>(sample)->loadLooseData(basePath);
return {sample->m_data, sample->m_data->m_looseData.get()};
}
return m_samp + sample->m_sampleOff;
return {{}, m_samp + sample->m_data->m_sampleOff};
}
}

View File

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

View File

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

View File

@ -44,17 +44,17 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
{
EntryDNA<athena::Big> ent;
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);
}
for (auto& p : m_entries)
{
if (p.second.m_adpcmParmOffset)
if (p.second->m_data->m_adpcmParmOffset)
{
r.seek(p.second.m_adpcmParmOffset, athena::Begin);
r.readUBytesToBuf(&p.second.m_ADPCMParms, sizeof(ADPCMParms::DSPParms));
p.second.m_ADPCMParms.swapBigDSP();
r.seek(p.second->m_data->m_adpcmParmOffset, athena::Begin);
r.readUBytesToBuf(&p.second->m_data->m_ADPCMParms, sizeof(ADPCMParms::DSPParms));
p.second->m_data->m_ADPCMParms.swapBigDSP();
}
}
}
@ -68,7 +68,7 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
{
MusyX1AbsSdirEntry<athena::Big> ent;
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);
}
}
@ -78,15 +78,15 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
{
MusyX1SdirEntry<athena::Big> ent;
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);
}
}
for (auto& p : m_entries)
{
memcpy(&p.second.m_ADPCMParms, sampData + p.second.m_sampleOff, sizeof(ADPCMParms::VADPCMParms));
p.second.m_ADPCMParms.swapBigVADPCM();
memcpy(&p.second->m_data->m_ADPCMParms, sampData + p.second->m_data->m_sampleOff, sizeof(ADPCMParms::VADPCMParms));
p.second->m_data->m_ADPCMParms.swapBigVADPCM();
}
}
@ -98,9 +98,9 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
{
MusyX1AbsSdirEntry<athena::Little> ent;
ent.read(r);
Entry& store = m_entries[ent.m_sfxId];
store = ent;
store.m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24;
auto& store = m_entries[ent.m_sfxId];
store = MakeObj<Entry>(ent);
store->m_data->m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24;
SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId);
}
}
@ -110,9 +110,9 @@ AudioGroupSampleDirectory::AudioGroupSampleDirectory(athena::io::IStreamReader&
{
MusyX1SdirEntry<athena::Little> ent;
ent.read(r);
Entry& store = m_entries[ent.m_sfxId];
store = ent;
store.m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24;
auto& store = m_entries[ent.m_sfxId];
store = MakeObj<Entry>(ent);
store->m_data->m_numSamples |= atUint32(SampleFormat::PCM_PC) << 24;
SampleId::CurNameDB->registerPair(NameDB::generateName(ent.m_sfxId, NameDB::Type::Sample), ent.m_sfxId);
}
}
@ -149,6 +149,88 @@ static uint32_t DSPNibbleToSample(uint32_t nibble)
return ret;
}
void AudioGroupSampleDirectory::EntryData::loadLooseDSP(SystemStringView dspPath)
{
athena::io::FileReader r(dspPath);
if (!r.hasError())
{
DSPADPCMHeader header;
header.read(r);
m_pitch = header.m_pitch;
if (m_pitch == 0)
m_pitch = 60;
m_sampleRate = atUint16(header.x8_sample_rate);
m_numSamples = header.x0_num_samples;
if (header.xc_loop_flag)
{
m_loopStartSample = DSPNibbleToSample(header.x10_loop_start_nibble);
m_loopLengthSamples = DSPNibbleToSample(header.x14_loop_end_nibble) - m_loopStartSample;
}
m_ADPCMParms.dsp.m_ps = uint8_t(header.x3e_ps);
m_ADPCMParms.dsp.m_lps = uint8_t(header.x44_loop_ps);
m_ADPCMParms.dsp.m_hist1 = header.x40_hist1;
m_ADPCMParms.dsp.m_hist2 = header.x42_hist2;
for (int i = 0; i < 8; ++i)
for (int j = 0; j < 2; ++j)
m_ADPCMParms.dsp.m_coefs[i][j] = header.x1c_coef[i][j];
uint32_t dataLen = (header.x4_num_nibbles + 1) / 2;
m_looseData.reset(new uint8_t[dataLen]);
r.readUBytesToBuf(m_looseData.get(), dataLen);
}
}
void AudioGroupSampleDirectory::EntryData::loadLooseWAV(SystemStringView wavPath)
{
athena::io::FileReader r(wavPath);
if (!r.hasError())
{
atUint32 riffMagic = r.readUint32Little();
if (riffMagic != SBIG('RIFF'))
return;
atUint32 wavChuckSize = r.readUint32Little();
atUint32 wavMagic = r.readUint32Little();
if (wavMagic != SBIG('WAVE'))
return;
while (r.position() < wavChuckSize + 8)
{
atUint32 chunkMagic = r.readUint32Little();
atUint32 chunkSize = r.readUint32Little();
atUint64 startPos = r.position();
if (chunkMagic == SBIG('fmt '))
{
WAVFormatChunk fmt;
fmt.read(r);
m_sampleRate = atUint16(fmt.sampleRate);
}
else if (chunkMagic == SBIG('smpl'))
{
WAVSampleChunk smpl;
smpl.read(r);
m_pitch = atUint8(smpl.midiNote);
if (m_pitch == 0)
m_pitch = 60;
if (smpl.numSampleLoops)
{
WAVSampleLoop loop;
loop.read(r);
m_loopStartSample = loop.start;
m_loopLengthSamples = loop.end - loop.start + 1;
}
}
else if (chunkMagic == SBIG('data'))
{
m_numSamples = ((chunkSize / 2) & 0xffffff) | (atUint32(SampleFormat::PCM_PC) << 24);
m_looseData.reset(new uint8_t[chunkSize]);
r.readUBytesToBuf(m_looseData.get(), chunkSize);
}
r.seek(startPos + chunkSize, athena::Begin);
}
}
}
void AudioGroupSampleDirectory::Entry::loadLooseData(SystemStringView basePath)
{
SystemString wavPath = SystemString(basePath) + _S(".wav");
@ -165,88 +247,19 @@ void AudioGroupSampleDirectory::Entry::loadLooseData(SystemStringView basePath)
wavValid = false;
}
if (dspValid && (!m_looseData || dspStat.st_mtime > m_looseModTime))
EntryData& curData = *m_data;
if (dspValid && (!curData.m_looseData || dspStat.st_mtime > curData.m_looseModTime))
{
athena::io::FileReader r(dspPath);
if (!r.hasError())
{
DSPADPCMHeader header;
header.read(r);
m_pitch = header.m_pitch;
m_sampleRate = atUint16(header.x8_sample_rate);
m_numSamples = header.x0_num_samples;
if (header.xc_loop_flag)
{
m_loopStartSample = DSPNibbleToSample(header.x10_loop_start_nibble);
m_loopLengthSamples = DSPNibbleToSample(header.x14_loop_end_nibble) - m_loopStartSample;
}
m_ADPCMParms.dsp.m_ps = uint8_t(header.x3e_ps);
m_ADPCMParms.dsp.m_lps = uint8_t(header.x44_loop_ps);
m_ADPCMParms.dsp.m_hist1 = header.x40_hist1;
m_ADPCMParms.dsp.m_hist2 = header.x42_hist2;
for (int i = 0; i < 8; ++i)
for (int j = 0; j < 2; ++j)
m_ADPCMParms.dsp.m_coefs[i][j] = header.x1c_coef[i][j];
uint32_t dataLen = (header.x4_num_nibbles + 1) / 2;
m_looseData.reset(new uint8_t[dataLen]);
r.readUBytesToBuf(m_looseData.get(), dataLen);
m_looseModTime = dspStat.st_mtime;
return;
}
m_data = MakeObj<EntryData>();
m_data->loadLooseDSP(dspPath);
m_data->m_looseModTime = dspStat.st_mtime;
}
if (wavValid && (!m_looseData || wavStat.st_mtime > m_looseModTime))
else if (wavValid && (!curData.m_looseData || wavStat.st_mtime > curData.m_looseModTime))
{
athena::io::FileReader r(wavPath);
if (!r.hasError())
{
atUint32 riffMagic = r.readUint32Little();
if (riffMagic != SBIG('RIFF'))
return;
atUint32 wavChuckSize = r.readUint32Little();
atUint32 wavMagic = r.readUint32Little();
if (wavMagic != SBIG('WAVE'))
return;
while (r.position() < wavChuckSize + 8)
{
atUint32 chunkMagic = r.readUint32Little();
atUint32 chunkSize = r.readUint32Little();
atUint64 startPos = r.position();
if (chunkMagic == SBIG('fmt '))
{
WAVFormatChunk fmt;
fmt.read(r);
m_sampleRate = atUint16(fmt.sampleRate);
}
else if (chunkMagic == SBIG('smpl'))
{
WAVSampleChunk smpl;
smpl.read(r);
m_pitch = atUint8(smpl.midiNote);
if (smpl.numSampleLoops)
{
WAVSampleLoop loop;
loop.read(r);
m_loopStartSample = loop.start;
m_loopLengthSamples = loop.end - loop.start + 1;
}
}
else if (chunkMagic == SBIG('data'))
{
m_numSamples = ((chunkSize / 2) & 0xffffff) | (atUint32(SampleFormat::PCM_PC) << 24);
m_looseData.reset(new uint8_t[chunkSize]);
r.readUBytesToBuf(m_looseData.get(), chunkSize);
}
r.seek(startPos + chunkSize, athena::Begin);
}
m_looseModTime = wavStat.st_mtime;
return;
}
m_data = MakeObj<EntryData>();
m_data->loadLooseWAV(wavPath);
m_data->m_looseModTime = wavStat.st_mtime;
}
}
@ -273,15 +286,16 @@ AudioGroupSampleDirectory AudioGroupSampleDirectory::CreateAudioGroupSampleDirec
SampleId::CurNameDB->registerPair(baseName, sampleId);
#endif
Entry& entry = ret.m_entries[sampleId];
auto& entry = ret.m_entries[sampleId];
entry = MakeObj<Entry>();
SystemString basePath = SystemString(ent.m_path.begin(), ent.m_path.begin() + ent.m_path.size() - 4);
entry.loadLooseData(basePath);
entry->loadLooseData(basePath);
}
return ret;
}
void AudioGroupSampleDirectory::_extractWAV(SampleId id, const Entry& ent,
void AudioGroupSampleDirectory::_extractWAV(SampleId id, const EntryData& ent,
amuse::SystemStringView destDir, const unsigned char* samp)
{
amuse::SystemString path(destDir);
@ -389,16 +403,16 @@ void AudioGroupSampleDirectory::extractWAV(SampleId id, amuse::SystemStringView
auto search = m_entries.find(id);
if (search == m_entries.cend())
return;
_extractWAV(id, search->second, destDir, samp);
_extractWAV(id, *search->second->m_data, destDir, samp);
}
void AudioGroupSampleDirectory::extractAllWAV(amuse::SystemStringView destDir, const unsigned char* samp) const
{
for (const auto& ent : m_entries)
_extractWAV(ent.first, ent.second, destDir, samp);
_extractWAV(ent.first, *ent.second->m_data, destDir, samp);
}
void AudioGroupSampleDirectory::_extractCompressed(SampleId id, const Entry& ent,
void AudioGroupSampleDirectory::_extractCompressed(SampleId id, const EntryData& ent,
amuse::SystemStringView destDir, const unsigned char* samp)
{
SampleFormat fmt = SampleFormat(ent.m_numSamples >> 24);
@ -475,13 +489,46 @@ void AudioGroupSampleDirectory::extractCompressed(SampleId id, amuse::SystemStri
auto search = m_entries.find(id);
if (search == m_entries.cend())
return;
_extractCompressed(id, search->second, destDir, samp);
_extractCompressed(id, *search->second->m_data, destDir, samp);
}
void AudioGroupSampleDirectory::extractAllCompressed(amuse::SystemStringView destDir,
const unsigned char* samp) const
{
for (const auto& ent : m_entries)
_extractCompressed(ent.first, ent.second, destDir, samp);
_extractCompressed(ent.first, *ent.second->m_data, destDir, samp);
}
void AudioGroupSampleDirectory::reloadSampleData(SystemStringView groupPath)
{
DirectoryEnumerator de(groupPath, DirectoryEnumerator::Mode::FilesSorted);
for (const DirectoryEnumerator::Entry& ent : de)
{
if (ent.m_name.size() < 4)
continue;
SystemString baseName;
if (!CompareCaseInsensitive(ent.m_name.data() + ent.m_name.size() - 4, _S(".dsp")) ||
!CompareCaseInsensitive(ent.m_name.data() + ent.m_name.size() - 4, _S(".wav")))
baseName = SystemString(ent.m_name.begin(), ent.m_name.begin() + ent.m_name.size() - 4);
else
continue;
#ifdef _WIN32
std::string baseNameStd = athena::utility::wideToUtf8(baseName);
#else
std::string& baseNameStd = baseName;
#endif
if (SampleId::CurNameDB->m_stringToId.find(baseNameStd) == SampleId::CurNameDB->m_stringToId.end())
{
ObjectId sampleId = SampleId::CurNameDB->generateId(NameDB::Type::Sample);
SampleId::CurNameDB->registerPair(baseNameStd, sampleId);
auto& entry = m_entries[sampleId];
entry = MakeObj<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)
{
for (std::shared_ptr<Sequencer>& seq : m_engine.getActiveSequencers())
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->keyOff(chan, key, velocity);
#if 0
openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON);
@ -185,7 +185,7 @@ void BooBackendMIDIReader::noteOff(uint8_t chan, uint8_t key, uint8_t velocity)
void BooBackendMIDIReader::noteOn(uint8_t chan, uint8_t key, uint8_t velocity)
{
for (std::shared_ptr<Sequencer>& seq : m_engine.getActiveSequencers())
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->keyOn(chan, key, velocity);
#if 0
openlog("LogIt", (LOG_CONS|LOG_PERROR|LOG_PID), LOG_DAEMON);
@ -198,13 +198,13 @@ void BooBackendMIDIReader::notePressure(uint8_t /*chan*/, uint8_t /*key*/, uint8
void BooBackendMIDIReader::controlChange(uint8_t chan, uint8_t control, uint8_t value)
{
for (std::shared_ptr<Sequencer>& seq : m_engine.getActiveSequencers())
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->setCtrlValue(chan, control, value);
}
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);
}
@ -212,13 +212,13 @@ void BooBackendMIDIReader::channelPressure(uint8_t /*chan*/, uint8_t /*pressure*
void BooBackendMIDIReader::pitchBend(uint8_t chan, int16_t pitch)
{
for (std::shared_ptr<Sequencer>& seq : m_engine.getActiveSequencers())
for (ObjToken<Sequencer>& seq : m_engine.getActiveSequencers())
seq->setPitchWheel(chan, (pitch - 0x2000) / float(0x2000));
}
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);
}
@ -228,7 +228,7 @@ void BooBackendMIDIReader::localControl(uint8_t /*chan*/, bool /*on*/) {}
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);
}

View File

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

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()
{
m_backend.setCallbackInterface(nullptr);
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers)
for (ObjToken<Sequencer>& seq : m_activeSequencers)
if (!seq->m_destroyed)
seq->_destroy();
for (std::shared_ptr<Emitter>& emitter : m_activeEmitters)
for (ObjToken<Emitter>& emitter : m_activeEmitters)
emitter->_destroy();
for (std::shared_ptr<Voice>& vox : m_activeVoices)
for (ObjToken<Voice>& vox : m_activeVoices)
vox->_destroy();
}
@ -57,51 +57,49 @@ std::pair<AudioGroup*, const SFXGroupIndex*> Engine::_findSFXGroup(int groupId)
return {};
}
std::list<std::shared_ptr<Voice>>::iterator Engine::_allocateVoice(const AudioGroup& group, int groupId,
double sampleRate, bool dynamicPitch, bool emitter,
std::weak_ptr<Studio> studio)
std::list<ObjToken<Voice>>::iterator Engine::_allocateVoice(const AudioGroup& group, int groupId,
double sampleRate, bool dynamicPitch, bool emitter,
ObjToken<Studio> studio)
{
std::shared_ptr<Studio> st = studio.lock();
auto it =
m_activeVoices.emplace(m_activeVoices.end(), new Voice(*this, group, groupId, m_nextVid++, emitter, studio));
m_activeVoices.emplace(m_activeVoices.end(), MakeObj<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->setChannelLevels(st->getMaster().m_backendSubmix.get(), FullLevels, false);
m_activeVoices.back()->m_backendVoice->setChannelLevels(st->getAuxA().m_backendSubmix.get(), FullLevels, false);
m_activeVoices.back()->m_backendVoice->setChannelLevels(st->getAuxB().m_backendSubmix.get(), FullLevels, false);
m_activeVoices.back()->m_backendVoice->setChannelLevels(studio->getMaster().m_backendSubmix.get(), FullLevels, false);
m_activeVoices.back()->m_backendVoice->setChannelLevels(studio->getAuxA().m_backendSubmix.get(), FullLevels, false);
m_activeVoices.back()->m_backendVoice->setChannelLevels(studio->getAuxB().m_backendSubmix.get(), FullLevels, false);
return it;
}
std::list<std::shared_ptr<Sequencer>>::iterator Engine::_allocateSequencer(const AudioGroup& group, int groupId,
int setupId, std::weak_ptr<Studio> studio)
std::list<ObjToken<Sequencer>>::iterator Engine::_allocateSequencer(const AudioGroup& group, int groupId,
int setupId, ObjToken<Studio> studio)
{
const SongGroupIndex* songGroup = group.getProj().getSongGroupIndex(groupId);
if (songGroup)
{
auto it = m_activeSequencers.emplace(m_activeSequencers.end(),
new Sequencer(*this, group, groupId, songGroup, setupId, studio));
MakeObj<Sequencer>(*this, group, groupId, songGroup, setupId, studio));
return it;
}
const SFXGroupIndex* sfxGroup = group.getProj().getSFXGroupIndex(groupId);
if (sfxGroup)
{
auto it = m_activeSequencers.emplace(m_activeSequencers.end(),
new Sequencer(*this, group, groupId, sfxGroup, studio));
MakeObj<Sequencer>(*this, group, groupId, sfxGroup, studio));
return it;
}
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);
m_activeStudios.emplace(m_activeStudios.end(), ret);
ObjToken<Studio> ret = MakeObj<Studio>(*this, mainOut);
ret->m_master.m_backendSubmix = m_backend.allocateSubmix(ret->m_master, mainOut, 0);
ret->m_auxA.m_backendSubmix = m_backend.allocateSubmix(ret->m_auxA, mainOut, 1);
ret->m_auxB.m_backendSubmix = m_backend.allocateSubmix(ret->m_auxB, mainOut, 2);
return ret;
}
std::list<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());
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);
}
std::list<std::shared_ptr<Sequencer>>::iterator
Engine::_destroySequencer(std::list<std::shared_ptr<Sequencer>>::iterator it)
std::list<ObjToken<Sequencer>>::iterator
Engine::_destroySequencer(std::list<ObjToken<Sequencer>>::iterator it)
{
assert(this == &(*it)->getEngine());
if ((*it)->m_destroyed)
@ -157,15 +155,6 @@ void Engine::_bringOutYourDead()
}
++it;
}
for (auto it = m_activeStudios.begin(); it != m_activeStudios.end();)
{
std::shared_ptr<Studio> st = it->lock();
if (!st)
it = m_activeStudios.erase(it);
else
++it;
}
}
void Engine::_on5MsInterval(IBackendVoiceAllocator& engine, double dt)
@ -173,11 +162,11 @@ void Engine::_on5MsInterval(IBackendVoiceAllocator& engine, double dt)
m_channelSet = engine.getAvailableSet();
if (m_midiReader)
m_midiReader->pumpReader(dt);
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers)
for (ObjToken<Sequencer>& seq : m_activeSequencers)
seq->advance(dt);
for (std::shared_ptr<Emitter>& emitter : m_activeEmitters)
for (ObjToken<Emitter>& emitter : m_activeEmitters)
emitter->_update();
for (std::shared_ptr<Listener>& listener : m_activeListeners)
for (ObjToken<Listener>& listener : m_activeListeners)
listener->m_dirty = false;
}
@ -187,7 +176,7 @@ void Engine::_onPumpCycleComplete(IBackendVoiceAllocator& engine)
/* Determine lowest available free vid */
int maxVid = -1;
for (std::shared_ptr<Voice>& vox : m_activeVoices)
for (ObjToken<Voice>& vox : m_activeVoices)
maxVid = std::max(maxVid, vox->maxVid());
m_nextVid = maxVid + 1;
}
@ -273,10 +262,10 @@ void Engine::removeAudioGroup(const AudioGroupData& data)
}
/** Create new Studio within engine */
std::shared_ptr<Studio> Engine::addStudio(bool mainOut) { return _allocateStudio(mainOut); }
ObjToken<Studio> Engine::addStudio(bool mainOut) { return _allocateStudio(mainOut); }
/** 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);
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)
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);
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) */
std::shared_ptr<Voice> Engine::macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel,
uint8_t mod, std::weak_ptr<Studio> smx)
ObjToken<Voice> Engine::macroStart(const AudioGroup* group, SoundMacroId id, uint8_t key, uint8_t vel,
uint8_t mod, ObjToken<Studio> smx)
{
if (!group)
return {};
std::list<std::shared_ptr<Voice>>::iterator ret =
std::list<ObjToken<Voice>>::iterator ret =
_allocateVoice(*group, {}, NativeSampleRate, true, false, smx);
if (!(*ret)->loadMacroObject(id, 0, 1000.f, key, vel, mod))
@ -321,9 +310,8 @@ std::shared_ptr<Voice> Engine::macroStart(const AudioGroup* group, SoundMacroId
}
/** 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,
int sfxId, float minVol, float maxVol, bool doppler,
std::weak_ptr<Studio> smx)
ObjToken<Emitter> Engine::addEmitter(const float* pos, const float* dir, float maxDist, float falloff,
int sfxId, float minVol, float maxVol, bool doppler, ObjToken<Studio> smx)
{
auto search = m_sfxLookup.find(sfxId);
if (search == m_sfxLookup.end())
@ -334,7 +322,7 @@ std::shared_ptr<Emitter> Engine::addEmitter(const float* pos, const float* dir,
if (!grp)
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);
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(),
new Emitter(*this, *grp, *vox, maxDist, minVol, falloff, doppler));
MakeObj<Emitter>(*this, *grp, *vox, maxDist, minVol, falloff, doppler));
Emitter& ret = *(*emitIt);
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 */
std::shared_ptr<Listener> Engine::addListener(const float* pos, const float* dir, const float* heading, const float* up,
float frontDiff, float backDiff, float soundSpeed, float volume)
ObjToken<Listener> Engine::addListener(const float* pos, const float* dir, const float* heading, const float* up,
float frontDiff, float backDiff, float soundSpeed, float volume)
{
auto listenerIt = m_activeListeners.emplace(m_activeListeners.end(),
new Listener(volume, frontDiff, backDiff, soundSpeed));
MakeObj<Listener>(volume, frontDiff, backDiff, soundSpeed));
Listener& ret = *(*listenerIt);
ret.setVectors(pos, dir, heading, up);
return *listenerIt;
@ -379,13 +367,12 @@ void Engine::removeListener(Listener* listener)
}
/** Start song playing from loaded audio groups */
std::shared_ptr<Sequencer> Engine::seqPlay(int groupId, int songId, const unsigned char* arrData,
std::weak_ptr<Studio> smx)
ObjToken<Sequencer> Engine::seqPlay(int groupId, int songId, const unsigned char* arrData, ObjToken<Studio> smx)
{
std::pair<AudioGroup*, const SongGroupIndex*> songGrp = _findSongGroup(groupId);
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)
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);
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)
return {};
return *ret;
@ -413,18 +400,18 @@ void Engine::setVolume(float vol)
}
/** 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)
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)
return ret;
}
@ -450,7 +437,7 @@ void Engine::killKeygroup(uint8_t kg, bool now)
++it;
}
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers)
for (ObjToken<Sequencer>& seq : m_activeSequencers)
seq->killKeygroup(kg, now);
}
@ -464,7 +451,7 @@ void Engine::sendMacroMessage(ObjectId macroId, int32_t val)
vox->message(val);
}
for (std::shared_ptr<Sequencer>& seq : m_activeSequencers)
for (ObjToken<Sequencer>& seq : m_activeSequencers)
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,
std::weak_ptr<Studio> studio)
ObjToken<Studio> studio)
: Entity(engine, group, groupId), m_songGroup(songGroup), m_studio(studio)
{
auto it = m_songGroup->m_midiSetups.find(setupId);
@ -67,7 +67,7 @@ Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const
}
Sequencer::Sequencer(Engine& engine, const AudioGroup& group, int groupId, const SFXGroupIndex* sfxGroup,
std::weak_ptr<Studio> studio)
ObjToken<Studio> studio)
: Entity(engine, group, groupId), m_sfxGroup(sfxGroup), m_studio(studio)
{
std::map<uint16_t, const SFXGroupIndex::SFXEntry*> sortSFX;
@ -210,13 +210,16 @@ size_t Sequencer::getVoiceCount() const
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)
return {};
if (m_lastVoice->isDestroyed())
m_lastVoice.reset();
/* 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();
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);
if (keySearch != m_chanVoxs.cend())
{
if (keySearch->second == m_lastVoice.lock())
if (keySearch->second == m_lastVoice)
m_lastVoice.reset();
keySearch->second->keyOff();
keySearch->second->setPedal(false);
@ -239,7 +242,7 @@ std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo
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);
if (*ret)
{
@ -284,7 +287,7 @@ std::shared_ptr<Voice> Sequencer::ChannelState::keyOn(uint8_t note, uint8_t velo
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)
return {};
@ -301,7 +304,7 @@ void Sequencer::ChannelState::keyOff(uint8_t note, uint8_t velocity)
if (keySearch == m_chanVoxs.cend())
return;
if (keySearch->second == m_lastVoice.lock())
if (m_lastVoice->isDestroyed() || keySearch->second == m_lastVoice)
m_lastVoice.reset();
keySearch->second->keyOff();
m_keyoffVoxs.emplace(std::move(keySearch->second));
@ -425,9 +428,11 @@ void Sequencer::setTempo(double ticksPerSec) { m_ticksPerSec = ticksPerSec; }
void Sequencer::ChannelState::allOff()
{
if (m_lastVoice->isDestroyed())
m_lastVoice.reset();
for (auto it = m_chanVoxs.begin(); it != m_chanVoxs.end();)
{
if (it->second == m_lastVoice.lock())
if (it->second == m_lastVoice)
m_lastVoice.reset();
it->second->keyOff();
m_keyoffVoxs.emplace(std::move(it->second));
@ -476,12 +481,15 @@ void Sequencer::allOff(uint8_t chan, bool now)
void Sequencer::ChannelState::killKeygroup(uint8_t kg, bool now)
{
if (m_lastVoice->isDestroyed())
m_lastVoice.reset();
for (auto it = m_chanVoxs.begin(); it != m_chanVoxs.end();)
{
Voice* vox = it->second.get();
if (vox->m_keygroup == kg)
{
if (it->second == m_lastVoice.lock())
if (it->second == m_lastVoice)
m_lastVoice.reset();
if (now)
{
@ -518,7 +526,7 @@ void Sequencer::killKeygroup(uint8_t kg, bool now)
chan.killKeygroup(kg, now);
}
std::shared_ptr<Voice> Sequencer::ChannelState::findVoice(int vid)
ObjToken<Voice> Sequencer::ChannelState::findVoice(int vid)
{
for (const auto& vox : m_chanVoxs)
if (vox.second->vid() == vid)
@ -529,13 +537,13 @@ std::shared_ptr<Voice> Sequencer::ChannelState::findVoice(int vid)
return {};
}
std::shared_ptr<Voice> Sequencer::findVoice(int vid)
ObjToken<Voice> Sequencer::findVoice(int vid)
{
for (auto& chan : m_chanStates)
{
if (chan)
{
std::shared_ptr<Voice> ret = chan.findVoice(vid);
ObjToken<Voice> ret = chan.findVoice(vid);
if (ret)
return ret;
}

View File

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

View File

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